首页 > 编程语言 >SpringDataJPA 程序未配置乐观锁的情况下,报了乐观锁异常

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

时间:2023-01-10 21:56:02浏览次数:39  
标签:SpringDataJPA java invoke 程序 springframework 乐观 totainfo org com

目录

问题

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

程序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

标签:SpringDataJPA,java,invoke,程序,springframework,乐观,totainfo,org,com
From: https://www.cnblogs.com/daheww/p/17041471.html

相关文章

  • Linux 下使用GCC/G++编译C++程序
    Linux下使用GCC/G++编译C++程序例程:/*hello.cpp*/#include<iostream>usingnamespacestd;intmain(){cout<<"hellocpp"<<endl;return0;}......
  • 程序开机自启动与取消自启动
    程序开机自启动与取消自启动设置或者删除程序的注册表信息以达到开机自启动或者取消开机自启动的目的。那么开始介绍:函数原型如下:intSetReg(boolset,//设置(true......
  • VS调试release程序
    按如下设置vs工程属性:cmake生成带调试信息的release工程set(CMAKE_CXX_FLAGS_RELEASE"$ENV{CXXFLAGS}-Od-Wall")set(CMAKE_CXX_FLAGS"${CMAKE_CXX_FLAGS......
  • 《程序是怎样跑起来的第八章》
    计算机只能运行本地代码(母语的)。用某种编程语言编写的程序称为源文件,保存源代码的文件称为源文件。用C语言编写的源文件的扩展名为.c。源代码无法直接运行是因为CPU能直接......
  • Java程序员用代码,计算最大公约数和最小公倍数
    作者:小傅哥博客:https://bugstack.cn源码:https://github.com/fuzhengwei/java-algorithms沉淀、分享、成长,让自己和他人都能有所收获!......
  • 每个Java程序员都必须知道的四种负载均衡算法
    前言一般来说,我们在设计系统的时候,为了系统的高扩展性,会尽可能的创建无状态的系统,这样我们就可以采用集群的方式部署,最终很方便的根据需要动态增减服务器数量。但是,要使系......
  • 小程序 canvas(老写法) 图片切图(一张图片切成3*3 、6*6 、长宽均等)
    <viewclass='sudoku'><viewclass='canvas-box'><canvascanvas-id='canvasIn'id='canvas'class='canvascanvas-in'style='{{canvasWH}}'wx:if='{{canvasI......
  • 创建好的小程序如何正式发布?
    前置准备:一个待发布的小程序。具体步骤:Editor中点击Publish创建新的试用小程序已有试用小程序轮转绑定进入UserCMS完成认证个人认证企业认证完善小程序信息提交审核......
  • 如何使用 Towify 在小程序中创建关联表?
    前置准备:一张需要被关联的表格具体步骤(5):添加关联字段选择被关联表双击关联字段格选择被关联数据关联成功步骤分解:添加关联字段选择被关联表双击关联字段格选择被关联数......
  • Linux下查看文件占用的空间及程序占用的内存
    1.查看目前磁盘空间和使用情况df-h2.查看当前路径下每个文件夹的大小du-sh*3.通过 top 命令动态查看内存占用top4. 查看内存占用前10名的程序psau......