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