一、Spring 事务的传播行为
所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。在 TransactionDefinition 定义中包括了如下几个表示传播行为的常量:
1.TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。默认值。
2.TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。
3.TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
4.TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
5.TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
6.TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
7.TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。
二、回滚机制
1. Spring 的 AOP 即声明式事务管理默认是针对 unchecked exception(RuntimeException) 回滚。
2. Spring 的事务边界是在调用业务方法之前开始的,业务方法执行完毕之后来执行 commit or rollback(Spring 默认取决于是否抛出 runtimeException)。
3. 如果你在方法中有 try{}catch(Exception e){} 处理,那么 try 里面的代码块就脱离了事务的管理,若要事务生效需要在 catch 中 throw new RuntimeException ("xxxxxx")。
三、底层实现
事物调用图如下:

四、事务失效常见场景
1. 示例:事务方法访问修饰符非public,导致事务失效
解决:将方法修饰符改为public;开启AspectJ代理模式
注意:如果事务是static、final的,同样无法通过动态代理,事务也是不会生效的。Spring的声明式事务是基于动态代理实现的,我们无法重写final修饰的方法;不管是JDK动态代理还是Cglib的动态代理,就是要通过代理的方式获取到代理的具体对象,而static方法修饰的方法是属于类的,不属于任何对象,所以static方法不能被重写,即便写法上是重写,但是并不具备重写的含义,也就是说static方法也不被进行动态代理。
2. 示例:@Transactional注解的方法抛出的异常不是spring的事务支持的异常,导致事务失效
解决:修改为@Transactional(rollbackFor = Exception.class)或抛出throw new RuntimeException ("");
注意:spring的事务只支持未检查异常(unchecked),不支持已检查异常(checked)。
3. 示例:数据表本身是不支持事务,导致事务失效
解决:如果使用MySQL且存储引擎是MyISAM,则事务是不起作用的,原因是MyIASM不支持事务。
解决:数据表可以改为InnoDB存储引擎,支持事务
4. 示例:@Transactional注解所在的类没有被spring管理,导致事务失效
解决:加上@Service注解或者使用其他能注册成Spring Bean的方式或注解。
5. 示例:catch掉异常之后,没有再次抛出异常,导致事务失效
解决:在catch中抛出异常
注意:如果在加有事务的方法内,使用了try…catch…语句块对异常进行了捕获,而catch语句块没有throw new RuntimeException异常或者Spring支持的异常类型,则事务不会回滚。
6. 示例:非事务方法自身(this)事务问题,导致事务失效
解决:自己注入自己,用注入实例调用;获取代理类,利用代理类(AopContext.currentProxy ())调用自己的类方法;拆分。
注意:由于@Transactional的实现原理是AOP,aop是动态代理,自己调用自己,并不存在代理对象的调用
7. 示例:数据源没有配置事务管理器,导致事务失效
8. 示例:传播类型不支持事务,导致事务失效
9. 示例:多线程调用,导致事务失效
注意:两个方法不在同一个线程,数据库连接不一样,从而有不同的事务。
本篇文章来源于微信公众号: 方片四
微信扫描下方的二维码阅读本文

Comments NOTHING