前言
Spring在TransactionDefinition接口中规定了7种类型的事务传播行为。事务传播行为是Spring框架独有的事务增强特性,他不属于的事务实际提供方数据库行为。这是Spring为我们提供的强大的工具箱,使用事务传播行可以为我们的开发工作提供许多便利。但是人们对他的误解也颇多,你一定也听过“service方法事务最好不要嵌套”的传言。要想正确的使用工具首先需要了解工具。
基础概念
1. 什么是事务传播行为?
事务传播行为用来描述由某一个事务传播行为修饰的方法被嵌套进另一个方法的时事务如何传播。
用伪代码说明:
1 @Transaction(Propagation=XXX) 2 public void methodA(){ 3 methodB(); 4 //doSomething 5 } 6 7 public void methodB(){ 8 //doSomething 9 }
methodA中存在事务,他又调用了methodB。methodB事物的一些特性由methodA决定,这就是事务的传播行为。
2. Spring中七种事务传播行为
事务传播行为类型 | 说明 |
---|---|
PROPAGATION_REQUIRED | 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。 |
PROPAGATION_SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行。 |
PROPAGATION_MANDATORY | 使用当前的事务,如果当前没有事务,就抛出异常。 |
PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起。 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。 |
定义非常简单,也很好理解,下面我们就进入代码测试部分,验证我们的理解是否正确。
代码验证
第一种情况。内部均为 propagation = Propagation.REQUIRED
1 @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.REPEATABLE_READ) 2 @Override 3 public void testTransactional() { 4 int insert = downloadImgDao.test1(); 5 log.info("insert1 = {}", insert); 6 7 insert = downloadImgDao.test2(); 8 log.info("insert2 = {}", insert); 9 } 10 11 @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.REPEATABLE_READ) 12 @Override 13 public int test1() { 14 DownloadImg downloadImg = new DownloadImg(); 15 downloadImg.setId(666L); 16 downloadImg.setLink("张三"); 17 downloadImg.setLinkname("16"); 18 int res = downloadImgMapper.insertSelective(downloadImg); 19 log.info("res1 = {}", res); 20 return res; 21 } 22 23 24 @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.REPEATABLE_READ) 25 @Override 26 public int test2() { 27 DownloadImg downloadImg = new DownloadImg(); 28 downloadImg.setId(888L); 29 downloadImg.setLink("李四"); 30 downloadImg.setLinkname("18"); 31 int res = downloadImgMapper.insertSelective(downloadImg); 32 log.info("res2 = {}", res); 33 34 int i = 5 / 0; 35 return res; 36 }
张三,李四插入均失败。
第二种情况。内部一种为 propagation = Propagation.REQUIRED,一种为Propagation.REQUIRES_NEW
1 @Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.REPEATABLE_READ) 2 @Override 3 public int test1() { 4 DownloadImg downloadImg = new DownloadImg(); 5 downloadImg.setId(666L); 6 downloadImg.setLink("张三"); 7 downloadImg.setLinkname("16"); 8 int res = downloadImgMapper.insertSelective(downloadImg); 9 log.info("res1 = {}", res); 10 return res; 11 } 12 13 14 @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.REPEATABLE_READ) 15 @Override 16 public int test2() { 17 DownloadImg downloadImg = new DownloadImg(); 18 downloadImg.setId(888L); 19 downloadImg.setLink("李四"); 20 downloadImg.setLinkname("18"); 21 int res = downloadImgMapper.insertSelective(downloadImg); 22 log.info("res2 = {}", res); 23 24 int i = 5 / 0; 25 return res; 26 }
张三插入成功,李四插入失败。
第三种情况。内部均为Propagation.REQUIRES_NEW
跟我们现象的是一样的,如果哪个test异常,哪个就失败,无异常的就成功。
还有一种情况是,外围抛异常了,内部都不抛异常,两种内部插入也都会成功。
结论
本程序是实验了PROPAGATION_REQUIRED以及Propagation.REQUIRES_NEW。
事务默认以PROPAGATION_REQUIRED来隔离。
1: 如果内部是PROPAGATION_REQUIRED隔离级别,内部只要一个方法出错,那么整个事务都会回滚。
2: 如果内部有方法以Propagation.REQUIRES_NEW来隔离。那么他会创建一个新的事务来运行,如果他抛异常了,并不会影响其他事务的以及外部的事务。
spring事务官方文档:https://docs.spring.io/spring/docs/5.2.6.RELEASE/spring-framework-reference/data-access.html#tx-propagation