@Transactional默认捕获运行时异常, 那Error会回滚吗?
先说结论,会,为什么?直接看源码
首先,添加了事务注解的代码如下,打了断点,跟断点看代码
@Transactional
public void changeInfo(){
// TODO 事务默认回滚的异常
studentDao.updateAgeById(88,1);
int i = 1/0;
System.out.println("-----------");
}
这个方法肯定要被AOP代理的,然后外面会有一个环绕通知,try捕获目标方法抛出的异常,看代码, 打断点定位源码
可以验证,invocation代理的目标方法就是
changeInfo。因为StudentService类没有实现接口,所以采用的CGlib动态代理。
源码往下走,可以看到,先通过Throwable 顶级父类捕获 算数异常(运行时异常)
再把捕获的异常 传递给 completeTransactionAfterThrowing 方法处理回滚。
往下走,进入completeTransactionAfterThrowing。
内部有个
transactionAttribute属性调用rollbackOn方法进行回滚判断,返回一个bool值判断是否需要回滚。
进入
transactionAttribute.rollbackOn回滚判断方法。
transactionAttribute是一个事务属性接口,它有好几种是实现。
- RuleBasedTransactionAttribute
- DefaultTransactionAttribute
- DelegatingTransactionAttribute
最终会走到DefaultTransactionAttribute这个实现类的rollbackOn 方法进行判断。
最终找到答案,关键来了,如果是运行时异常 或者 Error本身或子类,那么rollBackOn方法返回True。
返回True之后,在外部completeTransactionAfterThrowing方法中,通过事务管理器执行回滚。
如果自己通过事务注解属性配置回滚异常为Exception,那么Error还会回滚吗?
先说结论,也会回滚
@Transactional(rollbackFor = Exception.class)
public void changeInfo(){
// TODO 事务默认回滚的异常
studentDao.updateAgeById(88,1);
throw new Error();
}
还是跟源码, 目标方法还是抛出了一个算数异常(运行时异常)
还是跟源码, 目标方法还是抛出了一个算数异常(运行时异常)
txInfo是TransactionInfo对象,TransactionInfo是一个内部类,用来保持当前事务的一些信息,保存什么信息?保存我们通过transactional注解配置的事务属性信息(例如事务传播行为,隔离级别,回滚异常,只读等)。
-
在这个例子中,我们配置的
@Transactional(rollbackFor = Exception.class),所以可以在txInfo中看到我们配置的事务属性信息。 -
可以看到,我们配置的回滚异常被保存在了
txInfo中的transactionAttribute中的targetAttribute中的rollBackRules这个集合中。
txInfo被传入了completeTransactionAfterThrowing这个方法,进行事务处理,先往下看。
跟之前一样,调用txInfo.transactionAttribute的rollbackOn方法判断是否需要回滚。
transactionAttribute是一个事务属性接口,它有好几种是实现。
- RuleBasedTransactionAttribute
- DefaultTransactionAttribute
- DelegatingTransactionAttribute
跟之前一样,先调用DelegatingTransactionAttribute的rollbackOn方法。
然后调用RuleBasedTransactionAttribute的rollbackOn方法。
看
RuleBasedTransactionAttribute的rollbackOn方法,非常有意思。
- 在这个方法中遍历了配置的回滚规则
rollBackRules,示例中只有一个是Exception.class。 - getDepth:获取配置的回滚规则 与 实际发生的异常的深度差(深度差是子类和父类的深度差) 不为父子关系则返回depth=-1.(这里我点进去源码看的,其实也就一个递归函数判断)
然后示例中目标方法内发生的异常是Error,也就是变量ex是Error,Error和Exception没有父子关系,最终depth=-1,所以不会进行if判断,所以winner == null。
- 所以还是会调用
super.rollbackOn方法。super就是DefaultTransactionAttribute,所以最终和之前一样还是会返回true,所以Error还是会回滚。