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

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)
}
}

Related

Convert String referential datatype to real referential datatypes

I have the following dataclasses:
data class JsonNpc(
val name: String,
val neighbours: JsonPreferences
)
data class JsonPreferences(
val loves: List<String>,
val hates: List<String>
)
I have a list of these, and they reference each other through strings like:
[
JsonNpc(
"first",
JsonPreferences(
listOf("second"),
listOf()
)
),
JsonNpc(
"second",
JsonPreferences(
listOf(),
listOf("first")
)
)
]
note that a likes b does not mean b likes a
I also have the Dataclasses
data class Npc(
val name: String,
val neighbours: NeighbourPreferences,
)
data class NeighbourPreferences(
val loves: List<Npc>,
val hates: List<Npc>
)
And I want to convert the String reference types to the normal reference types.
What I have tried:
recursively creating the npcs (and excluding any that are already in the chain, as that would lead to infinite recursion):
Does not work, as the Npc can not be fully created and the List is immutable (I dont want it to be mutable)
I have managed to find a way to do this. It did not work with Npc as a data class, as I needed a real constructor
fun parseNpcs(map: Map<String, JsonNpc>): Map<String, Npc> {
val resultMap: MutableMap<String, Npc> = mutableMapOf()
for (value in map.values) {
if(resultMap.containsKey(value.name))
continue
Npc(value, map, resultMap)
}
return resultMap
}
class Npc(jsonNpc: JsonNpc, infoList: Map<String, JsonNpc>, resultMap: MutableMap<String, Npc>) {
val name: String
val neighbourPreferences: NeighbourPreferences
init {
this.name = jsonNpc.name
resultMap[name] = this
val lovesNpc = jsonNpc.neighbours.loves.map {
resultMap[it] ?: Npc(infoList[it] ?: error("Missing an Npc"), infoList, resultMap)
}
val hatesNpc = jsonNpc.neighbours.hates.map {
resultMap[it] ?: Npc(infoList[it] ?: error("Missing an Npc"), infoList, resultMap)
}
this.neighbourPreferences = NeighbourPreferences(
lovesNpc, hatesNpc
)
}
}
data class NeighbourPreferences(
val loves: List<Npc>,
val hates: List<Npc>
)
checking in the debugger, the people carry the same references for each Neighbour, so the Guide is always one Npc instance.

Custom serializer with polymorphic kotlinx serialization

With kotlinx.serialization polymorphism, I want to get
{"type":"veh_t","owner":"Ivan","bodyType":"cistern","carryingCapacityInTons":5,"detachable":false}
but I get
{"type":"kotlin.collections.LinkedHashMap","owner":"Ivan","bodyType":"cistern","carryingCapacityInTons":5,"detachable":false}
I use the following models
interface Vehicle {
val owner: String
}
#Serializable
#SerialName("veh_p")
data class PassengerCar(
override val owner: String,
val numberOfSeats: Int
) : Vehicle
#Serializable
#SerialName("veh_t")
data class Truck(
override val owner: String,
val body: Body
) : Vehicle {
#Serializable
data class Body(
val bodyType: String,
val carryingCapacityInTons: Int,
val detachable: Boolean
//a lot of other fields
)
}
I apply the following Json
inline val VehicleJson: Json get() = Json(context = SerializersModule {
polymorphic(Vehicle::class) {
PassengerCar::class with PassengerCar.serializer()
Truck::class with TruckKSerializer
}
})
I use serializer TruckKSerializer because the server adopts a flat structure. At the same time, in the application I want to use an object Truck.Body. For flatten I override fun serialize(encoder: Encoder, obj : T) and fun deserialize(decoder: Decoder): T in Serializator using JsonOutput and JsonInput according to the documentation in these classes.
object TruckKSerializer : KSerializer<Truck> {
override val descriptor: SerialDescriptor = SerialClassDescImpl("Truck")
override fun serialize(encoder: Encoder, obj: Truck) {
val output = encoder as? JsonOutput ?: throw SerializationException("This class can be saved only by Json")
output.encodeJson(json {
obj::owner.name to obj.owner
encoder.json.toJson(Truck.Body.serializer(), obj.body)
.jsonObject.content
.forEach { (name, value) ->
name to value
}
})
}
#ImplicitReflectionSerializer
override fun deserialize(decoder: Decoder): Truck {
val input = decoder as? JsonInput
?: throw SerializationException("This class can be loaded only by Json")
val tree = input.decodeJson() as? JsonObject
?: throw SerializationException("Expected JsonObject")
return Truck(
tree.getPrimitive("owner").content,
VehicleJson.fromJson<Truck.Body>(tree)
)
}
}
And finally, I use stringify(serializer: SerializationStrategy<T>, obj: T)
VehicleJson.stringify(
PolymorphicSerializer(Vehicle::class),
Truck(
owner = "Ivan",
body = Truck.Body(
bodyType = "cistern",
carryingCapacityInTons = 5,
detachable = false
)
)
)
I end up with {"type":"kotlin.collections.LinkedHashMap", ...}, but I need {"type":"veh_t", ...}
How do I get the right type? I want using polymorphism for Vehicle and encode Body object with Truck.Body.serializer() to flatten.
With this serialization, the PassengerCar class runs fine.
VehicleJson.stringify(
PolymorphicSerializer(Vehicle::class),
PassengerCar(
owner = "Oleg",
numberOfSeats = 4
)
)
Result is correct:
{"type":"veh_p","owner":"Oleg","numberOfSeats":4}
I think the problem is the custom serializer TruckKSerializer.
And I noticed if I use in my overridden fun serialize(encoder: Encoder, obj : T) next code
encoder
.beginStructure(descriptor)
.apply {
//...
}
.endStructure(descriptor)
I get the correct type but cannot flatten the object Truck.Body using its serializer.
the correct way to open and close a composite {}
is this code
val composite = encoder.beginStructure(descriptor)
// use composite instead of encoder here
composite.endStructure(descriptor)
and you should be able to serialize Body using .encodeSerializable(Body.serializer(), body)
and always pass the descriptor along otherwise it will fall back to stuff like that LinkedhashMap for the json dictionary

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

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.

Is there a simple way to get a object by _id in Kotlin?

I'm a beginner of Kotlin, I hope to get a object by _id in the following data structure, so I write the fun getMDetailByID(aMDetailsList:MDetailsList, _id:Long)... which is a traditional method.
But I think the fun is too complex, is there a simple way do that? such as use Lambda expression.
class UIMain : AppCompatActivity() {
data class BluetoothDef(val Status:Boolean=false)
data class WiFiDef(val Name:String, val Status:Boolean=false)
data class MDetail (
val _id: Long,
val bluetooth: BluetoothDef,
val wiFi:WiFiDef
)
data class MDetailsList(val mListMetail: MutableList<MDetail>)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.layout_main)
var mBluetoothDef1=BluetoothDef()
var mWiFiDef1=WiFiDef("MyConnect 1",true)
var aMDetail1= MDetail(5L,mBluetoothDef1,mWiFiDef1)
var mBluetoothDef2=BluetoothDef(true)
var mWiFiDef2=WiFiDef("MyConnect 2")
var aMDetail2= MDetail(6L,mBluetoothDef2,mWiFiDef2)
val mListMetail:MutableList<MDetail> = mutableListOf(aMDetail1,aMDetail2)
var aMDetailsList=MDetailsList(mListMetail)
var c=getMDetailByID(aMDetailsList,5L)
}
fun getMDetailByID(aMDetailsList:MDetailsList, _id:Long):MDetail?{
var aMDetail: MDetail?=null
var a=aMDetailsList.mListMetail
for (b in a){
if (b._id==_id){
aMDetail=b
}
}
return aMDetail
}
}
A faster and simpler alternative to current answers (it's also better than your original code, since it doesn't always need to traverse the entire list):
fun getMDetailByID(aMDetailsList: MDetailsList, _id: Long) =
aMDetailsList.mListMetail.findLast { it._id == _id }
Also, consider whether you actually benefit from defining MDetailsList as a class instead of directly using (Mutable)List<MDetail> or making it a typealias. There are cases in which you do need such a type, but they aren't that common.
There is a simpler way using lambdas, indeed. You could replace your function code by:
fun getMDetailByID(aMDetailsList: MDetailsList, _id: Long): MDetail? {
return aMDetailsList.mListMetail.filter { it._id == _id }.lastOrNull()
}
It will, like your implementation did, return the last matching element or null if there is none.
Simpler code can be like:
fun getMDetailByID(aMDetailsList: MDetailsList, _id: Long) = aMDetailsList.mListMetail.filter { it._id == _id }.lastOrNull()

Kotlin generate constructor that sets default values to nulled arguments

Let's take the class of a data class:
data class User(
val userNumber: Int = -1,
val name: String,
val userGroups; List<String> = emptyList(),
val screenName: String = "new-user"
)
When calling this function from Kotlin, it is pretty straightforward. I can simply use the named-argument syntax to do so. Calling from Java, I have to specify all values, or use the #JvmOverloads annotation, which generates the following constructors (in addition to the constructor that kotlin generates with the bit-mask for default values):
User(int userNumber, #NotNull String name, #NotNull List userGroups,
#NotNull String screenName)
User(int userNumber, #NotNull String name, #NotNull List userGroups)
User(int userNumber, #NotNull String name)
User(#NotNull String name)
Now, if I want to create a User object in Java equivalent to User(name="John Doe", userGroups=listOf("admin", "super") I can't do it with the above constructors. I CAN however do it if I put val userNumber: Int = -1 at the end in the data class declaration (the generation of constructors seems to depend on the order the optional arguments are defined in). Which is fine, because expecting kotlin to generate all permutations is going to heavily bloat some classes.
The biggest problem that tools like Jackson simply don't work as they have no idea which constructor to use (and not like I can annotate one of the generated ones specially).
So, is there a way to generate a (single) constructor like:
User(Integer userNumber, String name, List<String> userGroups, String screenName) {
this.userNumber = (userNumber == null) ? -1 : userNumber;
this.userGroups = (userGroups == null) ? Collections.emptyList() : userGroups;
//...
}
Currently I am using the above approach, but manually defining the constructors where I need them.
EDIT
I should clarify, creating a similar constructor doesn't work, obviously because both the signatures would clash on the JVM. This is what it would like in my case:
data class User(
val userNumber: Int = -1,
val name: String,
val userGroups; List<String> = emptyList(),
val screenName: String = "new-user"
) {
companion object {
#JvmStatic
#JsonCreator
fun constructionSupport(
#JsonProperty("userNumber") userNumber : Int?,
#JsonProperty("name") name : String,
#JsonProperty("userGroups") userGroups : List<String>?,
#JsonProperty("screenName") screenName : String?
) = User(
userNumber = userNumber ?: -1,
name = name,
userGroups = userGroups ?: emptyList(),
screenName = screenName ?: "new-user"
)
}
}
Also note the redundancy where I have to write the default values for the properties twice. I Now that I look at it, I doubt there exists a solution for this. Maybe this is a good use-case for a kapt based side-project of mine :)
Better solution is to add possibility to library understand Kotlin functional. For example, for Jackson exists jackson-module-kotlin. With this library we can use default arguments in data classes.
Example:
data class User(
val userNumber: Int = -1,
val name: String,
val userGroups: List<String> = emptyList(),
val screenName: String = "new-user"
)
fun main(args: Array<String>) {
val objectMapper = ObjectMapper()
.registerModule(KotlinModule())
val testUser = User(userNumber = 5, name = "someName")
val stringUser = objectMapper.writeValueAsString(testUser)
println(stringUser)
val parsedUser = objectMapper.readValue<User>(stringUser)
println(parsedUser)
assert(testUser == parsedUser) {
println("something goes wrong")
}
}
After kicking this around for a minute, I think I found a solution that may work well here. Simply define a top level function in the same source file, that will build the object. Perhaps like so:
fun build_user(userNumber: Int?, name: String, userGroups: List<String>?, screenName: String?) : User {
return User(if(userNumber !== null) userNumber else -1, name, if(userGroups !== null) userGroups else emptyList(),
if(screenName !== null) screenName else "new-user")
}
Then when you need it, you simply call it from Java:
User user = UserKt.build_user(null, "Hello", null, "Porterhouse Steak");
System.out.println(user);
Output from the example:
User(userNumber=-1, name=Hello, userGroups=[], screenName=Porterhouse Steak)
The method is somewhere between a constructor and a builder. It beats hammering out a full-blown Builder object, and avoids cluttering your data class with unnecessary Java-interop glue code messiness.
See Package Level Functions for more information.