最新要闻

广告

手机

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

家电

天天观察:SpringDataJPA 程序未配置乐观锁的情况下,报了乐观锁异常

来源:博客园


(资料图片仅供参考)

目录
  • 问题
  • 原因
  • 复现
  • 附:报错日志

问题

  • 报错日志:详见文章结尾附:报错日志

程序ORM框架使用的SpringData JPA,程序中未配置@Version或者@OptimisticLocking注解,但是报了一个乐观锁异常。Cause By中可以看到数据是被其它线程更改了。

  • 程序逻辑:
@Transactionvoid 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@Servicepublic 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.调用这个方法查询一条数据库已有的数据2222step2.到断点处程序停止,然后去数据库中删除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()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()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()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()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()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()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

关键词: 的情况下