一,上篇源码总结
关于异常回滚:
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,外层方法有事务
当外层方法传播特性为REQUIRED
,REQUIRED NEW
,NESTED
时,外层必须创建事物!
那么内层方法事务传播特性为如下情况时:
- MANDATORY,REQUIRED,SUPPORTS:如果程序正常执行,那么内层事务不会提交,在外部事务中统一进行事务提交,如果内层事务,或者外层事务中出现异常情况,那么会在外层事务的处理中统一进行异常回滚。
- NEVER:外层方法不能出现事务,如果出现事务则直接报错。
- NOT SUPPORTED:外层方法中有事务,直接挂起,内层方法没有异常情况的话直接顺利执行,如果内层方法有异常的话,那么内层方法中已经执行的数据库操作不会触发回滚,而外层方法的事务会进行回滚操作,同样,如果外层方法中出现了异常操作,那么内部方法是不会回滚的,只有外层事务才会回滚。
- REQUIRED NEW:如果外层方法中存在事务,内层方法在运行的时候会挂起外层事务并开启一个新的事务,如果程序正常执行,则内层方法优先事务提交,然后外层方法再提交;如果内层方法中存在异常,内层事务会优先回滚,外层方法事务也会回滚,如果外层方法中存在异常,那么内层事务正常正常提交,而外层方法会进行回滚操作。
- NESTED:如果外层方法中有事务,那么直接创建一个保存点,后续操作中如果没有异常情况,那么会清除保存点信息,并且在外层事务中进行提交操作,如果内层方法中存在异常情况,那么会回滚到保存点,外层方法事务会直接进行回滚,如果外层方法中存在异常情况,那么会内层方法会正常执行,并且执行完毕之后释放保存点,并且外层方法事务会进行回滚。
2,外层方法没有事物
当外层方法传播特性为SUPPORTS
,NEVER
,NOT_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其实是一个事务,外层事务可以控制内层事务的回滚,内层就算没有异常,外层出现异常,也可以全部回滚。