吃透Spring源码(二十三):事物传播特性

一,上篇源码总结

关于异常回滚:

  1. 如果有保存点,回滚到保存点。
  2. 否则,如果当前是一个新事物(transaction != null && newTransaction==true),直接进行回滚。
  3. 否则,设置全局回滚标记,如果既没有保存点,又不是新的事务,如果可以设置全局的回滚标记的话,就会设置。

关于事物提交:

  1. 如果在事务链中已经被标记回滚,那么不会尝试提交事务,直接回滚
  2. 如果设置了全局回滚,则进行全局回滚
  3. 如果是新事务(transaction != null && newTransaction==true),则直接提交
  4. 如果不是新事务不会提交,要等外层是新事务才提交

二,事物传播特性

  • REQUIRED:如果有事物在运行,当前的方法就在这个事物内运行,否则,就启动一个新的事物,并在自己的事物内运行。
  • REQUIRED_NEW:当前方法必须启动新事物,并在自己的事物内运行。如果有事物正在运行,应该将它挂起。
  • SUPPORTS:如果有事物在运行,当前方法就在这个事物内运行,否则,它就不用事物。
  • NOT_SUPPORTS:当前的方法不应该运行在事物中,如果有事物正在运行,就将它挂起。
  • MANDATORY:当前方法必须运行在事物内部,如果没有正在运行的事物,就抛出异常。
  • NEVER:当前方法不应该运行在事物中,如果有事物正在运行,就抛出异常
  • NESTED:如果有事物在运行,当前的方法就应该在这个事物的嵌套事物内运行,否则,就启动一个新的事物,并在它自己的事物内运行。

在这里插入图片描述

三,传播特性测试

public class BookService {
    
    

    @Autowired
    BookDao bookDao;

    public BookDao getBookDao() {
    
    
        return bookDao;
    }

    public void setBookDao(BookDao bookDao) {
    
    
        this.bookDao = bookDao;
    }

    /**
     * 结账:传入哪个用户买了哪本书
     * @param username
     * @param id
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public void checkout(String username,int id){
    
    
        // 减库存
        bookDao.updateStock(id);
    }
}
public class BookDao {
    
    

    @Autowired
    JdbcTemplate jdbcTemplate;

    public JdbcTemplate getJdbcTemplate() {
    
    
        return jdbcTemplate;
    }

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
    
    
        this.jdbcTemplate = jdbcTemplate;
    }

    /**
     * 减库存,减去某本书的库存
     * @param id
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void updateStock(int id){
    
    
        String sql = "update book_stock set stock=stock-1 where id=?";
        jdbcTemplate.update(sql,id);
    }
}

1,外层方法有事务

当外层方法传播特性为REQUIREDREQUIRED NEWNESTED时,外层必须创建事物!

那么内层方法事务传播特性为如下情况时:

  • MANDATORY,REQUIRED,SUPPORTS:如果程序正常执行,那么内层事务不会提交,在外部事务中统一进行事务提交,如果内层事务,或者外层事务中出现异常情况,那么会在外层事务的处理中统一进行异常回滚。
  • NEVER:外层方法不能出现事务,如果出现事务则直接报错。
  • NOT SUPPORTED:外层方法中有事务,直接挂起,内层方法没有异常情况的话直接顺利执行,如果内层方法有异常的话,那么内层方法中已经执行的数据库操作不会触发回滚,而外层方法的事务会进行回滚操作,同样,如果外层方法中出现了异常操作,那么内部方法是不会回滚的,只有外层事务才会回滚。
  • REQUIRED NEW:如果外层方法中存在事务,内层方法在运行的时候会挂起外层事务并开启一个新的事务,如果程序正常执行,则内层方法优先事务提交,然后外层方法再提交;如果内层方法中存在异常,内层事务会优先回滚,外层方法事务也会回滚,如果外层方法中存在异常,那么内层事务正常正常提交,而外层方法会进行回滚操作。
  • NESTED:如果外层方法中有事务,那么直接创建一个保存点,后续操作中如果没有异常情况,那么会清除保存点信息,并且在外层事务中进行提交操作,如果内层方法中存在异常情况,那么会回滚到保存点,外层方法事务会直接进行回滚,如果外层方法中存在异常情况,那么会内层方法会正常执行,并且执行完毕之后释放保存点,并且外层方法事务会进行回滚。

2,外层方法没有事物

当外层方法传播特性为SUPPORTSNEVERNOT_SUPPORTS时,外层没有事物!

那么内层方法事务传播特性为如下情况时:

  • MANDATORY:外层方法中如果不包含事务的话,那么内层方法在获取事务对象的时候直接报错,而外层方法中不包含事务,所以无需回滚。
  • REQUIRED,REQUIRED NEW,NESTED:外层方法中不包含事务,所以内层方法会新建一个事务,如果程序正常执行,那么事务会正常提交,如果内层方法中出现异常,则内层方法事务正常回滚,而外层事务不做任何处理,如果外层方法中出现异常,则内层方法事务正常提交,外层方法抛出异常。
  • SUPPORTS,NEVER,NOT SUPPORTED:内外层方法都不包含事务的话,会以无事务的方法开始运行,每个数据库操作直接执行即可,如果出现异常情况,则后续的操作不会执行,但已经执行过的数据库操作不受任何影响

3,总结

  • MANDATORY不可以作为外层事务。
  • REQUIRED和NESTED回滚的区别:在回答两种方式区别的时候,最大的问题在于保存点的设置,其实在外层方法对内层方法的异常情况在进行捕获的时候区别很大,两者报的异常信息都不同,使用REQUIRED的时候,会报Transaction rolled back because it has been marked as rollback-only信息,因为内部异常了,设置了回滚标记,外部捕获之后,要进行事务的提交,此时发现有回滚标记,那么意味着要回滚,所以会报异常,而NESTED不会发生这种情况,因为在回滚的时候把回滚标记清除了,外部捕获异常后去提交,没发现回滚标记,就可以正常提交了。
  • REQUIRED_NEW和NESTED区别:这两种方式产生的效果是一样的,但是REQUIRED_NEW会有新的连接生成,而NESTED使用的是当前事务的连接,而且NESTED还可以回滚到保存点,REQUIRED_NEW每次都是一个新的事务,没有办法控制其他事务的回滚,但NESTED其实是一个事务,外层事务可以控制内层事务的回滚,内层就算没有异常,外层出现异常,也可以全部回滚。

猜你喜欢

转载自blog.csdn.net/u013277209/article/details/115022692