What is the benefit of using primarykey and references method in class jooq - kotlin

I'm start the learn jooq. I have mssql server. I create some class the represent table on my server. But I don't understand what is the benefit when I was using getPrimaryKey and getReferences methods in my table class?
class User : TableImpl<Record>("users") {
companion object {
val USER = User()
}
val id: TableField<Record, Int> = createField("id", SQLDataType.INTEGER)
val name: TableField<Record, String> = createField("name", SQLDataType.NVARCHAR(50))
val countryId: TableField<Record, Short> = createField("country_id", SQLDataType.SMALLINT)
override fun getPrimaryKey(): UniqueKey<Record> = Internal.createUniqueKey(this, id)
override fun getReferences(): MutableList<ForeignKey<Record, *>> =
mutableListOf(Internal.createForeignKey(primaryKey, COUNTRY, COUNTRY.id))
}
class Country : TableImpl<Record>("country") {
companion object {
val COUNTRY = Country()
}
val id: TableField<Record, Short> = createField("id", SQLDataType.SMALLINT)
val name: TableField<Record, String> = createField("name", SQLDataType.NVARCHAR(100))
override fun getPrimaryKey(): UniqueKey<Record> =
Internal.createUniqueKey(this, id)
}

The generated meta data is a mix of stuff that's useful...
to you, the API user
to jOOQ, which can reflect on that meta data for a few internal features
For instance, in the case of getPrimaryKey(), that method helps with all sorts of CRUD related operations as you can see in the manual:
https://www.jooq.org/doc/latest/manual/sql-execution/crud-with-updatablerecords/simple-crud
If you're not using the code generator (which would generate all of these methods for you), then there is no need to add them to your classes. You could shorten them to this:
class User : TableImpl<Record>("users") {
companion object {
val USER = User()
}
val id: Field<Int> = createField("id", SQLDataType.INTEGER)
val name: Field<String> = createField("name", SQLDataType.NVARCHAR(50))
val countryId: Field<Short> = createField("country_id", SQLDataType.SMALLINT)
}
However, using the code generator is strongly recommended for a variety of advanced jOOQ features which you might not get, otherwise.

Related

How to map the result of jooq multiset into Hashmap(Java Map)?

I have the following class and query. I want to use multiset to map the result of the images into Map<String, String>(Key: OrderNumber / Value: FileKey), but I don't know how to do it. Could you help me how to map the multiset result into hashmap?
data class User(
val id: UUID,
val name: String,
val images: Map<String, String>?
)
#Repository
#Transactional(readOnly = true)
class FetchUserRepository(private val ctx: DSLContext) {
private val user = JUser.USER
private val userImage = JUserImage.USER_IMAGE
override fun fetch(): List<User> {
return ctx.select(
user.ID,
user.NAME,
multiset(
select(userImage.ORDER_NUMBER.cast(String::class.java), userImage.FILE_KEY)
.from(userImage)
.where(userImage.USER_ID.eq(user.ID))
).convertFrom { r -> r.map(mapping(???)) } // I'm not sure how to map the result to hashmap
)
.from(user)
.fetchInto(User::class.java)
}
jOOQ 3.16 solution
The type of your multiset() expression is Result<Record2<String, String>>, so you can use the Result.intoMap(Field, Field) method, or even Result.collect(Collector) using the Records.intoMap() collector, which allows for avoiding the repetition of field names:
{ r -> r.collect(Records.intoMap()) }
I've explained this more in detail in a blog post, here.
jOOQ 3.17 solution
In fact, this seems so useful and powerful, let's add some convenience on top of the existing API using some extensions (located in the jOOQ-kotlin extensions module):
// New extension functions, e.g.
fun <R : Record, E> Field<Result<R>>.collecting(collector: Collector<R, *, E>)
= convertFrom { it.collect(collector) }
fun <K, V> Field<Result<Record2<K, V>>>.intoMap(): Field<Map<K, V>>
= collecting(Records.intoMap())
// And then, you can write:
multiset(...).intoMap()
The feature request is here: https://github.com/jOOQ/jOOQ/issues/13538
In addition to Lukas's answer, I would like to provide an alternative option with jsonObject & jsonObjectAgg.
The result of this query would be returned as JSON format, and it can be easily projected to the target class via Jackson or whatever. (It is really powerful feature when it comes to nested collection within the target class)
I believe it is the one of the coolest features of jOOQ as MULTISET :)
data class User(
val id: UUID,
val name: String,
val images: Map<String, String>?
)
#Repository
#Transactional(readOnly = true)
class FetchUserRepository(private val ctx: DSLContext) {
private val user = JUser.USER
private val userImage = JUserImage.USER_IMAGE
override fun fetch(): List<User> {
return ctx.select(
jsonObject(
key("id").value(user.ID),
key("name").value(user.NAME),
key("images").value(
field(
select(
jsonObjectAgg(
userImage.ORDER_NUMBER.cast(String::class.java),
userImage.FILE_KEY
)
)
.from(userImage)
.where(userImage.USER_ID.eq(user.ID))
)
)
)
)
.from(user)
.fetchInto(User::class.java)
}
}

Kotlin on Android: How to use LiveData from a database in a fragment?

I use MVVM and have a list of data elements in a database that is mapped through a DAO and repository to ViewModel functions.
Now, my problem is rather banal; I just want to use the data in fragment variables, but I get a type mismatch.
The MVVM introduces a bit of code, and for completeness of context I'll run through it, but I'll strip it to the essentials:
The data elements are of a data class, "Objects":
#Entity(tableName = "objects")
data class Objects(
#ColumnInfo(name = "object_name")
var objectName: String
) {
#PrimaryKey(autoGenerate = true)
var id: Int? = null
}
In ObjectsDao.kt:
#Dao
interface ObjectsDao {
#Query("SELECT * FROM objects")
fun getObjects(): LiveData<List<Objects>>
}
My database:
#Database(
entities = [Objects::class],
version = 1
)
abstract class ObjectsDatabase: RoomDatabase() {
abstract fun getObjectsDao(): ObjectsDao
companion object {
// create database
}
}
In ObjectsRepository.kt:
class ObjectsRepository (private val db: ObjectsDatabase) {
fun getObjects() = db.getObjectsDao().getObjects()
}
In ObjectsViewModel.kt:
class ObjectsViewModel(private val repository: ObjectsRepository): ViewModel() {
fun getObjects() = repository.getObjects()
}
In ObjectsFragment.kt:
class ObjectsFragment : Fragment(), KodeinAware {
private lateinit var viewModel: ObjectsViewModel
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = ViewModelProvider(this, factory).get(ObjectsViewModel::class.java)
// I use the objects in a recyclerview; rvObjectList
rvObjectList.layoutManager = GridLayoutManager(context, gridColumns)
val adapter = ObjectsAdapter(listOf(), viewModel)
// And I use an observer to keep the recyclerview updated
viewModel.getObjects.observe(viewLifecycleOwner, {
adapter.objects = it
adapter.notifyDataSetChanged()
})
}
}
The adapter:
class ObjectsAdapter(var objects: List<Objects>,
private val viewModel: ObjectsViewModel):
RecyclerView.Adapter<ObjectsAdapter.ObjectsViewHolder>() {
// Just a recyclerview adapter
}
Now, all the above works fine - but my problem is that I don't want to use the observer to populate the recyclerview; in the database I store some objects, but there are more objects that I don't want to store.
So, I try to do this instead (in the ObjectsFragment):
var otherObjects: List<Objects>
// ...
if (condition) {
adapter.objects = viewModel.getObjects()
} else {
adapter.objects = otherObjects
}
adapter.notifyDataSetChanged()
And, finally, my problem; I get type mismatch for the true condition assignment:
Type mismatch: inferred type is LiveData<List> but List was expected
I am unable to get my head around this. Isn't this pretty much what is happening in the observer? I know about backing properties, such as explained here, but I don't know how to do that when my data is not defined in the ViewModel.
We need something to switch data source. We pass switching data source event to viewModel.
mySwitch.setOnCheckedChangeListener { _, isChecked ->
viewModel.switchDataSource(isChecked)
}
In viewModel we handle switching data source
(To use switchMap include implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.4.0")
class ObjectsViewModel(private val repository: ObjectsRepository) : ViewModel() {
// Best practice is to keep your data in viewModel. And it is useful for us in this case too.
private val otherObjects = listOf<Objects>()
private val _loadDataFromDataBase = MutableLiveData<Boolean>()
// In case your repository returns liveData of favorite list
// from dataBase replace MutableLiveData(otherObjects) with repository.getFavorite()
fun getObjects() = _loadDataFromDataBase.switchMap {
if (it) repository.getObjects() else MutableLiveData(otherObjects)
}
fun switchDataSource(fromDataBase: Boolean) {
_loadDataFromDataBase.value = fromDataBase
}
}
In activity/fragment observe getObjects()
viewModel.getObjects.observe(viewLifecycleOwner, {
adapter.objects = it
adapter.notifyDataSetChanged()
})
You can do something like this:
var displayDataFromDatabase = true // Choose whatever default fits your use-case
var databaseList = emptyList<Objects>() // List we get from database
val otherList = // The other list that you want to show
toggleSwitch.setOnCheckedChangeListener { _, isChecked ->
displayDataFromDatabase = isChecked // Or the negation of this
// Update adapter to use databaseList or otherList depending upon "isChecked"
}
viewModel.getObjects.observe(viewLifecycleOwner) { list ->
databaseList = list
if(displayDataFromDatabase)
// Update adapter to use this databaseList
}

Retrieve Inheritance class full name from Kotlin Psi API

I’m trying to develop a codegen IDEA-Plugin. This plugin should analyze KtClass Inheritance and get all inheritance class full name (like com.example.config.TestConfig)
I have tried to find any useful information by viewing PsiViewer. I find that all
inheritance info of KtClass is stored in KtSuperTypeEntry, and I try my best to get full name of inheritance class.
for class Dest:
data class Dest(
val stringValue: String = "123",
override val stringConfig: String = "123",
override val iConfigStr: String = "123",
val b: B = B(),
val c: List<List<Set<Map<String, out Any?>>>> = listOf(),
val config: Config? = Config()
) : Config()
superTypeListEntry.typeAsUserType.referenceExpression.getReferencedName() -return-> "Config"
superTypeListEntry.importReceiverMembers() -return-> null
Seemingly SuperTypeListEntry just contain inheritance class simple name info.
I also try to find inheritance class full name by KtFile, but there is no idea when inheritance class was imported in this KtFile as wildcards:
fun KtSuperTypeListEntry.getType(ktFile: KtFile): String {
val simpleName = superTypeListEntry.text
// try to find by declared KtClass ...
ktFile.children.filterIsInstance<KtClass>().filter { it.name == simpleName }
// try to find by import ...
ktFile.importDirectives.filter { it.importPath.toString().contains(simpleName) }
// try to find by import wildcards ...
ktFile.importDirectives.filter { it.importPath.toString().endWith("*") }.forEach {
val split = it.importPath.split(".")
split.set(split.size - 1, simpleName)
val maybeFullName = split.joinToString(",") { it }
// confused on how to detect "maybeFullName" is correct ...
}
}
Question
How can I retrieve all inheritance class full name from Kotlin Psi API? Thank you!
After thousand of investigations and debugging, I find that it is possible to find a class's inheritance classes by BindingContext. BindingContext can analyze a TypeReference and find the reference of KotlinType. The code might be like this:
ktClass.superTypeListEntries.map { superTypeEntry ->
val typeReference = superTypeEntry.typeReference
val bindingContext = typeReference.analyze()
bindingContext.get(BindingContext.TYPE, typeReference)
}.forEach { kotlinType ->
val classId = kotlinType.constructor.declarationDescriptor.classId
val packageName = classId.packageFqName
val simpleName = classId.relativeClassName
// It can also get the generics of this class by KotlinType.arguments
val generics = kotlinType.arguments
}
Also, you can get super types full name of the class by KtLightClass, the code might be like this:
val ktLightClass = ktClass.toLightClass()
val superTypesFullName = ktLightClass?.supers?.forEach { superType ->
val packageName = superType.qualifiedName
val simpleName = superType.name
// you can get as KtClass by this, which can help you unify design of interface.
val ktClass = superType.kotlinOrigin
}

Room query with id doesn't return the right list RxJava MVVM architecture

I have a problem concerning my query returns, I have a student class that contains a string id from another table
data class StudentEntity(
#PrimaryKey
val idStudent: String,
val classId: String,
val name: String,
val notes: Note?,
)
I also created a room database that I'm populating from my api call
#Database(entities = [Student::class, Note::class], version = 14, exportSchema = false)
abstract class AppDatabase : RoomDatabase() {
abstract fun programDAO(): ProgramDAO
companion object{
#Volatile
private var INSTANCE: AppDatabase? = null
fun getInstance(context: Context) : AppDatabase {
synchronized(this) {
var instance = INSTANCE
if (instance == null) {
instance = Room.databaseBuilder(
context.applicationContext, AppDatabase::class.java, "student-database"
).fallbackToDestructiveMigration().build()
INSTANCE = instance
}
return instance
}
}
}
}
and for that, I have a programDao that helps me to run my queries
#Dao
interface ProgramDAO {
#Transaction
#Query("select * from studentEntity")
fun getStudents(): Single<List<StudentEntity>>
#Transaction
#Query("select * from studentEntity where classId = :classid")
fun getStudentsWithId(classid: String): Single<List<StudentEntity>>
}
In order to execute those queries, I have my Repository:
class ProgramRepository(val api: ApiService, val programDAO: ProgramDAO) {
fun getStudentsFromDbWithId(idClass: String) : Observable<StudentEntity>{
return programDAO.getStudentsWithId(idClass).toObservable()
}
fun getStudentsFromDb() : Observable<StudentEntity>{
return programDAO.getStudents().toObservable()
}
}
The MV allows me to connect the data and the view:
class ProgramListViewModel(private val programRepository: ProgramRepository) {
fun getListFromDBWithId(classID: String): Observable<List<StudentEntity>> {
return programRepository.getStudentsFromDbWithId(deliverySiteId)
}
fun getListFromDB(): Observable<List<StudentEntity>> {
return programRepository.getStudentsFromDb()
}
}
So in order to use the data and get the adapter and the KPIs on my fragment, I don't receive the right list from the database, I did log the results to see what I get, to start, I do log the whole list without id, and I have the list, but when I use an ID like:
subscribe(programListViewModel.getListFromDBWithId("111").subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
Log.e("list of students", it.toString())
})
I got an empty list, so I thought the problem was from the id, I tried to use the id that has been logged from the whole list and didn't work, I also used S to launch my sql query with the same id and I got the results.
Any help please?
Thanks
Is your AircraftEntity supposed to be StudentEntity? If yes (and I think it is, according to the rest of your code examples), then please update your question.
By default, Room uses the class name as the database table name, and I think it's case-sensitive, so your queries should have been "select * from StudentEntity". The better approach would be giving it a name explicitly:
#Entity(tableName = "students")
data class StudentEntity (
// ...
)
Then, your ProgramDAO would look like follow:
#Dao
interface ProgramDAO {
#Query("select * from students")
fun getStudents(): Single<List<StudentEntity>>
#Query("select * from students where classId = :classid")
fun getStudentsWithId(classid: String): Single<List<StudentEntity>>
}
You said: ... a room database that I'm populating from my api call
Where and how do you populate your database? I don't see any #Insert functions in your DAO, unless you just left them out from your code snippet. If your room DB is not populated, of course you will get no data.

How to adjust table name in Kotlin Exposed on runtime

We are using database table names which are prefixed with environment names e.g:
instead of just 'Cities' we have 'ci_Cities', 'dev_Cities' and 'prod_Cities'.
The problem is that Schema definitions are based on Kotlin objects, which is nice in an usage, but doesn't allow me to simply inject table prefix in e.g. constructor.
So the question is how to implement such a functionality in Kotlin-Exposed?
In the end I have found solution, which seems to be quite elegant.
But I think, that some improvements could be done also in Kotlin Exposed, so that in most cases solution is more concise.
City.kt
data class City(val id: Int, val name: String, val timestamp: Instant)
Schema.kt
import org.jetbrains.exposed.sql.ResultRow
import org.jetbrains.exposed.sql.Table
import org.jetbrains.exposed.sql.`java-time`.timestamp
class CitiesSchema(environment: String) {
val cities = CitiesTable(environment)
}
class CitiesTable(environment: String) : Table(environment + "_Cities") {
val id = varchar("id", 99)
val name = varchar("name", 99)
val timestamp = timestamp("timestamp")
}
CitiesRepository.kt
class CitiesRepository(dataSource: DataSource, private val schema: CitiesSchema) {
private val database = Database.connect(dataSource).defaultFetchSize(10000)
override fun save(city: City): City {
transaction(database) {
schema.cities.insert {
it[schema.cities.id] = city.id
it[schema.cities.name] = city.name
it[schema.cities.timestamp] = city.timestamp
}
}
return city
}
Then in e.g. Spring you can instantiate your schema:
#Bean
public CitiesSchema schema(#Value("${spring.application.env}") String environment) {
return new CitiesSchema(environment);
}
It would be nice to have in Kotlin Exposed ability to rename tables/columns on runtime. Then it would be possible to access Kotlin objects without additional ceremonies.
Such a feature could look like in Jooq:
https://www.jooq.org/doc/3.14/manual-single-page/#settings-render-mapping