事务分为编程式事务 与 声明式事务 这里描述常用的声明式事务的原理。
@Transactional 实现机制:
当在方法上使用@Transactional 时,Spring Framework 默认使用 AOP 代理,在代码运行时生成一个代理对象,根据@Transactional 的属性配置信息,这个代理对象决定该声明@Transactional 的目标方法是否由拦截器 TransactionInterceptor 来使用拦截,在 TransactionInterceptor 拦截时,会在在目标方法开始执行之前创建并加入事务,并执行目标方法的逻辑, 最后根据执行情况是否出现异常决定提交或回滚事务。
@Transactional 中相关参数说
name :当在配置文件中有多个 TransactionManager , 可以用该属性指定选择哪个事务管理器。
propagation :事务的传播行为,默认值为 REQUIRED。
isolation :事务的隔离度,默认值采用 DEFAULT。
timeout :事务的超时时间,默认值为-1。如果超过该时间限制但事务还没有完成,则自动回滚事务。
read-only :指定事务是否为只读事务,默认false 为了忽略那些不需要事务的方法,如读取数据可以设置read-only为true。
rollback-for :用于指定能够触发事务回滚的异常类型,如果有多个异常类型需要指定,各类型之间可以通过逗号分隔。
no-rollback- for:抛出 no-rollback-for 指定的异常类型,不回滚事务。
(重点说明)propagation : 事务的传播行为
Propagation.REQUIRED(默认):如果当前存在事务,则加入该事务,如果当前不存在事务,则创建一个新的事务。
Propagation.SUPPORTS:如果当前存在事务,则加入该事务;如果当前不存在事务,则以非事务的方式继续运行。
Propagation.MANDATORY:如果当前存在事务,则加入该事务;如果当前不存在事务,则抛出异常。
Propagation.REQUIRES_NEW:重新创建一个新的事务,如果当前存在事务,暂停当前的事务。
Propagation.NOT_SUPPORTED:以非事务的方式运行,如果当前存在事务,暂停当前的事务。
Propagation.NEVER:以非事务的方式运行,如果当前存在事务,则抛出异常。
Propagation.NESTED: Propagation.REQUIRED 效果一样。
/* *测试用例: */ 例 1:默认事务 (propagation.REQUIRED) class B{ @Transactional public void funB(){ } } cladd A { @Transactional public void funA(){ new B().funB(); } } //结果为:当B失败,则funA/B同时回滚 //例2:(Propagation.REQUIRES_NEW) 创建新事物 //需求:funA如果抛异常不得影响funB的执行,使用新启事务 Propagation.REQUIRES_NEW @Transactional(propagation = Propagation.REQUIRES_NEW) public void funA(){ funB} //直接失效 //这里有个坑!funB必须写在另一个类中才生效! 在默认的代理模式下,只有目标方法由外部调用,才能被 Spring 的事务拦截器拦截。 //在同一个类中的两个方法直接调用,是不会被 Spring 的事务拦截器拦截,可以使用 AspectJ 取代 Spring AOP 代理来解决这个问题。 //例3:(Propagation.MANDATORY) 事务有则加入无则报错 class B{ @Transactional(propagation = Propagation.MANDATORY) public void funB(){ } } cladd A { public void funA(){ new B().funB(); } } //结果:将报错因为在调用funB的时候不存在事务。 //例4:(Propagation.SUPPORTS)事务有则加入,无则按非事务运行。 与例三相对 class B{ @Transactional(propagation = Propagation.SUPPORTS) public void funB(){ } } cladd A { public void funA(){ new B().funB(); } } //例5:Propagation.NOT_SUPPORTED 与 Propagation.NEVER 都是以非事务方式执行, //后者更狠只要有事务就报错,而前者只是暂停。
不会执行事务的场景:懂原理才能知道为什么不执行!!
为什么不启用事务的排查逻辑:
1、public:SpringAOP中涉及的核心类CglibAopProxy 或者 JdkDynamicAopProxy 无法获取@Transactional 的相关配置,底层使用反射读取配置信息,但该方法非Public 所以获取不到。
2、try/catch:核心类找到了你@Transactional 的配置,但你在方法里catch 掉了,所以事务拦截器拦截的时候抓不到异常导致无法回滚
3、同类中自调用:找到了,也没有catch.但还是没有回滚,看看是不是在同一个类中形成了自调用(同类中方法事务A调用事务方法B)这里可以参考IBM的这个博客
4、propagation:找到没也没catch也无自调用,检查Propagation 参数是否配置了不启用事务的参数
5、回滚异常检测:检查rollbackFor 以及 noRollbackFor 这里检查的异常是否有问题。
参考文章:
https://blog.csdn.net/nextyu/article/details/78669997 《Spring Boot 中使用 @Transactional 注解配置事务管理》
https://www.ibm.com/developerworks/cn/java/j-master-spring-transactional-use/index.html 《透彻的掌握 Spring 中@transactional 的使用》
《Spring框架参考文档》