首页 > 解决方案 > 保存对象时出错 - 保存实体时出现乐观锁定异常

问题描述

我只是做简单的交易,比如:

  1. findByVariable(字符串变量);
  2. 操作数据,然后
  3. 保存数据);

但我得到了这样的异常

org.springframework.dao.OptimisticLockingFailureException: Optimistic lock exception on saving entity: Document{{dataKey=A, lastValue=XXX, version=1, createdDate=Mon Apr 13 21:53:25 WIB 2020, updatedDate=Mon Apr 13 22:34:28 WIB 2020, _class=SomeData}} to collection some_data
    at org.springframework.data.mongodb.core.ReactiveMongoTemplate.lambda$doUpdate$65(ReactiveMongoTemplate.java:1819)
    at reactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:177)
    at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:73)
    at reactor.core.publisher.MonoFlatMapMany$FlatMapManyInner.onNext(MonoFlatMapMany.java:242)
    at com.mongodb.reactivestreams.client.internal.SingleResultObservableToPublisher$1.onNext(SingleResultObservableToPublisher.java:42)
    at com.mongodb.reactivestreams.client.internal.ObservableToPublisher$1.onNext(ObservableToPublisher.java:66)
    at com.mongodb.async.client.AbstractSubscription.onNext(AbstractSubscription.java:142)
    at com.mongodb.async.client.AbstractSubscription.processResultsQueue(AbstractSubscription.java:217)
    at com.mongodb.async.client.AbstractSubscription.tryProcessResultsQueue(AbstractSubscription.java:172)
    at com.mongodb.async.client.SingleResultCallbackSubscription$1.onResult(SingleResultCallbackSubscription.java:48)
    at com.mongodb.async.client.MongoCollectionImpl$4.onResult(MongoCollectionImpl.java:647)
    at com.mongodb.async.client.MongoCollectionImpl$4.onResult(MongoCollectionImpl.java:641)
    at com.mongodb.async.client.MongoCollectionImpl$10.onResult(MongoCollectionImpl.java:1138)
    at com.mongodb.async.client.MongoCollectionImpl$10.onResult(MongoCollectionImpl.java:1122)
    at com.mongodb.internal.async.ErrorHandlingResultCallback.onResult(ErrorHandlingResultCallback.java:49)
    at com.mongodb.async.client.OperationExecutorImpl$2$1$1.onResult(OperationExecutorImpl.java:140)
    at com.mongodb.internal.async.ErrorHandlingResultCallback.onResult(ErrorHandlingResultCallback.java:49)
    at com.mongodb.operation.OperationHelper$ConnectionReleasingWrappedCallback.onResult(OperationHelper.java:432)
    at com.mongodb.operation.MixedBulkWriteOperation.addBatchResult(MixedBulkWriteOperation.java:527)
    at com.mongodb.operation.MixedBulkWriteOperation.access$1600(MixedBulkWriteOperation.java:72)
    at com.mongodb.operation.MixedBulkWriteOperation$6.onResult(MixedBulkWriteOperation.java:507)
    at com.mongodb.operation.MixedBulkWriteOperation$6.onResult(MixedBulkWriteOperation.java:479)
    at com.mongodb.internal.async.ErrorHandlingResultCallback.onResult(ErrorHandlingResultCallback.java:49)
    at com.mongodb.internal.connection.DefaultServer$DefaultServerProtocolExecutor$2.onResult(DefaultServer.java:245)
    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:467)
    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:399)
    at com.mongodb.internal.connection.InternalStreamConnection$2$1.onResult(InternalStreamConnection.java:376)
    at com.mongodb.internal.connection.InternalStreamConnection$MessageHeaderCallback$MessageCallback.onResult(InternalStreamConnection.java:677)
    at com.mongodb.internal.connection.InternalStreamConnection$MessageHeaderCallback$MessageCallback.onResult(InternalStreamConnection.java:644)
    at com.mongodb.internal.connection.InternalStreamConnection$5.completed(InternalStreamConnection.java:514)
    at com.mongodb.internal.connection.InternalStreamConnection$5.completed(InternalStreamConnection.java:511)
    at com.mongodb.connection.netty.NettyStream.readAsync(NettyStream.java:233)
    at com.mongodb.internal.connection.InternalStreamConnection.readAsync(InternalStreamConnection.java:511)
    at com.mongodb.internal.connection.InternalStreamConnection.access$1000(InternalStreamConnection.java:76)
    at com.mongodb.internal.connection.InternalStreamConnection$MessageHeaderCallback.onResult(InternalStreamConnection.java:634)
    at com.mongodb.internal.connection.InternalStreamConnection$MessageHeaderCallback.onResult(InternalStreamConnection.java:619)
    at com.mongodb.internal.connection.InternalStreamConnection$5.completed(InternalStreamConnection.java:514)
    at com.mongodb.internal.connection.InternalStreamConnection$5.completed(InternalStreamConnection.java:511)
    at com.mongodb.connection.netty.NettyStream.readAsync(NettyStream.java:233)
    at com.mongodb.connection.netty.NettyStream.handleReadResponse(NettyStream.java:263)
    at com.mongodb.connection.netty.NettyStream.access$800(NettyStream.java:69)
    at com.mongodb.connection.netty.NettyStream$InboundBufferHandler.channelRead0(NettyStream.java:322)
    at com.mongodb.connection.netty.NettyStream$InboundBufferHandler.channelRead0(NettyStream.java:319)
    at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:99)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:377)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:363)
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:355)
    at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:377)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:363)
    at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
    at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163)
    at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:714)
    at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:650)
    at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:576)
    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493)
    at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989)
    at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
    at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
    at java.lang.Thread.run(Thread.java:748)

@Version已经在我的实体上,当我调试时,我看到版本大于数据库中的现有值。在数据库中version0当我尝试增加到正确时save(data),它是正确的。version1

我已经阅读了关于Mongodb Optimistic Locking的 spring 文档,我认为我没有做错任何事情:')

请在下面查看我的实体

@Data
@EqualsAndHashCode(callSuper = true)
@SuperBuilder
@NoArgsConstructor
@Document(collection = CollectionName.SOME_DATA)
public class SomeData implement Serializable {
  private static final long serialVersionUID = 1L;

  @Id
  @Field(value = "_id")
  private String id;

  @Version
  @Field(value = "version")
  private Long version;

  @CreatedDate
  @Field(value = "createdDate")
  private Date createdDate;

  @LastModifiedDate
  @Field(value = "updatedDate")
  private Date updatedDate;

  @Indexed
  @Field(value = "dataKey")
  private String dataKey;

  @Indexed
  @Field(value = "lastValue")
  private String lastValue;

}

标签: javaspringmongodbspring-mongodb

解决方案


如果有人遇到同样的问题,在我们的遗留项目中,我们使用 spring data mongo 版本org.springframework.data:spring-data-mongodb:1.10.23.RELEASE,在这个遗留代码上,我们还覆盖了 mongo id

似乎根本原因是因为我们强制弹簧数据 mongo_id而不是id这使得 mongo 对象转换器保存id为 String 而不是 mongo 对象。解决方案是简单地删除@Field(value = "_id")或不修改id为其他字符串别名。但是,如果您想为此创建别名,id我想您需要扩展 mongo 对象转换器类MappingMongoConverter并制作自己的实现。

MappingMongoConverter注意:发生意外行为是因为在处理 mongo 别名时,新版本的 spring data mongo 在 mongo 对象转换器类上有不同的实现id。如果别名不完全id那么保存数据id时将保存为字符串


推荐阅读