While trying around with Spring Boot 3 native-image + Kotlin I discovered an error when trying to save smth in a H2 database:
java.lang.UnsupportedOperationException: Kotlin class com.example.demo.News has no .copy(?) method for property id
(The error is not present when running gradle :bootRun on JVM)
- Spring Boot 3.0.0
- Kotlin 1.7.21
- GraalVM CE 22.3.0 (build 17.0.5+8-jvmci-22.3-b08)
- System: Mac M1 Pro (ARM)
- Spring Initilizr Project
DemoApplication.kt
@SpringBootApplication
@RegisterReflectionForBinding(News::class)
class DemoApplication
fun main(args: Array<String>) {
runApplication<DemoApplication>(*args)
}
@Table("news")
data class News(
@Id
val id: Long = 0,
@Column("text")
val content: String,
)
interface NewsRepository : CoroutineCrudRepository<News, Long>
@Component
data class DataCreator(
private val repository: NewsRepository
) : ApplicationRunner {
override fun run(args: ApplicationArguments?) = runBlocking<Unit> {
repository.save(News(content = "News 1"))
}
}
schema.sql
CREATE TABLE IF NOT EXISTS news(
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255) NOT NULL
);
Comment From: mhalbritter
You can see the problem here https://github.com/spring-projects/spring-aot-smoke-tests/tree/main/data/data-jdbc-h2-kotlin, too
Comment From: wilkinsona
Full stack trace:
java.lang.IllegalStateException: Failed to execute ApplicationRunner
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:761) ~[gh-33351:3.0.0]
at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:748) ~[gh-33351:3.0.0]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) ~[gh-33351:3.0.0]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1302) ~[gh-33351:3.0.0]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1291) ~[gh-33351:3.0.0]
at com.example.gh33351.Gh33351ApplicationKt.main(Gh33351Application.kt:43) ~[gh-33351:na]
Caused by: java.lang.UnsupportedOperationException: Kotlin class com.example.gh33351.News 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]
at org.springframework.data.mapping.model.InstantiationAwarePropertyAccessor.setProperty(InstantiationAwarePropertyAccessor.java:81) ~[na:na]
at org.springframework.data.r2dbc.convert.MappingR2dbcConverter.potentiallySetId(MappingR2dbcConverter.java:625) ~[gh-33351:3.0.0]
at org.springframework.data.r2dbc.convert.MappingR2dbcConverter.lambda$populateIdIfNecessary$2(MappingR2dbcConverter.java:605) ~[gh-33351:3.0.0]
at io.r2dbc.h2.H2Result.lambda$map$1(H2Result.java:103) ~[na:na]
at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:106) ~[na:na]
at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79) ~[na:na]
at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:129) ~[na:na]
at reactor.core.publisher.FluxIterable$IterableSubscription.slowPath(FluxIterable.java:272) ~[gh-33351:3.5.0]
at reactor.core.publisher.FluxIterable$IterableSubscription.request(FluxIterable.java:230) ~[gh-33351:3.5.0]
at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.request(FluxMapFuseable.java:171) ~[na:na]
at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.set(Operators.java:2304) ~[gh-33351:3.5.0]
at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onSubscribe(FluxOnErrorResume.java:74) ~[na:na]
at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onSubscribe(FluxMapFuseable.java:96) ~[na:na]
at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:165) ~[na:na]
at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:87) ~[na:na]
at reactor.core.publisher.Flux.subscribe(Flux.java:8660) ~[gh-33351:3.5.0]
at reactor.core.publisher.FluxFlatMap$FlatMapMain.onNext(FluxFlatMap.java:426) ~[gh-33351:3.5.0]
at reactor.core.publisher.FluxOnAssembly$OnAssemblySubscriber.onNext(FluxOnAssembly.java:539) ~[gh-33351:3.5.0]
at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:122) ~[na:na]
at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:129) ~[na:na]
at reactor.core.publisher.FluxIterable$IterableSubscription.slowPath(FluxIterable.java:272) ~[gh-33351:3.5.0]
at reactor.core.publisher.FluxIterable$IterableSubscription.request(FluxIterable.java:230) ~[gh-33351:3.5.0]
at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.request(FluxMapFuseable.java:171) ~[na:na]
at reactor.core.publisher.FluxMap$MapSubscriber.request(FluxMap.java:164) ~[na:na]
at reactor.core.publisher.FluxOnAssembly$OnAssemblySubscriber.request(FluxOnAssembly.java:649) ~[gh-33351:3.5.0]
at reactor.core.publisher.FluxFlatMap$FlatMapMain.onSubscribe(FluxFlatMap.java:371) ~[gh-33351:3.5.0]
at reactor.core.publisher.FluxOnAssembly$OnAssemblySubscriber.onSubscribe(FluxOnAssembly.java:633) ~[gh-33351:3.5.0]
at reactor.core.publisher.FluxMap$MapSubscriber.onSubscribe(FluxMap.java:92) ~[na:na]
at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onSubscribe(FluxMapFuseable.java:96) ~[na:na]
at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:165) ~[na:na]
at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:87) ~[na:na]
at reactor.core.publisher.Flux.subscribe(Flux.java:8660) ~[gh-33351:3.5.0]
at reactor.core.publisher.FluxFlatMap.trySubscribeScalarMap(FluxFlatMap.java:200) ~[gh-33351:3.5.0]
at reactor.core.publisher.FluxFlatMap.subscribeOrReturn(FluxFlatMap.java:93) ~[gh-33351:3.5.0]
at reactor.core.publisher.Flux.subscribe(Flux.java:8646) ~[gh-33351:3.5.0]
at reactor.core.publisher.FluxUsingWhen$ResourceSubscriber.onNext(FluxUsingWhen.java:195) ~[na:na]
at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:122) ~[na:na]
at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79) ~[na:na]
at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79) ~[na:na]
at reactor.core.publisher.FluxRetry$RetrySubscriber.onNext(FluxRetry.java:87) ~[gh-33351:3.5.0]
at reactor.core.publisher.MonoFlatMap$FlatMapMain.secondComplete(MonoFlatMap.java:245) ~[gh-33351:3.5.0]
at reactor.core.publisher.MonoFlatMap$FlatMapInner.onNext(MonoFlatMap.java:305) ~[gh-33351:3.5.0]
at io.r2dbc.pool.MonoDiscardOnCancel$MonoDiscardOnCancelSubscriber.onNext(MonoDiscardOnCancel.java:92) ~[na:na]
at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79) ~[na:na]
at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.complete(MonoIgnoreThen.java:292) ~[gh-33351:3.5.0]
at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onNext(MonoIgnoreThen.java:187) ~[gh-33351:3.5.0]
at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.subscribeNext(MonoIgnoreThen.java:236) ~[gh-33351:3.5.0]
at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onComplete(MonoIgnoreThen.java:203) ~[gh-33351:3.5.0]
at reactor.core.publisher.MonoIgnoreElements$IgnoreElementsSubscriber.onComplete(MonoIgnoreElements.java:89) ~[na:na]
at reactor.core.publisher.FluxHandle$HandleSubscriber.onComplete(FluxHandle.java:222) ~[na:na]
at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onComplete(FluxSwitchIfEmpty.java:85) ~[na:na]
at reactor.core.publisher.MonoCallable$MonoCallableSubscription.request(MonoCallable.java:159) ~[gh-33351:3.5.0]
at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.set(Operators.java:2304) ~[gh-33351:3.5.0]
at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onSubscribe(Operators.java:2178) ~[gh-33351:3.5.0]
at reactor.core.publisher.MonoCallable.subscribe(MonoCallable.java:48) ~[na:na]
at reactor.core.publisher.Mono.subscribe(Mono.java:4444) ~[gh-33351:3.5.0]
at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.subscribeNext(MonoIgnoreThen.java:263) ~[gh-33351:3.5.0]
at reactor.core.publisher.MonoIgnoreThen.subscribe(MonoIgnoreThen.java:51) ~[na:na]
at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64) ~[gh-33351:3.5.0]
at io.r2dbc.pool.MonoDiscardOnCancel.subscribe(MonoDiscardOnCancel.java:50) ~[na:na]
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:165) ~[gh-33351:3.5.0]
at reactor.pool.AbstractPool$Borrower.deliver(AbstractPool.java:467) ~[gh-33351:1.0.0]
at reactor.pool.SimpleDequePool.lambda$drainLoop$8(SimpleDequePool.java:368) ~[gh-33351:1.0.0]
at reactor.core.scheduler.ImmediateScheduler.schedule(ImmediateScheduler.java:52) ~[na:na]
at reactor.pool.SimpleDequePool.drainLoop(SimpleDequePool.java:368) ~[gh-33351:1.0.0]
at reactor.pool.SimpleDequePool.pendingOffer(SimpleDequePool.java:598) ~[gh-33351:1.0.0]
at reactor.pool.SimpleDequePool.doAcquire(SimpleDequePool.java:294) ~[gh-33351:1.0.0]
at reactor.pool.AbstractPool$Borrower.request(AbstractPool.java:430) ~[gh-33351:1.0.0]
at reactor.core.publisher.MonoFlatMap$FlatMapMain.request(MonoFlatMap.java:194) ~[gh-33351:3.5.0]
at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.set(Operators.java:2304) ~[gh-33351:3.5.0]
at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onSubscribe(Operators.java:2178) ~[gh-33351:3.5.0]
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onSubscribe(MonoFlatMap.java:117) ~[gh-33351:3.5.0]
at reactor.pool.SimpleDequePool$QueueBorrowerMono.subscribe(SimpleDequePool.java:716) ~[na:na]
at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64) ~[gh-33351:3.5.0]
at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52) ~[na:na]
at reactor.core.publisher.FluxRetry$RetrySubscriber.resubscribe(FluxRetry.java:117) ~[gh-33351:3.5.0]
at reactor.core.publisher.MonoRetry.subscribeOrReturn(MonoRetry.java:50) ~[na:na]
at reactor.core.publisher.Mono.subscribe(Mono.java:4429) ~[gh-33351:3.5.0]
at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onError(FluxOnErrorResume.java:103) ~[na:na]
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onError(MonoFlatMap.java:180) ~[gh-33351:3.5.0]
at reactor.core.publisher.FluxMap$MapSubscriber.onError(FluxMap.java:134) ~[na:na]
at reactor.core.publisher.Operators.error(Operators.java:198) ~[gh-33351:3.5.0]
at reactor.core.publisher.MonoError.subscribe(MonoError.java:53) ~[na:na]
at reactor.core.publisher.MonoDeferContextual.subscribe(MonoDeferContextual.java:55) ~[na:na]
at reactor.core.publisher.Mono.subscribe(Mono.java:4444) ~[gh-33351:3.5.0]
at reactor.core.publisher.FluxUsingWhen.subscribe(FluxUsingWhen.java:104) ~[gh-33351:3.5.0]
at reactor.core.publisher.Mono.subscribe(Mono.java:4444) ~[gh-33351:3.5.0]
at reactor.core.publisher.FluxFlatMap.trySubscribeScalarMap(FluxFlatMap.java:200) ~[gh-33351:3.5.0]
at reactor.core.publisher.MonoFlatMap.subscribeOrReturn(MonoFlatMap.java:53) ~[gh-33351:3.5.0]
at reactor.core.publisher.Mono.subscribe(Mono.java:4429) ~[gh-33351:3.5.0]
at reactor.core.publisher.FluxFlatMap.trySubscribeScalarMap(FluxFlatMap.java:200) ~[gh-33351:3.5.0]
at reactor.core.publisher.MonoFlatMap.subscribeOrReturn(MonoFlatMap.java:53) ~[gh-33351:3.5.0]
at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:57) ~[gh-33351:3.5.0]
at reactor.core.publisher.MonoUsingWhen.subscribe(MonoUsingWhen.java:87) ~[na:na]
at reactor.core.publisher.Mono.subscribe(Mono.java:4444) ~[gh-33351:3.5.0]
at kotlinx.coroutines.reactive.AwaitKt.awaitOne(Await.kt:190) ~[na:na]
at kotlinx.coroutines.reactive.AwaitKt.awaitOne$default(Await.kt:183) ~[na:na]
at kotlinx.coroutines.reactive.AwaitKt.awaitSingleOrNull(Await.kt:141) ~[na:na]
at org.springframework.data.repository.core.support.RepositoryMethodInvoker.doInvokeReactiveToSuspended(RepositoryMethodInvoker.java:184) ~[gh-33351:3.0.0]
at org.springframework.data.repository.core.support.RepositoryMethodInvoker.invoke(RepositoryMethodInvoker.java:119) ~[gh-33351:3.0.0]
at org.springframework.data.repository.core.support.RepositoryComposition$RepositoryFragments.invoke(RepositoryComposition.java:516) ~[gh-33351:3.0.0]
at org.springframework.data.repository.core.support.RepositoryComposition.invoke(RepositoryComposition.java:285) ~[na:na]
at org.springframework.data.repository.core.support.RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke(RepositoryFactorySupport.java:628) ~[na:na]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[gh-33351:6.0.2]
at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:168) ~[na:na]
at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:143) ~[na:na]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[gh-33351:6.0.2]
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97) ~[na:na]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[gh-33351:6.0.2]
at org.springframework.data.repository.core.support.MethodInvocationValidator.invoke(MethodInvocationValidator.java:94) ~[na:na]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[gh-33351:6.0.2]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:218) ~[na:na]
at jdk.proxy4/jdk.proxy4.$Proxy48.save(Unknown Source) ~[na:na]
at com.example.gh33351.DataCreator$run$1.invokeSuspend(Gh33351Application.kt:39) ~[na:na]
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) ~[gh-33351:1.7.21-release-272(1.7.21)]
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106) ~[gh-33351:na]
at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:284) ~[gh-33351:na]
at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:85) ~[na:na]
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59) ~[na:na]
at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source) ~[na:na]
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:38) ~[na:na]
at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source) ~[na:na]
at com.example.gh33351.DataCreator.run(Gh33351Application.kt:38) ~[gh-33351:na]
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:758) ~[gh-33351:3.0.0]
... 5 common frames omitted
Comment From: wilkinsona
This will have to be addressed in Spring Data Commons. I've opened https://github.com/spring-projects/spring-framework/issues/29593.
Comment From: wilkinsona
Note that @RegisterReflectionForBinding(News::class) should not have been necessary. https://github.com/spring-projects/spring-data-commons/issues/2737 should address that.