Spring中的事务失效问题
一、@Transactional 注解失效
- @Transactional 作用在非 public 修饰的方法上,虽然不报错,但是会导致失效。
@Transactional注解使用的是AOP,在使用动态代理的时候只能针对public方法进行代理,源码依据在AbstractFallbackTransactionAttributeSource类中的computeTransactionAttribute方法。
protected TransactionAttribute computeTransactionAttribute(Method method,
Class<?> targetClass) {
// 不允许非 public 创建事务
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;
}
-
使用MySQL,存储引擎选择MyISAM,则事务会不起作用,因为MyISAM不支持事务,可以改成InnoDB引擎。
-
方法内部调用同类的方法,即在类A里面有方法a和方法b,然后方法b上面用 @Transactional加了方法级别的事务,在方法a里面 调用了方法b, 方法b里面的事务不会生效。
在应用系统调用声明@Transactional 的目标方法时,Spring Framework 默认使用 AOP 代理,在代码运行时生成一个代理对象,当有注解的方法被调用的时候,实际上是代理类调用的,代理类在调用之前会开启事务,执行事务的操作。
当在实现类中直接调用内部方法时,其本质是通过this对象来调用的方法,而不是代理对象,所以会出现事务失效的情况。
public class Test{
@Transactional
public void A(){
//插入一条数据
//调用B方法
B();
}
@Transactional
public void B(){
//插入数据
}
}
- 如果使用Spring + Spring MVC,则context:component-scan重复扫描问题可能会引起事务失效。
- 如果Spring 和 Spring MVC的配置文件中都在扫描Service层,会先加载Spring MVC配置文件,再加载Spring配置文件。
- 事物一般都在Spring配置文件中进行配置。
- 如果此时在加载Spring MVC配置文件的时候,把Service也给注册了,但是此时事物还没加载,也就导致后面的事物无法成功注入到Service中。
- 最好在具体的类 / 类的方法上使用 @Transactional 注解,而不要使用在类实现的任何接口上使用。在接口上使用 @Transactional 注解,只能当设置了基于接口的代理时它才生效。即@Transactional 作用于接口,却使用 CGLIB 动态代理会导致事务失效。
因为注解是不能继承的,这就意味着如果正在使用基于类的代理时,事务的设置将不能被基于类的代理所识别,而且对象也将不会被事务代理所包装。
-
@Transactional 方法内异常被捕获,而catch语句块没有throw new RuntimeExecption异常,事务不会回滚。
-
默认 RuntimeException 和 Error 及子类抛出会回滚;对于rollbackFor,只有指定的异常及子类才会发生回滚。
-
@Transactional 注解属性 propagation 设置以下三种可能导致无法回滚:
- SUPPORTS
- NOT_SUPPORTED
- NEVER