Repository navigation
Suggestion: Support for in-line row mapper #4
Description
Activity
- changed the title
[-]Support for in-line row mapper[/-][+]Suggestion: Support for in-line row mapper[/+]on Nov 6, 2024 Yup. Good feature idea.
@arvearve If you're willing to use kotlin serialization you can build a custom serializer like this:
data class CustomPerson(val name: String, val age: Int) object CustomPersonSerializer : KSerializer<CustomPerson> { override val descriptor = buildClassSerialDescriptor("CustomPerson") { element("name", serialDescriptor<String>()) element("age", serialDescriptor<Int>()) } override fun serialize(encoder: Encoder, value: CustomPerson) = TODO("Not used by Terpal-SQL so don't care") override fun deserialize(decoder: Decoder): CustomPerson = decoder.decodeStructure(descriptor) { CustomPerson( decodeStringElement(descriptor, 0), decodeIntElement(descriptor, 1) ) } } // Then invoke it like this: val people = Sql("SELECT name, age FROM people").queryOf<CustomPerson>(CustomPersonSerializer).runOn(driver) println(people)
This is obviously not the nicest API but its roughly what JetBrains is expecting everyone to use when they want to build custom composite serializer.
I have purposely not built a cross-database row-abstraction because there are ultimately too many nasty gotchas in the process (e.g. some row-implementations have metadata, others don't. Some row-implementations have async callbacks, etc...).
What I am interested in is exploring some kind of way to make writing this
deserializefunction in a more palatable way which is what I'm exploring now. Which API would you prefer, this one?Sql("...").queryOf<Person>( shape = { fieldString("name") fieldInt("age") }, run = { CustomPerson(readString(), readInt()) } )
Or this one?
Sql("...").queryOf<Person>( { fields -> fields.string("name") fields.int("age") }, { read -> CustomPerson(read.string(), read.int()) } )
Hi. Personally I think the 2nd variant looks better 👍. However, it still feels quite like code repetition for the caller.
I can sort of see how for example async callbacks might make this unavoidable though.
In many cases it would be nice to have an overload for
queryOf<T>()/actionReturing<T>()that takes an extractor function / row mapper, e.g.Some times it’s nice to be allowed to access “under the hood”
If i understand correctly this would let you bypass the kotlinx.serialization loop and would be useful in complex cases where you would otherwise have to tailor your entity data classes a lot in order to make things work. E.g. custom Serializers, @contextual and @SerialName annotations,
Example envisioned usage:
This suggestion is inspired by Kotliquery's implementation.
References:
Rowhelper class