Spring Data JPA: How to use objects with the #Query annotation? - kotlin

Consider the following JpaRepository.
interface ExpectedLossRepository : JpaRepository<ExpectedLoss, Long> {
fun getByDealId(dealId: Long): ExpectedLoss
#Query("select el from ExpectedLoss el where el.dealId = ?1.dealId and ...")
fun getByExtendedTerm(#Param("extended_term") term: ExtendedTerm): ExpectedLoss
}
The first function lets me query for ExpectedLoss entities by dealId, so far so good. But now I need to build a complex query and therefor don't want pass all required primitives, but instead an instance of an object.
This query doesn't work. How do I reference the passed object?
#Query("select el from ExpectedLoss el where el.dealId = (?1).dealId and ...")

I figured it out in the meantime. The correct syntax is:
interface ExpectedLossRepository : JpaRepository<ExpectedLoss, Long> {
#Query("select el from ExpectedLoss el where el.dealId = :#{#term.dealId}")
fun getByExtendedTerm(#Param("extended_term") term: ExtendedTerm): ExpectedLoss
fun getByDealId(dealId: Long): ExpectedLoss
}

Related

Kotlin DSL builders generalization

While writing DSL in Kotlin for an API with lots of classes I'd encountered an issue. Code seemed completely boilerplate to me. Is there a way to generalize code in DSL builders?
Suppose we have an API:
class Robot(val name: String, val head: Head, val body: Body, val hands: Hands, ...)
class Head(val mouth: Mouth, ... )
class Body(val material: Material, ... )
class Hands(...)
class Mouth(...)
class Material(...)
The classes dependency tree could be much larger than that.
DSL for this api would look something like:
fun robot(name: String, settings: RobotBuilder.() -> Unit) = RobotBuilder(name).apply(settings).build()
interface Builder<T> {
fun build(): T
}
#RobotDsl
class RobotBuilder(val name: String) : Builder<Robot> {
private var head: Head = DefaultBotParts.head
private var body: Body = DefaultBotParts.body
private var hands: Hands = DefaultBotParts.hands
...
fun head(initHead: HeadBuilder.() -> Unit) {
head = HeadBuilder().apply(initHead).build()
}
fun body(initBody: BodyBuilder.() -> Unit) {
body = BodyBuilder().apply(initBody).build()
}
fun hands(initHands: HandsBuilder.() -> Unit) {
body = HandsBuilder().apply(initHands).build()
}
...
override fun build(): Robot = Robot(name, head, hands)
}
#RobotDsl
class HeadBuilder : Builder<Head> {
private var mouth: Mouth = DefaultBotParts.mouth
fun mouth(init: MouthBuilder.() -> Unit) {
mouth = Mouth().apply(init).build()
}
override fun build(): Robot = Head(mouth)
}
#RobotDsl
class MouthBuilder : Builder<Mouth> {
//its properties and dsl functions
...
override fun build(): Mouth = Mouth(...)
}
I think the problem is clear now. Each builder can have one or more dependent ones, and they only needed for declaring dsl functions and scope separation, the code is boilerplate.
So, can there be a simple solution that generalizes the code?

Kotlin: generate a Factory by class

We're trying to do some generic processing in kotlin. Basically, for a given class, we want to get the related Builder object. i.a. for any object that extends a GenericObject, we want a Builder of that Object.
interface Builder<T : GenericObject>
object ConcreteBuilder: Builder<ConcreteObject>
We'd need a function that will return ConcreteBuilder from ConcreteObject
Our current implementation is a Map:
val map = mapOf<KClass<out GenericObject>, Builder<out GenericObject>>(
ConcreteObject::class to ConcreteBuilder
)
Then we can get it with:
inline fun <reified T : GenericObject> transform(...): T {
val builder = map[T::class] as Builder<T>
...
However this isn't very nice as:
we need an explicit cast to Builder<T>
the map has no notion of T, a key and a value could be related to different types.
Is there any better way to achieve it?
A wrapper for the map could be:
class BuilderMap {
private val map = mutableMapOf<KClass<out GenericObject>, Builder<out GenericObject>>()
fun <T: GenericObject> put(key: KClass<T>, value: Builder<T>) {
map[key] = value
}
operator fun <T: GenericObject> get(key: KClass<T>): Builder<T> {
return map[key] as Builder<T>
}
}
This hides the ugliness, while not completely removing it.
To use:
val builderMap = BuilderMap()
builderMap.put(ConcreteObject::class, ConcreteBuilder)
builderMap.put(BetonObject::class, BetonBuilder)
// builderMap.put(BetonObject::class, ConcreteBuilder) – will not compile
val builder = builderMap[T::class]

Kotlin generics with in produces Type mismatch when compiling

I´m working on a code with generics and when I use an in I got a TypeMismatch when compiling.
The code is the following:
open class A
class B:A()
data class DataContainer(val a:String,
val b:A)
interface Repo<T:A>{
fun setParam(param:T)
fun getParam():T
}
abstract class RepoImp<T:A>:Repo<T>{
private lateinit var parameter:T
override fun setParam(param: T) {
parameter = param
}
override fun getParam(): T {
return parameter
}
}
class BRepo:RepoImp<B>()
class Repo2(val repo: Repo<in A>){
fun process(b:DataContainer){
repo.setParam(b.b)
}
}
val repoB = BRepo()
val repo2 = Repo2(repoB)// Here I got: Type mismatch: inferred type is BRepo but Repo<in A> was expected
I also tried changing the attribute repo from Repo2 to Repo<*>
Since BRepo is a Repo<B>, it is not a Repo<in A>, (but it would satisfy Repo<out A>).
In other words, a Repo<in A> must be able to accept setParam(A()), but BRepo.setParam() can only accept a B or subclass of B.
Or to put it another way, BRepo is a Repo<B>, which is a tighter restriction on the type than Repo<A> when it comes to writing values (but looser restriction when reading values).
The reason class Repo2(val repo: Repo<*>) doesn't work is that Repo<*> is essentially a Repo<in Nothing/out A>. You can't call setParam() on a Repo<*> with any kind of object.
There's a design flaw in your code that you can't fix simply by changing Repo2's constructor signature. As it stands now, Repo2 needs to be able write A's to the object you pass to it, and a BRepo by definition does not support writing A's, only B's. You will need to make at least one of your class's definitions more flexible about types.
It might be easier to understand the covariance limitation with more common classes:
val stringList: MutableList<String> = ArrayList()
var anyList: MutableList<in Any> = ArrayList()
anyList.add(5) // ok
anyList = stringList // Compiler error.
// You wouldn't be able to call add(5) on an ArrayList<String>
Basically MutableList<String> is not a MutableList<in Any> the same way Repo<B> is not a Repo<in A>.
The Repo2 class expect to consume only type A, use Repo2<T : A>(val repo: Repo<in T>)
open class A
class B : A()
class C : A()
class D : A()
class BRepo : RepoImp<B>()
class CRepo : RepoImp<C>()
class DRepo : RepoImp<D>()
interface Repo<T : A> {
fun setParam(param: T)
fun getParam(): T
}
abstract class RepoImp<T : A> : Repo<T> {
private lateinit var parameter: T
override fun setParam(param: T) {
parameter = param
}
override fun getParam(): T {
return parameter
}
}
class Repo2<T : A>(val repo: Repo<in T>) {
fun process(b: DataContainer<T>) {
repo.setParam(b.b)
}
}
data class DataContainer<T : A>(
val a: String,
val b: T
)
fun main() {
val repoB = BRepo()
val repoC = CRepo()
val repoD = DRepo()
val repo2 = Repo2(repoB)
val repo3 = Repo2(repoC)
val repo4 = Repo2(repoD)
repo2.process(DataContainer("Process B type", B()))
repo3.process(DataContainer("Process C type", C()))
repo4.process(DataContainer("Process D type", D()))
println(repo2.repo.getParam())
println(repo3.repo.getParam())
println(repo4.repo.getParam())
}

Kotlin. Trying to use reified types to parse Lists and Arrays

I am trying to use reified type when parsing json.
It works perfectly with single json entry, but fails with list.
QUESTIONS:
What am I missing in String.parseList() method?
How come ClassCastException upon .first() despite assignment passed one line earlier?
package qa
import com.fasterxml.jackson.databind.ObjectMapper
import org.slf4j.LoggerFactory
import org.testng.Assert
import org.testng.annotations.Test
class ReifiedParseListTest {
data class User(var name: String = "userName", var age: Int = 0)
val log = LoggerFactory.getLogger(this.javaClass.name)
val objectMapper = ObjectMapper()
val json: String = """[{"name":"Alice","age":1},{"name":"Bob","age":2}]"""
val expected: String = "[User(name=Alice, age=1), User(name=Bob, age=2)]"
inline fun <reified V> String.parseList(): List<V> = objectMapper
.readValue(this, Array<V>::class.java).toList()
#Test
fun checkParseList_OK() {
val actual: List<User> = objectMapper
.readValue(json, Array<User>::class.java).toList()
log.info("actual.first() is of type: {}", actual.first().javaClass)
Assert.assertEquals(actual.toString(), expected)
}
#Test
fun checkParseListReified_FAILS() {
val actual: List<User> = json.parseList<User>()
Assert.assertEquals(actual.toString(), expected)
// java.lang.AssertionError:
// Expected :[User(name=Alice, age=1), User(name=Bob, age=2)]
// Actual :[{name=Alice, age=1}, {name=Bob, age=2}]
}
#Test
fun checkParseListReifiedClassCast_FAILS() {
val actual: List<User> = json.parseList<User>()
log.info("actual.first() is of type: {}", actual.first().javaClass)
// java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to qa.ReifiedParseListTest$User
}
}
In this case, reified helps to propagate the type's class, but there's still type erasure.
To avoid that, you can use something like JavaType:
inline fun <reified V> String.parseList(): List<V> {
return objectMapper.readValue(this, objectMapper.getTypeFactory()
.constructCollectionType(List::class.java, V::class.java))
}
Note that without reified we wouldn't be able to use V::class.java
Now to answer your second question, how come that although val actual is List<User>, you get ClassCastException - the answer is again type erasure, with some obfuscation of platform types.
If you look at what this function returns (it's your function without asList() call:
inline fun <reified V> String.parseList() =
objectMapper.readValue(this, Array<V>::class.java)
You'll notice it returns Array<???>!, which is Kotlin's way of saying "it's something from Java, I hope it will work, but I can't promise". Now by calling toList() this relaxes the compiler, saying "yeah, in the end we return a Kotlin type, it will be alright". But that's a false promise, actually.
What you get is Array<Any> filled with LinkedHashMap, which of course fail when they're being cast to User based on a false promise we've given the compiler.
i finally end up with yet another solution, that seems to handle both single entities and lists
inline fun <reified V> String.parse(): V = objectMapper.readValue(this, object : TypeReference<V>() {})
#Test
fun checkParseSingle() {
val jsonSingle: String = """{"name":"Carol","age":3}"""
val expectedSingle: String = "User(name=Carol, age=3)"
val actual: User = jsonSingle.parse<User>()
Assert.assertEquals(actual.toString(), expectedSingle)
}
#Test
fun checkParseList() {
val jsonList: String = """[{"name":"Alice","age":1},{"name":"Bob","age":2}]"""
val expectedList: String = "[User(name=Alice, age=1), User(name=Bob, age=2)]"
val actual: List<User> = jsonList.parse<List<User>>()
Assert.assertEquals(actual.toString(), expectedList)
}
It fails because of Array<V>::class.java always returning class of Array<Any>. You can see it by executing the following code:
printReifiedArr<String>() // prints `class [Ljava.lang.Object;`
inline fun <reified V> printReifiedArr() {
println(Array<V>::class.java)
}
Your function can be fixed by replacing Array<V>::class.java with a manual array class obtaining:
inline fun <reified V> String.parseList(): List<V> = objectMapper
.readValue(this, Class.forName("[L${V::class.java.name};") as Class<Array<V>>).toList()
Note: this approach uses boxed version of primitives array, other approaches can be found here.
You need to capture generic type which T:class.java won't give. But following will work for any generic type
inline fun <reified T> jacksonTypeRef(): TypeReference<T> = object: TypeReference<T>() {}
inline fun <reified T : Any> String.parseJson(): T {
return objectMapper.readValue(this, jacksonTypeRef<T>())
}

Implementing multiple overloaded interfaces with `by`

I'd like to leverage by to build class APIs in a nice way. Is there any way to do something like the following?
interface Foo<T> {
fun foo(t: T)
}
inline fun <reified T> createFoo() = object : Foo<T> {
override fun foo(t: T) {
// do stuff
}
}
// This is an error
class StringIntFoo : Foo<String> by createFoo(), Foo<Int> by createFoo()
fun main(){
val foo = StringIntFoo()
foo.foo("")
foo.foo(2)
}
// Doing it manually obviously isn't an issue
class ManualStringIntFoo {
fun foo(t: String){
}
fun foo(t: Int){
}
}
Link to a playground.
It looks like the generated method end up having the same JVM signature. I was hoping the reified types would get around it. With only a single implementation it works just fine and the types look correct.
Is there some way of actually doing this? Whether or not the StringIntFoo is technically a Foo I suppose isn't important for the problem at hand. It would be cool to be able to construct classes in this way.
Doing it manually doesn't work either if you try to actually implement the interfaces: ManualStringIntFoo : Foo<String>, Foo<Int> gives the same error as StringIntFoo.
So by can't help because it still compiles to class StringIntFoo : Foo<String>, Foo<Int> only setting the implementation of methods.