Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Error when save Object - Optimistic lock exception on saving entity

I just doing simple transactions like :

  1. findByVariable(String variable);
  2. manipulate the data, then
  3. save(data);

but i got Exception like this

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)

I have @Version already on my Entity and when i was debuging, i saw that the version is greater than the existing value in database. in database the version is 0 and exactly when i try to save(data) the version has increased to 1 which is correct.

I have already read the spring docs about Mongodb Optimistic Locking and i think i didn't do anything wrong :')

Please take a look my entity below

@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;

}
like image 405
donny Avatar asked Oct 26 '25 23:10

donny


1 Answers

in case someone encounter the same issue, on our legacy project we using spring data mongo version org.springframework.data:spring-data-mongodb:1.10.23.RELEASE and on this legacy code we also overriding the mongo id.

And it seem the root cause is because we force the spring data mongo to _id instead id this make the mongo object converter to save id as String instead of mongo object. The solution is simply remove @Field(value = "_id") or dont modify id as other string alias. But if you want to make alias for this id I suppose you need extend the mongo object converter class MappingMongoConverter and make your own implementation.

note: the unexpected behavior happen because on the new version of spring data mongo have different implementation on mongo object converter MappingMongoConverter class when handling alias for mongo id. If the alias not exactly id then when saving data id will be saved as String

like image 200
satrioaditya Avatar answered Oct 29 '25 14:10

satrioaditya



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!