Propagation.NESTED 和Propagation.REQUIRES_NEW的区别
首先感谢这个微博的博主,解决了我的疑惑。https://www.cnblogs.com/-flq/p/9768576.html
说到spring的事务传播行为,最难理解的不过是Propagation.REQUIRES_NEW和Propagation.NESTED了。上网搜了一下大家的描述,我自己总结下就是:
Propagation.NESTED:如果当前存在事务,则开启一个嵌套事务,如果当前不存在事务,则新建一个事务并运行。
Propagation.REQUIRES_NEW:如果当前存在事务,则挂起当前事务,开启一个新的事务,新事务提交后,则继续运行外部事务。
道理大概弄清了,那怎么证明自己真的明白了呢?还是写几行代码验证以下吧。
两者都是嵌套在别的事务里运行的,那好写两个方法验证一番:
@Service
public class PropagationTestServiceImpl implements PropagationTestService{
@Autowired
public ItemDao itemDao;
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public void outterFunc() {
innerFunc();
ItemModel itemModel = new ItemModel();
itemModel.setCount(11);
itemModel.setName("outterFunc");
itemModel.setTime(new Date());
itemDao.insert(itemModel);
System.out.println("外部函数执行过");
//throw new RuntimeException();
}
@Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
public void innerFunc() {
ItemModel itemModel = new ItemModel();
itemModel.setCount(11);
itemModel.setName("innerFunc");
itemModel.setTime(new Date());
itemDao.insert(itemModel);
System.out.println("内部函数执行过");
//throw new RuntimeException();
}
}
再写个测试单元,每次只要执行这个单元测试就行了
@RunWith(SpringRunner.class)
@SpringBootTest
public class PropagationTest {
@Autowired
public PropagationTestService propagationTestService;
@Test
public void test() {
propagationTestService.outterFunc();
}
}
如果插入语句没写错的话,数据库会多出两条数据。
确定测试思路
主要是看看外部函数抛出异常,会不会让嵌套的事务也触发数据库回滚,再看看内部函数抛出异常会不会让外部事务触发数据库回滚。
因为都是两层嵌套事务,我们保持外层的propagation = Propagation.REQUIRED不变做四次测试。(为了让测试结果清晰,每次开始测试前我都会把数据库清空)
-
设置innerFunc的propagation = Propagation.REQUIRES_NEW,在outterFunc函数的最后加上
throw new RuntimeException();
-
设置innerFunc的propagation = Propagation.REQUIRES_NEW,在innerFunc函数的最后加上
throw new RuntimeException();
-
设置innerFunc的propagation = Propagation.NESTED ,在outterFunc函数的最后加上
throw new RuntimeException();
-
设置innerFunc的propagation = Propagation.NESTED ,在innerFunc函数的最后加上
throw new RuntimeException();
测试结果还是让我有点懵逼的,所有测试结果数据库都发生了回滚,数据库一条数据也没怎加,那Propagation.REQUIRES_NEW和Propagation.NESTED岂不是相等。于是带着疑问上网查了半天才发现所有测试结果都相同的罪魁祸首是我把两个函数都写在了同一个类里,先不求原理,我把两个方法放到两个service里再测试一次。
大体代码改成如下:
PropagationTestServiceImpl.java
@Service
public class PropagationTestServiceImpl implements PropagationTestService{
@Autowired
public ItemDao itemDao;
@Autowired
public PropagationTestService2 propagationTestService2;
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public void outterFunc() {
propagationTestService2.innerFunc();
ItemModel itemModel = new ItemModel();
itemModel.setCount(11);
itemModel.setName("outterFunc");
itemModel.setTime(new Date());
itemDao.insert(itemModel);
System.out.println("外部函数执行过");
//throw new RuntimeException();
}
}
PropagationTestService2Impl.java
@Service
public class PropagationTestService2Impl implements PropagationTestService2{
@Autowired
public ItemDao itemDao;
@Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
public void innerFunc() {
ItemModel itemModel = new ItemModel();
itemModel.setCount(11);
itemModel.setName("innerFunc");
itemModel.setTime(new Date());
itemDao.insert(itemModel);
System.out.println("内部函数执行过");
//throw new RuntimeException();
}
}
测试2、3、4数据库都是一条数据都没有怎加,说明内外的事务都回滚了。
测试1的结果如下:
很明显外部事务发生异常,并没有导致内部事务发生回滚。
总结
- 要想让事务嵌套起作用必须不能发在同一个类中
- 无论是Propagation.REQUIRES_NEW,还是Propagation.NESTED,只要是内部事务抛出异常数据库都会回滚
- 区别在于Propagation.REQUIRES_NEW是一个全新开启的事务,即使外部事务抛出异常发生数据库回滚,也不影响内部事务的提交。而Propagation.NESTED则更像是一个事务的子事务,受外部事务的影响。
- 什么时候用到Propagation.REQUIRES_NEW呢?举个例子,像是在生成订单的业务里,往往生成订单号的逻辑会单独分离出来,这个时候就可以给这个生成订单号的函数声明propagation=Propagation.REQUIRES_NEW,因为这样即使生成订单的过程出现错误也没必要让生成订单号相关的数据库回滚,生成就生成了,数据库回滚是浪费资源的。