事务传播行为
指通过@Transactional方式声明注解时,调用另一个带有事务注解的方法 (两个事务注解都生效),即嵌套事务的情况
propagation属性控制传播行为,默认值是Propagation.REQUIRED,一共有七种情况
注:以下所指的外层方法是调用者,内层方法指的是被调用的方法,这两个方法分别来自两个代理对象,事务注解都生效
propagation属性会加在内层方法的注解上,外层方法只有@Transactional
(默认)Propagation.REQUIRED: 外层如果有事务,内存就加入到该事务,外层没有事务内层就新增一个事务。
@Override @Transactional
//内层方法 public Integer saveUserInfo() { UserInfo userInfo = new UserInfo(); userInfo.setUserName("测试内层"); userInfo = userInfoRepository.save(userInfo); if (true) {
//内层方法抛出异常,事务回滚,外层内层两条数据都不会产生 throw new RuntimeException(); } return userInfo.getId(); } @Override @Transactional
//外层方法 public void test(UserInfo userInfoParam) { userInfoParam.setUserName("测试外层");
UserInfo userInfo = userInfoRepository.save(userInfoParam);
//调用内层方法 userInfoService.saveUserInfo(); }
注意点:
这种情况下,如果内层抛出异常,而外层捕获了异常没有抛出,会抛出:
org.hibernate.TransactionException: Transaction was marked for rollback only; cannot commit
@Override @Transactional //内层方法 public Integer saveUserInfo() { UserInfo userInfo = new UserInfo(); userInfo.setUserName("测试内层"); userInfo = userInfoRepository.save(userInfo); if (true) { throw new RuntimeException(); } return userInfo.getId(); } @Override @Transactional //外层方法 public void test(UserInfo userInfoParam) { userInfoParam.setUserName("测试外层"); UserInfo userInfo = userInfoRepository.save(userInfoParam); //调用内层方法 try {
//捕获内层抛出的异常 userInfoService.saveUserInfo(); } catch (Exception e) { e.printStackTrace(); } }
内层抛出异常后,就被标记为rollback,当外层尝试提交的事务的时候不再被允许
(常用)Propagation.REQUIRES_NEW: 创建一个新事务,如果外层有事务则将外层事务挂起,自己方法返回后事务提交完毕,外层事务继续
这种情况较为常用,能够将内层的事务独立出来。
内层事务提交后,外层事务如果再抛出异常,内层事务不会受影响而回滚。
注意点:
显然的,如果内层方法内抛出了异常会回滚,而外层方法也没有捕获内层抛出的异常,那么外层事务也会回滚。
当然,如果外层捕获了就不会受影响。
@Override @Transactional(propagation = Propagation.REQUIRES_NEW) //内层方法 public Integer saveUserInfo() throws RuntimeException { UserInfo userInfo = new UserInfo(); userInfo.setUserName("测试内层"); userInfo = userInfoRepository.save(userInfo); if (true) { //内层回滚 throw new RuntimeException(); } return userInfo.getId(); } @Override @Transactional //外层方法 public void test(UserInfo userInfoParam) { userInfoParam.setUserName("测试外层"); UserInfo userInfo = userInfoRepository.save(userInfoParam); //调用内层方法 try { //捕获内层异常,外层不会受影响 userInfoService.saveUserInfo(); } catch (RuntimeException e) { } }
其他不太常用的:
Propagation.SUPPORTS:外层有事务就加入,外层没有内层也就没有
Propagation.NOT_SUPPORTED:无事务,外层有事务则将外层事务挂起
Propagation.MANDATORY:外层必须有事务
调用者没有事务就会抛出异常:org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'
Propagation.NEVER:外层必须没有事务
调用者有事务就会抛出异常:org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never'
(Hibernate
不支持)PROPAGATION_NESTED:如果当前存在事务则在 嵌套事务 内执行
嵌套事务开始执行时会取得保存点savepoint,如果嵌套事务失败则回滚到此savepoint
嵌套事务随着外层事务提交而提交,外层事务回滚也会回滚