SpringDataJPA 程序未配置乐观锁的情况下,报了乐观锁异常
问题
- 报错日志:详见文章结尾
附:报错日志
程序ORM框架使用的SpringData JPA,程序中未配置@Version或者@OptimisticLocking注解,但是报了一个乐观锁异常。Cause By中可以看到数据是被其它线程更改了。
- 程序逻辑:
@Transaction void method(String name) { // 略 // @Lock(value = LockModeType.PESSIMISTIC_WRITE) // User findPessimisticWriteByUsername(String id); User user = findPessimisticWriteByUsername(name); if (user == null) { user = new User(); user.setUsername(name); } userDao.save(user); }
- 数据库:MySQL
原因
悲观锁 for update 是当前读,普通查询是快照读。
- 程序事务开始后,在悲观锁读之前,其它逻辑查询过一次数据库,所以数据库生成了一次快照
- 在悲观锁读之前,数据被删除了
- 然后程序悲观锁读数据不存在,所以新建了一条记录想插入到数据库中
- 但是save之前会select一次,这次是快照读,所以jpa判断数据存在,然后想要update,但是实际数据库中没有这条数据。update失败
复现
@Slf4j @Service public class TestService { @Autowired private TestTableDao testTableDao; @SneakyThrows @Transactional public String testPessimisticWrite(String id) { log(TransactionAspectSupport.currentTransactionStatus().toString()); testTableDao.findById("111").ifPresent(System.out::println); TestTable testTable1 = testTableDao.findById(id).orElseGet(() -> null); log("finished query1 " + testTable1); TestTable testTable = testTableDao.findPessimisticWriteByUsername(id).orElseGet(() -> null); log("finished query2 " + testTable); TestTable testTable2 = testTableDao.findById(id).orElseGet(() -> null); log("finished query3 " + testTable2); if (testTable == null) { testTable = new TestTable().setUsername(id); } log("start save"); TestTable save = testTableDao.save(testTable); log("finished save " + save.getPassword()); return save.toString(); } private void log(String startQuery) { log.info(" ----------- " + Thread.currentThread().getName() + " --- " + startQuery); } }
在第一次TestTable testTable1 = testTableDao.findById(id).orElseGet(() -> null);
前打个断点。
step1.调用这个方法查询一条数据库已有的数据2222
step2.到断点处程序停止,然后去数据库中删除2222
数据
step3.放行方法
可以看到,只有select for update查询到的2222
是null,因为它是当前读。
程序最终会报一个乐观锁异常:
附:报错日志
org.springframework.orm.ObjectOptimisticLockingFailureException: Object of class [com.totainfo.entity.ppt.McsCarrier] with identifier [H66P0430]: optimistic locking failed; nested exception is org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.totainfo.entity.ppt.McsCarrier#H66P0430] at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:337) at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:255) at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:531) at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61) at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:242) at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:154) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:178) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:95) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212) at com.sun.proxy.$Proxy142.save(Unknown Source) at com.totainfo.synchronize.RetWcsSynchronizeSecsServiceImpl.lambda$enhancedPorts$4(RetWcsSynchronizeSecsServiceImpl.java:351) at java.util.ArrayList$Itr.forEachRemaining(ArrayList.java:891) at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801) at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580) at com.totainfo.synchronize.RetWcsSynchronizeSecsServiceImpl.enhancedPorts(RetWcsSynchronizeSecsServiceImpl.java:322) at com.totainfo.synchronize.RetWcsSynchronizeSecsServiceImpl.subMainProc(RetWcsSynchronizeSecsServiceImpl.java:82) at com.totainfo.synchronize.RetWcsSynchronizeSecsServiceImpl.subMainProc(RetWcsSynchronizeSecsServiceImpl.java:34) at com.totainfo.synchronize.RetWcsSynchronizeSecsServiceImpl$$FastClassBySpringCGLIB$$a4ca3f32.invoke(<generated>) at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688) at com.totainfo.synchronize.RetWcsSynchronizeSecsServiceImpl$$EnhancerBySpringCGLIB$$b7e2b709.subMainProc(<generated>) at com.totainfo.send.RetSendWcsSecsServiceImpl.handleS1F4(RetSendWcsSecsServiceImpl.java:137) at com.totainfo.send.RetSendWcsSecsServiceImpl.S1F3(RetSendWcsSecsServiceImpl.java:87) at com.totainfo.send.RetSendWcsSecsServiceImpl$$FastClassBySpringCGLIB$$dc9e2ffa.invoke(<generated>) at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688) at com.totainfo.send.RetSendWcsSecsServiceImpl$$EnhancerBySpringCGLIB$$7cd74661.S1F3(<generated>) at com.totainfo.recieive.RetReceiveWcsSecsServiceImpl.scPauseCompleted(RetReceiveWcsSecsServiceImpl.java:2076) at com.totainfo.recieive.RetReceiveWcsSecsServiceImpl.subMainProc(RetReceiveWcsSecsServiceImpl.java:245) at com.totainfo.recieive.RetReceiveWcsSecsServiceImpl.subMainProc(RetReceiveWcsSecsServiceImpl.java:61) at com.totainfo.common.core.bean.base.serviceabstract.ICIMBaseServiceAbstract.mainProc(Unknown Source) at com.totainfo.common.core.bean.base.serviceabstract.ICIMBaseServiceAbstract$$FastClassBySpringCGLIB$$3038395b.invoke(<generated>) at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:779) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:367) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:118) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750) at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:692) at com.totainfo.recieive.RetReceiveWcsSecsServiceImpl$$EnhancerBySpringCGLIB$$49e64182.mainProc(<generated>) at com.totainfo.listener.RetWcsListener.rptId13(RetWcsListener.java:595) at com.totainfo.listener.RetWcsListener.receiveWcsS6F11(RetWcsListener.java:156) at com.totainfo.listener.RetWcsListener.subMainProc(RetWcsListener.java:69) at com.totainfo.serviceabstract.ICIMMcsAbstractService.mainProc(ICIMMcsAbstractService.java:32) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:282) at com.lmrj.util.spring.SpringContext.reInvoke(SpringContext.java:134) at com.lmrj.codec.secs.e.a.iiIiiIIIIi.???()(rb:21) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748) Caused by: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.totainfo.entity.ppt.McsCarrier#H66P0430] at org.hibernate.persister.entity.AbstractEntityPersister.check(AbstractEntityPersister.java:2649) at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3492) at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:3355) at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3769) at org.hibernate.action.internal.EntityUpdateAction.execute(EntityUpdateAction.java:201) at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:604) at org.hibernate.engine.spi.ActionQueue.lambda$executeActions$1(ActionQueue.java:478) at java.util.LinkedHashMap.forEach(LinkedHashMap.java:684) at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:475) at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:344) at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:40) at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:99) at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1362) at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1349) at sun.reflect.GeneratedMethodAccessor198.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:314) at com.sun.proxy.$Proxy121.flush(Unknown Source) at org.springframework.data.jpa.repository.support.SimpleJpaRepository.flush(SimpleJpaRepository.java:601) at com.totainfo.common.core.dao.BaseDaoImpl.save(Unknown Source) at sun.reflect.GeneratedMethodAccessor197.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.springframework.data.repository.core.support.ImplementationInvocationMetadata.invoke(ImplementationInvocationMetadata.java:72) at org.springframework.data.repository.core.support.RepositoryComposition$RepositoryFragments.invoke(RepositoryComposition.java:382) at org.springframework.data.repository.core.support.RepositoryComposition.invoke(RepositoryComposition.java:205) at org.springframework.data.repository.core.support.RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke(RepositoryFactorySupport.java:550) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:155) at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:130) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:80) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:367) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:118) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:139) ... 53 common frames omitted
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)