zl程序教程

您现在的位置是:首页 >  其他

当前栏目

@Transactional 注解rollbackFor 范围问题以及同一个类方法之间调用事务问题

2023-03-14 22:39:59 时间

spring Transactional 注解rollbackFor 范围问题

我们首先看下面一组代码

代码1

    @Transactional
    public String testInnerInvodeTransactional(Integer id) throws Exception {
 
 
 
        testMapper.deleteById(id);
        if(true){
            throw new RuntimeException("测试异常");
        }
        return "SUCCESS";
 
    }

代码2 

    @Transactional
    public String testInnerInvodeTransactional(Integer id) throws Exception {
 
 
 
        testMapper.deleteById(id);
        if(true){
            throw new Exception("测试异常");
        }
        return "SUCCESS";
 
    }

原数据库数据如下

image

代码1 执行结果

image

代码2 执行结果

image


两个代码主要区别是抛出异常一个是RuntimeException ,一个是Exception,可以看出代码1回滚了而代码2没有回滚,为什么呢

spring Transactional 的rollbackFor 原理

spring Transactional 的rollbackFor 原理

spring Transactional 默认的rollbackFor 范围是UncheckExcetion ,也就是RuntimeException和Error

代码1 我们抛出的是RuntimeException在默认范围内

代码2 我们抛出的是Exception 属于父类excetion 并不是默认范围,因此没有回滚

一定要设置spring Transactional 的rollbackFor 范围

比如可以直接设置为Exception.class,同时进来设置好传播范围,参考如下设置:

 解决方案:

@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)

spring 同一个类方法之间调用事务问题

我们看下面一组代码

代码1

 /**
     * 外层方法处理数据
     * */
    public String testInnerInvodeTransactional(Integer id) throws Exception {
 
        //内层方法处理数据
        innerInvodeTransactional(id);
        return "SUCCESS";
    }
 
    @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
    public void innerInvodeTransactional(Integer id) {
        testMapper.deleteById(id);
        if(true){
            throw new RuntimeException("测试异常");
        }
    }

代码2

 /**
     * 外层方法处理数据
     * */
    @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
    public String testInnerInvodeTransactional(Integer id) throws Exception {
 
        //内层方法处理数据
        innerInvodeTransactional(id);
        return "SUCCESS";
    }
 
    public void innerInvodeTransactional(Integer id) {
        testMapper.deleteById(id);
        if(true){
            throw new RuntimeException("测试异常");
        }
    }

原数据库数据如下



代码1执行结果



代码2执行结果 


image

上面两个方法的主要区别在于Transactional放在了入口类还是被调用类, 可以看出代码1没有回滚,而代码2回滚了,为什么呢

spring Transactional 原理

spring 会扫描带@Transactional 的方法,然后形成aop代理,执行以下流程

1.代理类会设置事务为手动提交

2.try ,catch 住要执行的方法

3.如果抛出异常,则执行catch 内的回滚代码

4.如果正常执行,则finally 中提交事务

解决方案: 

由以上可以内部方法调用问题主要在于否执行时执行的是aop代理类

如果是代理类,则会执行事务回滚

如果不是代理类,则不会执行事务回滚

所以入口的方法一定要加上事务注解,不然不会生成aop代理也就不会执行回滚操作