https://kotlinlang.org logo
#komapper
Title
# komapper
d

dave08

12/10/2023, 12:54 PM
Another little problem... say I have a value class for FooId and I have an tagging entity that has a foreign key that can link to Foo or to Baz (that has a BazId) when I try to make the join I do:
Copy code
innerJoin(Meta.baz) { Meta.foo.id eq Meta.tagging.recordId ;Meta.tagging.type eq "Foo" }
but
recordId
is an
Int
... can I somehow still do this w/o giving up on using those value classes? Something like
Meta.tagging.recordId.map { FooId(it) }
?
t

Toshihiro Nakamura

12/11/2023, 12:58 PM
You can write the following class:
Copy code
class MyExtension(private val context: CriteriaContext) {

    infix fun ColumnExpression<*, Int>.eq(operand: ColumnExpression<FooId, *>) {
        val o1 = Operand.Column(this)
        val o2 = Operand.Column(operand)
        context.add {
            visit(o1)
            append(" = ")
            visit(o2)
        }
    }

}
In you query, use the above class:
Copy code
innerJoin(Meta.baz) { 
    extension(::MyExtension) {
        Meta.foo.id eq Meta.tagging.recordId 
        Meta.tagging.type eq "Foo" 
    }
}
d

dave08

12/11/2023, 1:02 PM
Wow! I'm far from understanding how that works, but I'll try, thanks! I'm really not too clear on how this
visit(..)
manages to "transform" the property type to the value class's type how does it know to take it's
value
property to extract the value from it?
t

Toshihiro Nakamura

12/11/2023, 1:33 PM
In that case, no type conversion is taking place. It’s simply extracting the column name. You might be interested in the following code. https://github.com/komapper/komapper/blob/v1.15.0/komapper-core/src/main/kotlin/org/komapper/core/dsl/builder/BuilderSupport.kt#L128
d

dave08

12/12/2023, 9:33 AM
Oh, so the extension is just to allow
eq
to use that value class type (thanks for the reference to that code -- little by little I'm starting to understand Komapper's internals... which will greatly help using such things in the future...). Don't you think it might have been nice to be able to do this not per-operator? It does happen to have such use cases (like in my case, it's an Tagging entity that allows to tag multiple types of entities, each having a different value class for their ids... that would be pretty cumbersome to write an extension for each of those id types and each of the matchers being used on them...)
I don't know if what I suggested
Meta.tagging.recordId.map { FooId(it) }
would make sense, but maybe something similar?
t

Toshihiro Nakamura

12/13/2023, 12:43 PM
What are the definitions of your ValueClasses? If their internal types are Number types, then they are compatible.
Copy code
@JvmInline
value class AaaId(val value: Int)

@JvmInline
value class BbbId(val value: Int)

@KomapperEntity
data class MyEntity(@KomapperId val id: Int, val aaaId: AaaId, val bbbId: BbbId)
Copy code
val m = Meta.myEntity
QueryDsl.from(m).where { m.aaaId eq m.bbbId}
If there is no compatibility, you may cast it.
Copy code
innerJoin(Meta.baz) { 
    Meta.foo.id eq (Meta.tagging.recordId as ColumnExpression<Int, Int>)
    Meta.tagging.type eq "Foo" 
}
👍🏼 1
d

dave08

12/13/2023, 12:59 PM
Basically that should work when there's no boxing taking place I'd suppose... that's a bit of a hacky way of doing it though...
It could be nice to have an official way of "unwrapping" such a
value class
w/o losing the type safety... as long as both inner types are
Int
it should work they way you suggested, but if the user changes one to Long, for example, then it should really complain at compile time, not at run time...
But the compiler won't let me do the first thing you suggested.
t

Toshihiro Nakamura

12/13/2023, 1:32 PM
That feature was introduced in Komapper v1.15.0. What version of Komapper are you using?
d

dave08

12/13/2023, 2:20 PM
What feature? I'm using 1.15.0...
t

Toshihiro Nakamura

12/13/2023, 2:28 PM
OK. Can you show me your value class definitions?