How to debug myBatis NoSuchMethodException in kotlin? - kotlin

I have a simple class like this:
data class Foo(
val id: Long,
val created: LocalDateTime,
val myBool: Boolean? = null,
val comment: SensitiveString? = null
)
I get the exception:
org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.reflection.ReflectionException: Error instantiating class com.XXX.XXX.XXX.XXX.Foo with invalid types (long,LocalDateTime,boolean,SensitiveString) or values (XX,2020-03-19T17:36:30.415,false,0#SensitiveString).
Cause: java.lang.NoSuchMethodException: com.XXX.XXX.XXX.XXX.Foo.<init>(long, java.time.LocalDateTime, boolean, com.XXX.XXX.XXX.XXX.SensitiveString)
Here is my xml resultMap:
<resultMap id="fooMap" type="com.XXX.XXX.XXX.XXX.Foo">
<constructor>
<arg column="id" javaType="_long"/>
<arg column="created" javaType="java.time.LocalDateTime"
typeHandler="com.XXX.XXX.XXX.XXX.LocalDateTimeTypeHandler"/>
<arg column="myBool" javaType="_Boolean"/>
<arg column="comment" javaType="com.XXX.XXX.XXX.XXX.SensitiveString"
typeHandler="com.XXX.XXX.XXX.XXX.EncryptedStringTypeHandler"/>
</constructor>
</resultMap>
How can I solve this? the asked constructor should definitely be available, so it is very confusing.
Moreover: when I try to create my own constructor with all the fields then kotlin complains saying the constructor is conflicting with the implicit one.
How can I tackle this?

I had this exact problem. The issue for me was the Boolean? type - the Java type is java.lang.Boolean when the type is nullable. I did it with annotations, so it would look like this:
#ConstructorArgs (
...
Arg(column = "myBool", javaType = java.lang.Boolean::class)
...
)

Related

What is the correct syntax for specifying generic boundaries while inheriting from superclass in Kotlin?

I've encountered a problem.
The classes I have in reality are more complex, but here's the general outline. There's a class A:
open class A(
val id: Any
)
And class B that bounds that Any to a more specific class:
class B<T>(
val clazz: Class<T>,
nonnull: T
) : A(nonnull)
But it won't compile as T is by default bounds to Any?, and class A has a non-nullable Any. And usually I'd write where T : Any, or even where T : OneInterface, SecondInterface. But it does not compile. I am sure I'm just writing something wrong way, but what?..
I've tried both googling and looking into the documentation, but I am not the master of Google-Fu, and documentation is silent on this matter. Both Inheritance and Generics are easy to understand, but they lack this exact intersection of language syntax. :/
Put the constraint in a different place:
class B<T: Any>(
val clazz: Class<T>,
nonnull: T
) : A(nonnull)
Or
class B<T>(
val clazz: Class<T>,
nonnull: T
) : A(nonnull) where T: Any
I'd prefer the first one and only use where when you can't use it, e.g. for multiple constraints.
Restrict T to not accept a nullable Any:
class B<T: Any>(
...

Does the jOOQ 3.15.1 new ad-hoc conversion API work in Kotlin?

I am attempting to translate the worked-example from jOOQ 3.15's New Multiset Operator Will Change How You Think About SQL into Kotlin, but am having some type-inference issues preventing me from compiling.
Here it is so far:
using the sakila schema
generating Kotlin from the jOOQ code generation step
adapting Java's CompletionStage future to Kotlin co-routines with the async() extension method from kotlinx.coroutines.jdk-8
data class Actor(val firstName: String?, val lastName: String?)
data class Film(val title: String?, val actors: List<Actor>, val
categories: List<String>)
...
val future = dsl.select(
FILM.TITLE,
multiset(
select(
FILM_ACTOR.actor().FIRST_NAME,
FILM_ACTOR.actor().LAST_NAME
)
.from(FILM_ACTOR)
.where(FILM_ACTOR.FILM_ID.eq(FILM.FILM_ID))
).`as`("actors").convertFrom { it.map( mapping(::Actor) ) }, // <--[1]
multiset(
select(FILM_CATEGORY.category().NAME)
.from(FILM_CATEGORY)
.where(FILM_CATEGORY.FILM_ID.eq(FILM.FILM_ID))
).`as`("films").convertFrom { it.map { r -> r.getValue(0) } }
)
.from(FILM)
.orderBy(FILM.TITLE)
.fetchAsync()
val result = future.await().map( mapping(::Film) ) // <--[2]
Problems I'm having are noted at [1] and [2] in the code.
At [1], it comes down to Kotlin vs Java when getting the constructor reference:
Type mismatch.
Required: ((String?, String?) → Actor!)!
Found: KFunction2<String, String, Actor>
... which is close, but no cigar. There is probably a simple way to adapt it that I'm missing (other than manually invoking the constructor).
At [2], Kotlin can't decide which overload of mapping(...) to select.
It is currently opaque to me why the issue is different in each case.
Can I please get some guidance whether the mapping(/*constructor reference*/): RecordManager<R,U> is expected to work with Kotlin in jOOQ 3.15.1? and if not, what I might reasonably do to work around it?
Regarding problem [1], it's sometimes not an option to make the attributes nullable: your whole application will then be contaminated by this nullable attribute. Even though the column might be specified as NOT NULL in the database, jOOQ (3.15.1) currently doesn't provide non-null record fields (since they are not guaranteed to be non-null in e.g. outer joins).
With non-nullable attributes, the simplest solution I've found so far is this construct:
multiset(
select(
FILM_ACTOR.actor().FIRST_NAME,
FILM_ACTOR.actor().LAST_NAME
)
.from(FILM_ACTOR)
.where(FILM_ACTOR.FILM_ID.eq(FILM.FILM_ID))
).`as`("actors").convertFrom(List::class.java) { it.map { s -> Actor(s.value1()!!, s.value2()!!) } },
A: Yes it does!
This is the most straightforward Kotlin equivalent of the final worked example at jOOQ 3.15's New Multiset Operator Will Change How You Think About SQL without adding any complexity with fetchAsync().
The declared properties of the data classes must all be nullable types.
data class Actor(val firstName: String?, val lastName: String?)
data class Film(val title: String?, val actors: List<Actor>?, val categories: List<String>?)
and then
val result = dsl.select(
FILM.TITLE,
multiset(
select(
FILM_ACTOR.actor().FIRST_NAME,
FILM_ACTOR.actor().LAST_NAME
)
.from(FILM_ACTOR)
.where(FILM_ACTOR.FILM_ID.eq(FILM.FILM_ID))
).`as`("actors").convertFrom { it?.map( mapping(::Actor) ) },
multiset(
select(FILM_CATEGORY.category().NAME)
.from(FILM_CATEGORY)
.where(FILM_CATEGORY.FILM_ID.eq(FILM.FILM_ID))
).`as`("films").convertFrom { it?.map { r -> r.getValue1() } }
)
.from(FILM)
.orderBy(FILM.TITLE)
.fetch( mapping(::Film) )
Gotchas
The gotchas I had included:
not making all properties of my data/record classes use nullable types
accidentally used Record1#getValue() instead of Record1#getValue1() to map the name of the film, and therefore had an Any! instead of a String!
my data class Film(...) not being used because IDEA had imported the same-named class that jOOQ generates instead of using mine
In all of the above cases, the problems manifest as 'wrong type inference` issues with bewildering presentations.
I also had:
an NPE in the convertFrom{...} blocks where depending on the data in the table, it was possible that it was null! (In an unmodified Sakila database, I had problems with author)
trouble invoking Record#map(RecordMapper) ... sometimes with mapping(::MyClass) to get a RecordMapper (in which case, map() is the right invocation style) and sometimes with a lambda with signature R -> T (in which case map{}) is the right invocation style. Using the latter style, but supplying a RecordMapper via mapping() gives you the wrong type later on! eg.
convertFrom { it?.map( mapping(::Actor) } // <- gives List<Actor> in the object graph's type after fetch()
vs
convertFrom { it?.map { mapping(::Actor) } // <- gives List<RecordMapper<Record3<...etc...>,Actor>> in the object's graph type after fetch()
Your error [1]
Are you sure you made your attributes in the data class nullable in the test? The error message is:
Type mismatch.
Required: ((String?, String?) → Actor!)!
Found: KFunction2<String, String, Actor>
The latter should be KFunction2<String?, String?, Actor> instead.
Your error [2]
Not sure what the kotlinx-coroutines library does here, but if you use JDK CompletionStage API, this also works for me (with the correct nullability applied):
.fetchAsync().thenApply { mapping(::Film) }
Perhaps that, too, is a side-effect of wrong nullability being applied

How to map string to function and call it later

I'm new to Kotlin and what I'm trying to achieve is very simple in dynamically typed languages like Python and JavaScript, but no so easy in Kotlin. I have a set of message handler functions accepting message objects. Each message class implements Message interface. I want to map each message handler function to a String key:
interface Message
data class TestMessage1(val body: String): Message
data class TestMessage2(val body: String): Message
fun testMessage1Handler(msg: TestMessage1) { println(msg.body) }
fun testMessage2Handler(msg: TestMessage2) { println(msg.body) }
val functions = mapOf<String, (Message)->Unit> (
"testMessage1" to ::testMessage1Handler,
"testMessage2" to ::testMessage2Handler
)
This code gives me two errros:
error: type inference failed.
Expected type mismatch: inferred type is
Pair<String, KFunction1<#ParameterName Line_1.TestMessage1, Unit>>
but
Pair<String, (Line_1.Message) -> Unit> was expected
error: type inference failed.
Expected type mismatch: inferred type is
Pair<String, KFunction1<#ParameterName Line_1.TestMessage2, Unit>>
but
Pair<String, (Line_1.Message) -> Unit> was expected
Why can't I use interface Message as function type parameter? Since both TestMessage1 and TestMessage2 implement this interface it seems correct to me. How would you implement something like this?
There is a related question How to save a function reference as the value in a Map type, and invoke it with a parameter later on in Kotlin? but I don't want to change message handler parameter msg type to Any
Let's look at in another way.
What if instead of trying and failing to specify the type, we give Kotlin chance to infer it, by specifying incorrect type:
val functions: String = mapOf(
"testMessage1" to ::testMessage1Handler,
"testMessage2" to ::testMessage2Handler
)
This produces:
inferred type is Map<String, KFunction1<*, Unit>> but String was expected.
Now if you put that signature, it will actually compile:
val functions = mapOf<String, KFunction1<*, Unit>>(
"testMessage1" to ::testMessage1Handler,
"testMessage2" to ::testMessage2Handler
)
This is similar to:
val functions: Map<String, KFunction1<*, Unit>> = mapOf(
"testMessage1" to ::testMessage1Handler,
"testMessage2" to ::testMessage2Handler
)
When trying to invoke this, though, you get a hint why this is not correct:
functions["testMessage1"]?.invoke(TestMessage1("a"))
Gets you
Type mismatch.
Required: Nothing
Found: TestMessage1
Notice that Kotlin infers that type of your input is Nothing
To understand why, let's look at only one function:
fun testMessage1Handler(msg: TestMessage1)
And ask ourselves: what other type besides TestMessage1 this function may receive? No other type. It cannot receive TestMessage2. It cannot receive Message, since Message is not necessarily TestMessage1.
For second function, we'll get the same answer. So, the common type of both is Nothing. Which makes this abstraction not very useful to begin with.
The following code compiles but renders the question kind of moot.
The type parameters match now. Maybe generics could be applied as well, but I don’t know how.
interface Message {
val body: String
}
data class TestMessage1(override val body: String): Message
data class TestMessage2(override val body: String): Message
fun messageHandler(msg: Message) { println(msg.body) }
val functions = mapOf<String, (Message)->Unit> (
"testMessage1" to ::messageHandler,
"testMessage2" to ::messageHandler
)

How to pass null argument in Android Databinding

Why can't pass null value? how can fix it? I can't find any hint from Document.
ERROR
****/ data binding error ****msg:cannot find method onClick(java.lang.Object, java.lang.Object) in class kr.co.app.MyActivity.MyListener file:/Users/jujaeho/projects/app/src/main/res/layout/activity_my.xml loc:24:71 - 24:106 ****\ data binding error ****
CODE
class MyActivity {
interface MyListener {
fun onClick(abc: ABC?, count: Int?)
}
}
<layout>
<data>
<variable
name="handler"
type="kr.co.app.MyActivity.MyListener" />
</data>
<View
...
android:onClick="#{() -> handler.onClick(null, null)}" />
</layout>
I just encountered this problem today, and what I did basically was to cast the null parameters to the types expected in the method parameters. In your case, this should be something like:
<layout>
<data>
<import type="ABC" /> // just an illustration, specify the full package
<variable
name="handler"
type="kr.co.app.MyActivity.MyListener" />
</data>
<View
...
android:onClick="#{() -> handler.onClick((ABC) null, (int) null)}" />
</layout>
I am not sure about the int casting, but you can try it or use the Integer wrapper class for casting.
if you are intending to pass null why cannot you assign default value for your onClick method
class MyActivity {
interface MyListener {
fun onClick(abc: ABC?=null, count: Int?=null)
}
}
you can pass nothing if you want to pass null

Kotlin compiler: Data binding error, cannot find method

Mirgating from Java to Kotlin I try to use static function with Data Binding:
<data>
<import type="com.package.domain.tools.helper.StringValidator"/>
...
</data>
Then I call function hideNumber:
<com.hastee.pay.ui.view.Text
...
android:text='#{StringValidator.hideNumber(account.number)}'
app:layout_constraintRight_toRightOf="#+id/number"
app:layout_constraintTop_toBottomOf="#+id/number" />
Using databinding here causes error:
[kapt] An exception occurred:
android.databinding.tool.util.LoggedErrorException: Found data binding
errors.
****/ data binding error ****msg:cannot find method
hideNumber(java.lang.String) in class
com.package.domain.tools.helper.StringValidator....
Here's this object:
object StringValidator {
...
fun hideNumber(number: String): String {
return "****" + number.substring(number.length - 4)
}
}
How can I reach this function using Kotlin and Data Binding?
The data binding compiler is looking for a static method.
Since a named object alone is not enough to make all methods inside that object static, you need an additional #JvmStatic annotation on your hideNumber-method:
#JvmStatic
fun hideNumber(number: String): String {
return "****" + number.substring(number.length - 4)
}
see also: https://kotlinlang.org/docs/reference/java-to-kotlin-interop.html#static-methods