保存对象时出错 - 保存实体时出现乐观锁异常
Error when save Object - Optimistic lock exception on saving entity
我只是在做简单的交易,比如:
- findByVariable(字符串变量);
- 处理数据,然后
- 保存(数据);
但我得到了这样的异常
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(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.onNext(SingleResultObservableToPublisher.java:42)
at com.mongodb.reactivestreams.client.internal.ObservableToPublisher.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.onResult(SingleResultCallbackSubscription.java:48)
at com.mongodb.async.client.MongoCollectionImpl.onResult(MongoCollectionImpl.java:647)
at com.mongodb.async.client.MongoCollectionImpl.onResult(MongoCollectionImpl.java:641)
at com.mongodb.async.client.MongoCollectionImpl.onResult(MongoCollectionImpl.java:1138)
at com.mongodb.async.client.MongoCollectionImpl.onResult(MongoCollectionImpl.java:1122)
at com.mongodb.internal.async.ErrorHandlingResultCallback.onResult(ErrorHandlingResultCallback.java:49)
at com.mongodb.async.client.OperationExecutorImpl.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.access00(MixedBulkWriteOperation.java:72)
at com.mongodb.operation.MixedBulkWriteOperation.onResult(MixedBulkWriteOperation.java:507)
at com.mongodb.operation.MixedBulkWriteOperation.onResult(MixedBulkWriteOperation.java:479)
at com.mongodb.internal.async.ErrorHandlingResultCallback.onResult(ErrorHandlingResultCallback.java:49)
at com.mongodb.internal.connection.DefaultServer$DefaultServerProtocolExecutor.onResult(DefaultServer.java:245)
at com.mongodb.internal.async.ErrorHandlingResultCallback.onResult(ErrorHandlingResultCallback.java:49)
at com.mongodb.internal.connection.CommandProtocolImpl.onResult(CommandProtocolImpl.java:85)
at com.mongodb.internal.connection.DefaultConnectionPool$PooledConnection.onResult(DefaultConnectionPool.java:467)
at com.mongodb.internal.connection.UsageTrackingInternalConnection.onResult(UsageTrackingInternalConnection.java:111)
at com.mongodb.internal.async.ErrorHandlingResultCallback.onResult(ErrorHandlingResultCallback.java:49)
at com.mongodb.internal.connection.InternalStreamConnection.onResult(InternalStreamConnection.java:399)
at com.mongodb.internal.connection.InternalStreamConnection.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.completed(InternalStreamConnection.java:514)
at com.mongodb.internal.connection.InternalStreamConnection.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.access00(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.completed(InternalStreamConnection.java:514)
at com.mongodb.internal.connection.InternalStreamConnection.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.access0(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.run(SingleThreadEventExecutor.java:989)
at io.netty.util.internal.ThreadExecutorMap.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.lang.Thread.run(Thread.java:748)
我的实体上已经有 @Version
,当我调试时,我看到版本大于数据库中的现有值。在数据库中 version
是 0
并且当我尝试 save(data)
时 version
已经增加到 1
这是正确的。
我已经阅读了关于 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;
}
以防有人遇到同样的问题,在我们的遗留项目中,我们使用 spring 数据 mongo 版本 org.springframework.data:spring-data-mongodb:1.10.23.RELEASE
,在这个遗留代码中,我们也覆盖了 mongo id
.
似乎根本原因是因为我们强制 spring 数据 mongo 为 _id
而不是 id
这使得 mongo 对象转换器为将 id
保存为字符串而不是 mongo 对象。解决方案是简单地删除 @Field(value = "_id")
或不要将 id
修改为其他字符串别名。但是如果你想为这个 id
创建别名,我想你需要扩展 mongo 对象转换器 class MappingMongoConverter
并自己实现。
注意:意外行为的发生是因为在 spring 的新版本上数据 mongo 在 mongo 对象转换器 MappingMongoConverter
class 上有不同的实现处理 mongo id
的别名。如果别名不完全 id
那么在保存数据时 id
将被保存为 String
我只是在做简单的交易,比如:
- findByVariable(字符串变量);
- 处理数据,然后
- 保存(数据);
但我得到了这样的异常
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(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.onNext(SingleResultObservableToPublisher.java:42)
at com.mongodb.reactivestreams.client.internal.ObservableToPublisher.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.onResult(SingleResultCallbackSubscription.java:48)
at com.mongodb.async.client.MongoCollectionImpl.onResult(MongoCollectionImpl.java:647)
at com.mongodb.async.client.MongoCollectionImpl.onResult(MongoCollectionImpl.java:641)
at com.mongodb.async.client.MongoCollectionImpl.onResult(MongoCollectionImpl.java:1138)
at com.mongodb.async.client.MongoCollectionImpl.onResult(MongoCollectionImpl.java:1122)
at com.mongodb.internal.async.ErrorHandlingResultCallback.onResult(ErrorHandlingResultCallback.java:49)
at com.mongodb.async.client.OperationExecutorImpl.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.access00(MixedBulkWriteOperation.java:72)
at com.mongodb.operation.MixedBulkWriteOperation.onResult(MixedBulkWriteOperation.java:507)
at com.mongodb.operation.MixedBulkWriteOperation.onResult(MixedBulkWriteOperation.java:479)
at com.mongodb.internal.async.ErrorHandlingResultCallback.onResult(ErrorHandlingResultCallback.java:49)
at com.mongodb.internal.connection.DefaultServer$DefaultServerProtocolExecutor.onResult(DefaultServer.java:245)
at com.mongodb.internal.async.ErrorHandlingResultCallback.onResult(ErrorHandlingResultCallback.java:49)
at com.mongodb.internal.connection.CommandProtocolImpl.onResult(CommandProtocolImpl.java:85)
at com.mongodb.internal.connection.DefaultConnectionPool$PooledConnection.onResult(DefaultConnectionPool.java:467)
at com.mongodb.internal.connection.UsageTrackingInternalConnection.onResult(UsageTrackingInternalConnection.java:111)
at com.mongodb.internal.async.ErrorHandlingResultCallback.onResult(ErrorHandlingResultCallback.java:49)
at com.mongodb.internal.connection.InternalStreamConnection.onResult(InternalStreamConnection.java:399)
at com.mongodb.internal.connection.InternalStreamConnection.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.completed(InternalStreamConnection.java:514)
at com.mongodb.internal.connection.InternalStreamConnection.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.access00(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.completed(InternalStreamConnection.java:514)
at com.mongodb.internal.connection.InternalStreamConnection.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.access0(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.run(SingleThreadEventExecutor.java:989)
at io.netty.util.internal.ThreadExecutorMap.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.lang.Thread.run(Thread.java:748)
我的实体上已经有 @Version
,当我调试时,我看到版本大于数据库中的现有值。在数据库中 version
是 0
并且当我尝试 save(data)
时 version
已经增加到 1
这是正确的。
我已经阅读了关于 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;
}
以防有人遇到同样的问题,在我们的遗留项目中,我们使用 spring 数据 mongo 版本 org.springframework.data:spring-data-mongodb:1.10.23.RELEASE
,在这个遗留代码中,我们也覆盖了 mongo id
.
似乎根本原因是因为我们强制 spring 数据 mongo 为 _id
而不是 id
这使得 mongo 对象转换器为将 id
保存为字符串而不是 mongo 对象。解决方案是简单地删除 @Field(value = "_id")
或不要将 id
修改为其他字符串别名。但是如果你想为这个 id
创建别名,我想你需要扩展 mongo 对象转换器 class MappingMongoConverter
并自己实现。
注意:意外行为的发生是因为在 spring 的新版本上数据 mongo 在 mongo 对象转换器 MappingMongoConverter
class 上有不同的实现处理 mongo id
的别名。如果别名不完全 id
那么在保存数据时 id
将被保存为 String