Trying to run a very simple Spring Boot 3 app on GraalVM (openjdk 19.0.1). The app just stores some data in H2 on startup. It is basically the same as what Josh does in the Spring Tips about Ahead-of-Time compilation, but using Kotlin instead of Java.
When starting the native image, I get the following error after Spring startup:
java.lang.UnsupportedOperationException: Kotlin class com.example.demo.basics.Customer has no .copy(…) method for property id
at org.springframework.data.mapping.model.BeanWrapper$KotlinCopyUtil.setProperty(BeanWrapper.java:171) ~[na:na]
at org.springframework.data.mapping.model.BeanWrapper.setProperty(BeanWrapper.java:79) ~[na:na]
...
The code which fails is the following class:
@Configuration
class BasicsConfiguration {
@Bean // execute on application start
fun basicsApplicationListener(customerRepository: CustomerRepository): ApplicationListener<ApplicationReadyEvent> {
return ApplicationListener<ApplicationReadyEvent> {
// store some values in the database
customerRepository
.saveAll(listOf("A", "B", "C").map { Customer(null, it) })
.forEach { println(it) }
}
}
}
interface CustomerRepository : CrudRepository<Customer, Int>
data class Customer(@Id val id: Long?, val name: String)
Running the app on the JDK works perfectly fine: ./gradlew bootRun
2022-11-30T11:23:15.300+01:00 INFO 33997 --- [ main] com.example.demo.DemoApplicationKt : Started DemoApplicationKt in 2.383 seconds (process running for 2.733)
Customer(id=1, name=A)
Customer(id=2, name=B)
Customer(id=3, name=C)
The native image is also created successfully: ./gradlew nativeCompile
Starting the native image works, the server process starts up but then fails:
2022-11-30T11:08:11.085+01:00 INFO 33059 --- [ main] com.example.demo.DemoApplicationKt : Started DemoApplicationKt in 0.147 seconds (process running for 0.158)
2022-11-30T11:08:11.089+01:00 ERROR 33059 --- [ main] o.s.boot.SpringApplication : Application run failed
java.lang.UnsupportedOperationException: Kotlin class com.example.demo.basics.Customer has no .copy(…) method for property id
at org.springframework.data.mapping.model.BeanWrapper$KotlinCopyUtil.setProperty(BeanWrapper.java:171) ~[na:na]
at org.springframework.data.mapping.model.BeanWrapper.setProperty(BeanWrapper.java:79) ~[na:na]
...
When I add a special reflect-config.json the code runs successfully:
[
{
"name": "com.example.demo.basics.Customer",
"allDeclaredFields": true,
"queryAllDeclaredMethods": true,
"queryAllPublicMethods": true,
"queryAllDeclaredConstructors": true,
"methods": [
{
"name": "<init>",
"parameterTypes": [
"java.lang.Long",
"java.lang.String"
]
},
{
"name": "component1",
"parameterTypes": []
},
{
"name": "component2",
"parameterTypes": []
},
{
"name": "copy",
"parameterTypes": [
"java.lang.Long",
"java.lang.String"
]
},
{
"name": "copy$default",
"parameterTypes": [
"com.example.demo.basics.Customer",
"java.lang.Long",
"java.lang.String",
"int",
"java.lang.Object"
]
},
{
"name": "equals",
"parameterTypes": [
"java.lang.Object"
]
},
{
"name": "hashCode",
"parameterTypes": []
},
{
"name": "toString",
"parameterTypes": []
}
]
}]
Of course I do not want to manually add a reflect config for each data class. Am I missing a config param or is something not fully created?
Repo: https://github.com/HenningWaack/springboot-kotlin-graalvm-demo
Thanks!
Comment From: philwebb
It looks like Spring Data might be missing hints for that code path. I've raised https://github.com/spring-projects/spring-data-commons/issues/2748 so that they can take a look.