Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3.2.0-M5] UnsupportedOperationException in Kotlin for @Version, @CreatedDate, @LastModifiedDate, #3599

Closed
juergenzimmermann opened this issue Mar 17, 2021 · 9 comments
Assignees
Labels
status: duplicate A duplicate of another issue

Comments

@juergenzimmermann
Copy link

juergenzimmermann commented Mar 17, 2021

After upgrading from 3.2.0-M4 to 3.2.0-M5 I get the stacktrace below. I'm using Kotlin 1.4.31 and basically have an entity resp. Kotlin data class like this:

data class Kunde(
    val id: UUID?,

    @Version
    val version: Int? = null,

    @CreatedDate
    private val erzeugt: LocalDateTime? = null,

    @LastModifiedDate
    private val aktualisiert: LocalDateTime? = null
)

Using 3.2.0-M5 I have to change all annotated props from val to var. Otherwise I get this stacktrace during insertion for @Version, @CreatedDate and @LastModifiedDate:

java.lang.IllegalStateException: Failed to execute CommandLineRunner
        at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:795)
        at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:776)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:333)
        at com.acme.kunde.ApplicationKt.main(Application.kt:55)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:564)
        at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)
Caused by: java.lang.UnsupportedOperationException: No accessor to set property @org.springframework.data.annotation.Version()private final java.lang.Integer com.acme.kunde.entity.Kunde.version!
        at com.acme.kunde.entity.Kunde_Accessor_1eaycm.setProperty(Unknown Source)
        at org.springframework.data.mapping.model.InstantiationAwarePropertyAccessor.setProperty(InstantiationAwarePropertyAccessor.java:104)
        at org.springframework.data.mapping.model.ConvertingPropertyAccessor.setProperty(ConvertingPropertyAccessor.java:63)
        at org.springframework.data.mongodb.core.EntityOperations$AdaptibleMappedEntity.initializeVersionProperty(EntityOperations.java:736)
        at org.springframework.data.mongodb.core.ReactiveMongoTemplate.lambda$doInsert$33(ReactiveMongoTemplate.java:1320)
        at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:113)
        at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1815)
        at reactor.core.publisher.MonoFlatMap$FlatMapInner.onNext(MonoFlatMap.java:249)
        at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:127)
        at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1815)
        at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:151)
        at reactor.core.publisher.FluxHide$SuppressFuseableSubscriber.onNext(FluxHide.java:136)
        at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:120)
        at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1815)
        at reactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.onComplete(FluxDefaultIfEmpty.java:108)
        at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:142)
        at reactor.core.publisher.Operators.complete(Operators.java:136)
        at reactor.core.publisher.MonoEmpty.subscribe(MonoEmpty.java:45)
        at reactor.core.publisher.Mono.subscribe(Mono.java:4099)
        at reactor.core.publisher.FluxFlatMap.trySubscribeScalarMap(FluxFlatMap.java:203)
        at reactor.core.publisher.MonoFlatMap.subscribeOrReturn(MonoFlatMap.java:53)
        at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:57)
        at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:157)
        at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.onNext(FluxPeekFuseable.java:210)
        at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2397)
        at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.request(FluxPeekFuseable.java:144)
        at reactor.core.publisher.MonoFlatMap$FlatMapMain.onSubscribe(MonoFlatMap.java:110)
        at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.onSubscribe(FluxPeekFuseable.java:178)
        at reactor.core.publisher.MonoJust.subscribe(MonoJust.java:54)
        at reactor.core.publisher.Mono.subscribe(Mono.java:4099)
        at kotlinx.coroutines.reactive.AwaitKt.awaitOne(Await.kt:137)
        at kotlinx.coroutines.reactive.AwaitKt.awaitOne$default(Await.kt:135)
        at kotlinx.coroutines.reactive.AwaitKt.awaitSingle(Await.kt:81)
        at com.acme.kunde.config.dev.DbPopulate$dbPopulate$1$1$1.invokeSuspend(DbPopulate.kt:163)
        at com.acme.kunde.config.dev.DbPopulate$dbPopulate$1$1$1.invoke(DbPopulate.kt)
        at kotlinx.coroutines.flow.FlowKt__TransformKt$onEach$$inlined$unsafeTransform$1$2.emit(Collect.kt:134)
        at kotlinx.coroutines.flow.FlowKt__BuildersKt$flowOf$$inlined$unsafeFlow$1.collect(SafeCollector.common.kt:114)
        at kotlinx.coroutines.flow.FlowKt__TransformKt$onEach$$inlined$unsafeTransform$1.collect(SafeCollector.common.kt:114)
        at com.acme.kunde.config.dev.DbPopulate$dbPopulate$1$1.invokeSuspend(DbPopulate.kt:155)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
        at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:274)
        at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:84)
        at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59)
        at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
        at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:38)
        at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
        at com.acme.kunde.config.dev.DbPopulate$dbPopulate$1.run(DbPopulate.kt:77)
        at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:792)
        ... 8 more
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Mar 17, 2021
@christophstrobl christophstrobl added the for: team-attention An issue we need to discuss as a team to make progress label Mar 17, 2021
@mp911de mp911de self-assigned this Mar 17, 2021
@juergenzimmermann
Copy link
Author

@mp911de I saw spring-projects/spring-data-commons#2324. Maybe my issue is caused because I actually have:

data class Kunde(
    val id: KundeId?,

    @Version
    var version: Int? = null,
    @CreatedDate
    private var erzeugt: LocalDateTime? = null,
    @LastModifiedDate
    private var aktualisiert: LocalDateTime? = null,
) {
    @Transient
    var user: CustomUser? = null

    override fun equals(other: Any?) // ...
    override fun hashCode() // ...
}

@mp911de
Copy link
Member

mp911de commented Mar 18, 2021

We had recently a change in copy method discovery. We need to investigate what's happening here.

@mp911de
Copy link
Member

mp911de commented Mar 18, 2021

I wasn't able to reproduce the issue given both variants. I used the following data classes:

data class KundeId(val id: String)
data class CustomUser(val id: String)

data class Kunde(
    val id: KundeId?,

    var version: Int? = null,
    private var erzeugt: LocalDateTime? = null,
    private var aktualisiert: LocalDateTime? = null,
) {
    @Transient
    var user: CustomUser? = null

}

@mp911de mp911de added the status: waiting-for-feedback We need additional information before we can continue label Mar 18, 2021
@juergenzimmermann
Copy link
Author

@mp911de You have var, and not val. Also the annotations @Version, @CreatedDate and @LastModifiedDate are missing.

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Mar 18, 2021
@mp911de
Copy link
Member

mp911de commented Mar 18, 2021

Annotations don't impact the generation of the property accessor. Can you provide a minimal sample? It should only require something like this:

ClassGeneratingPropertyAccessorFactory factory = new ClassGeneratingPropertyAccessorFactory();
Kunde bean = new Kunde(UUID
		.randomUUID(), 1, LocalDateTime.MAX, LocalDateTime.MAX);

MappingContext mappingContext = …;
PersistentPropertyAccessor<Kunde> propertyAccessor = factory.getPropertyAccessor(
		mappingContext.getRequiredPersistentEntity(bean.getClass()),
		bean);
InstantiationAwarePropertyAccessor ipa = new InstantiationAwarePropertyAccessor(propertyAccessor, new EntityInstantiators());
PersistentEntity<?, ?> entity = mappingContext
		.getRequiredPersistentEntity(bean.getClass());

ipa.setProperty(entity.getRequiredPersistentProperty("version"), 2);
ipa.setProperty(entity.getRequiredPersistentProperty("erzeugt"), LocalDateTime.now());

to verify whether the PropertyAccessor works correctly.

@mp911de mp911de added status: waiting-for-feedback We need additional information before we can continue and removed status: feedback-provided Feedback has been provided labels Mar 18, 2021
@juergenzimmermann
Copy link
Author

@mp911de I tried to strip down a simple testcase. Now it's too simple and the issue is gone. I'll come back on Sunday or Monday.

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Mar 19, 2021
@juergenzimmermann
Copy link
Author

@mp911de I have a testcase attached. When you run .\gradlew bootRun you'll get the error. There are two variants so that the error disappears:
a) In Kunde.kt remove the property interessen, and in AppConfig.kt remove interessen from the schema definition and from the test value. OR:
b) In Kunde.kt remove the annotation @Version

I had no idea how to elaborate your sketch above because I don't know the semantics of e.g. MappingContext and the various Accessor classes.
testcase-3599.zip

@mp911de
Copy link
Member

mp911de commented Mar 22, 2021

That's a duplicate of spring-projects/spring-data-commons#2336. Let's move the discussion to the Commons ticket.

The issue is caused by a non-symmetric generics erasure where the Kotlin type (from its primary constructor) resolves to List<InteresseType> while the synthetic copy method resolves to List<?> and we check assignability according to Java rules.

@mp911de mp911de closed this as completed Mar 22, 2021
@mp911de mp911de added status: duplicate A duplicate of another issue and removed for: team-attention An issue we need to discuss as a team to make progress status: feedback-provided Feedback has been provided status: waiting-for-triage An issue we've not yet triaged labels Mar 22, 2021
@juergenzimmermann
Copy link
Author

@mp911de Confirmed. When I use Spring Data Commons 2.5.0-SNAPSHOT as mentioned in spring-projects/spring-data-commons#2336 , the issue is gone.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: duplicate A duplicate of another issue
Projects
None yet
Development

No branches or pull requests

4 participants