3
votes

@Transient sur spring-data-mongodb avec Kotlin entraîne une exception lors de la lecture

J'ai un problème dans mon projet avec une combinaison de Kotlin, SpringBoot 2.0 et MongoDB (avec Spring Data), avec @Transient . Tout d'abord, voici comment j'ai déclaré mes classes de données

org.springframework.data.mapping.MappingException: No property child found on entity class com.example.sample.domain.Parent to bind constructor parameter to!
    at org.springframework.data.mapping.model.PersistentEntityParameterValueProvider.getParameterValue(PersistentEntityParameterValueProvider.java:68)
    at org.springframework.data.mapping.model.SpELExpressionParameterValueProvider.getParameterValue(SpELExpressionParameterValueProvider.java:49)
    at org.springframework.data.convert.ClassGeneratingEntityInstantiator$EntityInstantiatorAdapter.extractInvocationArguments(ClassGeneratingEntityInstantiator.java:250)
    at org.springframework.data.convert.ClassGeneratingEntityInstantiator$EntityInstantiatorAdapter.createInstance(ClassGeneratingEntityInstantiator.java:223)
    at org.springframework.data.convert.ClassGeneratingEntityInstantiator.createInstance(ClassGeneratingEntityInstantiator.java:84)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:272)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:245)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:194)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:190)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:78)
    at org.springframework.data.mongodb.core.ReactiveMongoTemplate$ReadDocumentCallback.doWith(ReactiveMongoTemplate.java:2920)
    at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:100)
    at reactor.core.publisher.MonoNext$NextSubscriber.onNext(MonoNext.java:76)
    at com.mongodb.reactivestreams.client.internal.ObservableToPublisher$1.onNext(ObservableToPublisher.java:68)
    at com.mongodb.async.client.AbstractSubscription.onNext(AbstractSubscription.java:135)
    at com.mongodb.async.client.AbstractSubscription.processResultsQueue(AbstractSubscription.java:203)
    at com.mongodb.async.client.AbstractSubscription.tryProcessResultsQueue(AbstractSubscription.java:159)
    at com.mongodb.async.client.SingleResultCallbackSubscription$1.onResult(SingleResultCallbackSubscription.java:48)
    at com.mongodb.async.client.FindIterableImpl$1$1.onResult(FindIterableImpl.java:213)
    at com.mongodb.async.client.FindIterableImpl$1$1.onResult(FindIterableImpl.java:204)
    at com.mongodb.operation.AsyncQueryBatchCursor.next(AsyncQueryBatchCursor.java:136)
    at com.mongodb.operation.AsyncQueryBatchCursor.next(AsyncQueryBatchCursor.java:100)
    at com.mongodb.async.client.FindIterableImpl$1.onResult(FindIterableImpl.java:204)
    at com.mongodb.async.client.FindIterableImpl$1.onResult(FindIterableImpl.java:198)
    at com.mongodb.internal.async.ErrorHandlingResultCallback.onResult(ErrorHandlingResultCallback.java:49)
    at com.mongodb.async.client.OperationExecutorImpl$1$1.onResult(OperationExecutorImpl.java:82)
    at com.mongodb.internal.async.ErrorHandlingResultCallback.onResult(ErrorHandlingResultCallback.java:49)
    at com.mongodb.operation.FindOperation$3.onResult(FindOperation.java:806)
    at com.mongodb.operation.OperationHelper$ReferenceCountedReleasingWrappedCallback.onResult(OperationHelper.java:364)
    at com.mongodb.operation.CommandOperationHelper$2.onResult(CommandOperationHelper.java:405)
    at com.mongodb.internal.async.ErrorHandlingResultCallback.onResult(ErrorHandlingResultCallback.java:49)
    at com.mongodb.internal.connection.DefaultServer$DefaultServerProtocolExecutor$2.onResult(DefaultServer.java:227)
    at com.mongodb.internal.async.ErrorHandlingResultCallback.onResult(ErrorHandlingResultCallback.java:49)
    at com.mongodb.internal.connection.CommandProtocolImpl$1.onResult(CommandProtocolImpl.java:85)
    at com.mongodb.internal.connection.DefaultConnectionPool$PooledConnection$1.onResult(DefaultConnectionPool.java:461)
    at com.mongodb.internal.connection.UsageTrackingInternalConnection$2.onResult(UsageTrackingInternalConnection.java:111)
    at com.mongodb.internal.async.ErrorHandlingResultCallback.onResult(ErrorHandlingResultCallback.java:49)
    at com.mongodb.internal.connection.InternalStreamConnection$2$1.onResult(InternalStreamConnection.java:379)
    at com.mongodb.internal.connection.InternalStreamConnection$2$1.onResult(InternalStreamConnection.java:356)
    at com.mongodb.internal.connection.InternalStreamConnection$MessageHeaderCallback$MessageCallback.onResult(InternalStreamConnection.java:651)
    at com.mongodb.internal.connection.InternalStreamConnection$MessageHeaderCallback$MessageCallback.onResult(InternalStreamConnection.java:618)
    at com.mongodb.internal.connection.InternalStreamConnection$5.completed(InternalStreamConnection.java:494)
    at com.mongodb.internal.connection.InternalStreamConnection$5.completed(InternalStreamConnection.java:491)
    at com.mongodb.connection.netty.NettyStream.readAsync(NettyStream.java:236)
    at com.mongodb.internal.connection.InternalStreamConnection.readAsync(InternalStreamConnection.java:491)
    at com.mongodb.internal.connection.InternalStreamConnection.access$1000(InternalStreamConnection.java:74)
    at com.mongodb.internal.connection.InternalStreamConnection$MessageHeaderCallback.onResult(InternalStreamConnection.java:608)
    at com.mongodb.internal.connection.InternalStreamConnection$MessageHeaderCallback.onResult(InternalStreamConnection.java:593)
    at com.mongodb.internal.connection.InternalStreamConnection$5.completed(InternalStreamConnection.java:494)
    at com.mongodb.internal.connection.InternalStreamConnection$5.completed(InternalStreamConnection.java:491)
    at com.mongodb.connection.netty.NettyStream.readAsync(NettyStream.java:236)
    at com.mongodb.connection.netty.NettyStream.handleReadResponse(NettyStream.java:266)
    at com.mongodb.connection.netty.NettyStream.access$600(NettyStream.java:66)
    at com.mongodb.connection.netty.NettyStream$InboundBufferHandler.channelRead0(NettyStream.java:325)
    at com.mongodb.connection.netty.NettyStream$InboundBufferHandler.channelRead0(NettyStream.java:322)
    at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:105)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
    at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1434)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
    at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:965)
    at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163)
    at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:648)
    at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:583)
    at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:500)
    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:462)
    at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:897)
    at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
    at java.base/java.lang.Thread.run(Thread.java:844)

Je comprends qu'il pourrait y avoir une meilleure façon de faire référence à Child depuis le Parent , mais MongoDB est un existant.

Le problème ici est que je ne veux pas conserver la propriété child dans Parent , j'ai donc essayé d'annoter la propriété avec kotlin.jvm.Transient , ou org.springframework.data.annotation.Transient , ou les deux. Mais aucune combinaison ne semble résoudre mon problème. Le problème auquel je suis confronté est:

  1. Lorsque j'utilise kotlin.jvm.Transient , il est en cours de chargement, mais lorsque je le sauvegarde, il conservera la propriété child avec lui li >
  2. Lorsque j'utilise org.springframework.data.annotation.Transient , il est impossible de lire avec la trace de pile:
@Document data class Child(@Id val id: String?, val name: String)
@Document data class Parent(@Id val id: String?, val child: Child? = null, val childId: String)
  1. Lorsque j'utilise les deux annotations, c'est le même résultat que # 1 (évidemment), car l'application lit les données avant d'essayer de faire une logique.

Toute aide est grandement appréciée


0 commentaires

3 Réponses :


0
votes

Puisque vous déclarez la propriété dans le constructeur, je pense que vous devez faire ce qui suit pour appliquer l'annotation au champ qui est implicitement créé, puisque Spring Data examinera les annotations du champ au lieu des annotations de la propriété du constructeur:

@get:Transient val child: Child? = null


4 commentaires

Toujours le même. dans les 3 cas, l'erreur reste la même. :( @Document (collection = "parent") classe de données Parent (@Id val id: String? = null, @field: KotlinTransient @field: SpringTransient val child: Child? = null, val childId: String) < / code>


@JonathanHandoyo pourrait également essayer @get: Transient au lieu de @field


toujours pas ... un autre modèle que j'ai trouvé fonctionné est de déclarer les membres "transitoires" comme var s dans le corps de la classe de données , mais ce n'est pas intuitif depuis La méthode .copy (...) ne fonctionnera pas pour les membres déclarés dans le corps de la classe, et .equals (...) ne les prendra pas en compte


Je viens de vérifier quel bytecode est généré avec quelques annotations différentes, et il me semble que @get: org.springframework.data.annotation.Transient val child devrait fonctionner dans le constructeur. La méthode getter dans le bytecode a la même annotation qu'une méthode que je définis explicitement dans le corps de la classe avec l'annotation @Transient.



1
votes

Ajoutez un nouveau constructeur kotlin et donnez-lui l'annotation @PersistenceConstructor


1 commentaires

Je n'avais pas de problème parent-enfant, mais j'avais un transitoire et cela a résolu le problème pour moi.



1
votes

Cela peut être très ennuyeux, mais c'est la manière très exacte dont les données Spring fonctionnent.

Les données Spring ont besoin d'un objet Child pour créer un objet Parent après avoir chargé les données Parent de Mongo, mais il n'y a pas de données Child dans Mongo , et l'exception est levée.

Donc, nous devrions d'abord créer un objet Parent sans objet Child, puis initialiser le champ enfant de l'objet Parent.

Je pense en utilisant @ org.springframework.data.annotation.Transient et @PersistenceConstructor en tant que code suivant est la meilleure façon de le faire.

@Document data class Child(@Id val id: String?, val name: String)
@Document data class Parent(@Id val id: String?, @Transient val child: Child? = null, val childId: String) {
  @PersistenceConstructor   
  constructor(id: String, childId: String): this(id = id, childId = childId, child = null)
}


0 commentaires