Spring事务传播及属性REQUIRED和REQUIRES_NEW的区别详解

Spring事务传播

本文详解Spring事务传播行为, 如下为@Transactional注解说明

讲事务传播之前, 需要先会用@Transactional注解

  1. 作用位置: 业务层接口上方,或者实现类上方,或者具体业务方法上方

  2. 作用: 为当前的业务方法添加事务支持

  3. @Transactional注解属性

    • 名称作用
      readOnly表示只读,没有写操作。可以通过这个属性告诉数据库我们没有写操作,从而数据库可以针对只读sql做优化操作
      timeout事务再执行的时候,由于某些原因卡住,长时间占用数据库资源。此时很可能程序sql有问题,希望撤销事务,能够让事务结束,释放资源,即超时回滚
      rollbackFor&rollbackForClassName回滚策略,希望对于什么样的异常回滚, 注意并不是所有的异常 Spring 都会回滚,Spring 只对 Error 异常和 RuntimeException 异常回滚
      noRollbackFor&noRollbackForClassName属性值为某个异常, 代表出现这个异常不回滚
      isolation设置事务隔离级别
      propagation事务传播级别

正文开始

首先开始前, 先说明一下:

  1. 外围方法

    外围方法可以理解为外层方法, 比如outerMethod为外围方法, innerMethod为内层方法

    public void outerMethod(){
        innerMethod();
    }
    
  2. 内层方法

    外围方法中调用的方法, 如上所述的innerMethod方法

事务传播行为propagation详解

传播属性说明
REQUIRED外围方法会开启新事务,内部方法会加入到外部方法的事务中(@Transactional注解默认为REQUIRED)
SUPPORTS外围方法没有事务,则内部方法不执行事务
MANDATORY使用当前事务,如果当前没有事务就抛异常
REQUIRES_NEW新建事务,如果当前存在事务,把当前事务挂起。其实就是REQUIRES_NEW会开启新事务, 外围方法抛异常不会影响
NOT_SUPPORTED不支持事务
NEVER不支持事务,如果存在事务还会抛异常
NESTED如果当前存在事务,则在嵌套事务内执行,如果不存在,执行REQUIRED类似操作

本文主讲REQUIREDREQUIRES_NEW的区别:

  1. REQUIRED属性加到外围方法上, 该外围方法中的内部方法的事务会加入到外围方法的事务, 也就是该外围方法中持久层操作, 与内部方法共用一个事务, 无论是内部方法抛出异常还是外围方法抛出异常, 事务都会回滚~
  2. REQUIRES_NEW属性加到内部方法且外围方法也加@Transactional且传播属性为默认(REQUIRED), 内部方法会将外围方法的事务挂起, 自己重新开启一个单独的新事务, 执行并提交之后, 再执行外围方法的事务

举例实现

  1. transactionTakesEffect方法加注解@Transactional(propagation = Propagation.REQUIRED),
    out方法加@Transactional(propagation = Propagation.REQUIRES_NEW)
    in方法加注解@Transactional(propagation = Propagation.REQUIRED)

    结果: transactionTakesEffect方法的事务不会影响out方法的事务, 也就是出现异常transactionTakesEffect出现异常in方法会回滚而out方法不会回滚


@Service("employeeService")
public class EmployeeServiceImpl implements IEmployeeService {

    @Autowired
    private EmployeeMapper employeeMapper;

    @Autowired
    private OtherService otherService;

    @Override
    @Transactional(rollbackFor = RuntimeException.class, propagation = Propagation.REQUIRED)
    public void transactionTakesEffect(Long srcId, Long deskId, long money) {
        /**转账扣钱
         * 由于使用事务传播方式为REQUIRES_NEW, 所以是挂起transactionTakesEffect方法创建的事务,
         * 自己新建一个事务, 所以transactionTakesEffect出现异常不会回滚扣钱的操作
        */
        otherService.out(srcId,money);
        /**接收转账收钱
         * 由于使用事务传播方式为REQUIRES, 所以是加入transactionTakesEffect方法创建的事务,
         * 所以transactionTakesEffect出现异常会回滚加钱的操作
         */
        otherService.in(deskId,money);
        //出现异常
        System.out.println(1/0);
    }
}

@Component
public class OtherService {

    @Autowired
    private EmployeeMapper employeeMapper;

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void out(Long srcId, long money){
        //转账扣钱
        employeeMapper.outAccount(srcId,money);
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void in(Long srcId, long money){
        //转账扣钱
        employeeMapper.inAccount(srcId,money);
    }
}
  1. 外围方法加注解@Transactional(propagation = Propagation.REQUIRED), 两个内部方法都不加事务注解

    结果: outNo方法和inNo方法会加入到外围方法transferRequired的事务中, 不论是哪个内部方法或者外围方法出现异常, 所有方法皆会回滚

@Service("employeeService")
public class EmployeeServiceImpl implements IEmployeeService {

    @Autowired
    private EmployeeMapper employeeMapper;

    @Autowired
    private OtherService otherService;

    @Override
    @Transactional(rollbackFor = RuntimeException.class, propagation = Propagation.REQUIRED)
    public void transactionTakesEffectAndTwoMethodInsert(Long srcId, Long deskId, long money) {
        /**转账扣钱
         */
        otherService.outNo(srcId,money);
        /**接收转账收钱
         */
        otherService.inNo(deskId,money);
        //出现异常
        System.out.println(1/0);
    }
}

@Component
public class OtherService {

    @Autowired
    private EmployeeMapper employeeMapper;
    

    public void outNo(Long srcId, long money){
        //转账扣钱
        employeeMapper.outAccount(srcId,money);
    }
    
    public void inNo(Long srcId, long money){
        //转账扣钱
        employeeMapper.inAccount(srcId,money);
    }
}

举例总结

抛出问题

在 Spring 中,如果 a 方法没有事务,而 b 方法有事务,那么 a 调用 b 时,事务情况是怎样的?

解答问题

  1. 首先事务是基于aop的, 所以说如果a方法和b方法处于同一个类中, 则属于内部调用, 而内部调用aop是不生效的, 显然事务不会生效
  2. 如果b方法是注入进来的, 那么b方法事务会正常生效, 但是只能是b方法中发生异常时生效, 而在a方法发生异常的时候已经执行过b方法了, b方法事务也不会回滚

感谢观看, 有问题可以留言, 看到会回复, 如有不对之处请大佬指教!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值