Spring事务不回滚,原理分析以及解决方案

转载 的地址 ---》》》》》https://blog.csdn.net/chl191623691/article/details/79385713
最近在做的项目和之前有点不一样,以前我们可能经常做后端的一直从Controller写到最终的持久层,当然这样的写法我也写了很久,中间转用restful风格,但是这种形式依然没有转变,这次项目结构和之前有所调整,所以在写着写着到最后自测的时候发现存在事务不会滚的情况,当时我就纳闷了,起初自我检查怀疑是我事务配置的有问题,但是最终发现我的配置是没有什么问题的(但是说有问题也是存在的,那就是切面的问题),我在项目中配置的时候采用的是声明式事物,正常来说我们对这个事务的认知优缺点是存在的!优点:使用方便,一次配置就可以了;缺点:事务的粒度比较大,只能到方法级别!好了,下面说正题:我们在做事物配置的时候一直都会紧记的就是Spring的事务管理默认只对出现运行期异常(java.lang.RuntimeException及其子类)进行回滚;如果一个方法抛出Exception或者Checked异常,Spring事务管理默认不进行回滚!我的坑就在这个地方,之前都是遇到Exception就直接往外丢,然后直到Controller才会做try..catch操作,但是这次我们采用的dubbo的模式,所以很多地方就直接到Service就结束了,这里因为我们依然要留存日志等操作,所以为了打印清楚日志,很多方法我都加tyr catch,在catch中打印日志,但是这边情况来了,当这个方法异常时候日志是打印了,但是加的事务却没有回滚!

下面对集中代码结构留存以备查:

①下面的存在两个操作的方法,其中第二个操作异常了第一个操作的记录不会回滚,因为异常被捕获不会被spring拿到运行异常!


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19
public Message insert(Mp entity){
    try{
        mpdao.insert(entity);
        Mp  mptwo=new Mp();
        mptwo.setCreateTime(new Date());
        mptwo.setMpAlias("1234566");
        mptwo.setMpAppId("147852");
        mptwo.setMpAppSecret("14782239941");
        mptwo.setMpMchPaykey("14721313");
        mptwo.setMpMenu("sdfafadfsafasfadfsaf");
        mptwo.setMpMsgType("14123"); //这里的数据数据库字段是char(2),所以插入操作会报错
        mpdao.insert(mptwo);
        return Message.success();
    }catch(Exception e){
            logger.error(e.toString(),e);
            return Message.error();
    }
         
}
②下面的方法第二个操作执行错误第一个会回滚,因为主动抛出了一个运行异常!

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20
public Message insert(Mp entity){
    try{
        mpdao.insert(entity);
        Mp  mptwo=new Mp();
        mptwo.setCreateTime(new Date());
        mptwo.setMpAlias("1234566");
        mptwo.setMpAppId("147852");
        mptwo.setMpAppSecret("14782239941");
        mptwo.setMpMchPaykey("14721313");
        mptwo.setMpMenu("sdfafadfsafasfadfsaf");
        mptwo.setMpMsgType("14123");  //这里的数据数据库字段是char(2),所以插入操作会报错
        mpdao.insert(mptwo);
        return Message.success();
        }catch(Exception e){
            logger.error(e.toString(),e);
            throw new RuntimeException(); //主动抛出异常
            return Message.error();
        }
         
}
③下面的第二个操作执行错误第一个也会回滚,因为我们手动做了事物处理!


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20
public Message insert(Mp entity){
    try{
        mpdao.insert(entity);
        Mp  mptwo=new Mp();
        mptwo.setCreateTime(new Date());
        mptwo.setMpAlias("1234566");
        mptwo.setMpAppId("147852");
        mptwo.setMpAppSecret("14782239941");
        mptwo.setMpMchPaykey("14721313");
        mptwo.setMpMenu("sdfafadfsafasfadfsaf");
        mptwo.setMpMsgType("14123");  //这里的数据数据库字段是char(2),所以插入操作会报错
        mpdao.insert(mptwo);
        return Message.success();
    }catch(Exception e){
            logger.error(e.toString(),e);
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); //手动事务处理
            return Message.error();
    }
         
}
不知道你看到这里有没有明白我这篇文章的意图和想要表达的内容呢?下面我们来做一个总结:

①我们的声明式事物为什么不会滚呢??

a. 默认spring事务只在发生未被捕获的 runtimeexcetpion时才回滚;

b. spring aop  异常捕获原理:被拦截的方法需显式抛出异常,并不能经任何处理,这样aop代理才能捕获到方法的异常,才能进行回滚,默认情况下aop只捕获runtimeexception的异常,但可以通过配置来捕获特定的异常并回滚  !

②解决方案

a.  如果是service层处理事务,那么service中的方法中不做异常捕获,或者在catch语句中最后增加throw new RuntimeException()语句,以便让aop捕获异常再去回滚,并且在service上层(webservice客户端,view层action)要继续捕获这个异常并处理

b. 在service层方法的catch语句中增加:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();语句,手动回滚,这样上层就无需去处理异常(现在我的项目的做法)


这是一个真实的挖坑以及埋坑的经历,在这里记录下来希望可以对朋友们给出帮助,最后感谢你的翻阅,如有疑问可以留言讨论,谢谢!


这样搞定

   /**
    * 支付超时,改变订单状态
    * @param map
    * @return
    */
   @Override
   public int cancelOrder(Map map) {
       Integer count = 1;
       Integer orderId = Integer.parseInt(map.get("orderId").toString());
       Integer activityId = Integer.parseInt(map.get("activityId").toString());
       Integer signupCount = Integer.parseInt(map.get("signupCount").toString());
       int one = orderMapper.cancelOrder(orderId);
       int two = orderMapper.updateActivityCurrentNumber(signupCount,activityId);
/*       int i = 1/0;
       System.err.println(i);*/
       int three = orderMapper.deleteActivitySignUpRecords(orderId,0);
       if (one > 0 && two > 0 && three > 0) {
           return count;
       } else {
           TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); //手动事务处理
       }

       return 0;
   }

————————————————
版权声明:本文为CSDN博主「专注JavaWeb开发」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/chl191623691/article/details/79385713

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring事务回滚原理是基于ACID特性和@Transactional注解的机制。当在执行某个方法时,如果方法中有包含对数据库的插入或者更新操作,并且在当前事务上下文抛出了异常,Spring会对已经进行的数据库操作进行回滚,恢复到原来的状态。\[1\] 使用Spring事务管理功能后,Spring会在启动时解析生成相关的bean,并查看拥有@Transactional注解的类和方法。然后为这些类和方法生成代理,并根据@Transactional的相关参数使用动态代理进行相关配置注入。这样,在代理中就会自动处理事务(开启正常提交事务,异常回滚事务)。\[2\] 需要注意的是,@Transactional注解可以作用于接口、接口方法、类以及类方法上。当作用于类时,该类的所有public方法都将具有事务的特性。但是Spring建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时才会生效。另外,@Transactional注解只能应用到public方法上,作用于protected、private时,会被忽略,也不会抛出任何异常,这是由Spring AOP的本质决定的。\[3\] #### 引用[.reference_title] - *1* *3* [Spring 事务回滚原理和方法](https://blog.csdn.net/weixin_44201216/article/details/97397530)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [spring事务回滚机制,事务原理](https://blog.csdn.net/qq_45076180/article/details/106496486)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值