声明式事务在哪些情况下会失效?

声明式事务在哪些情况下会失效?
1、@Transactional 在非 public 修饰的方法上的应用
如果 @Transactional 注解应用在非 public 修饰的方法上,@Transactional 注解将会失效。
这是因为在 Spring AOP 的代理过程中,事务拦截器 TransactionInterceptor 在目标方法执行前后进行拦截。在 CglibAopProxy(Cglib 动态代理)的内部类 DynamicAdvisedInterceptor 的 intercept 方法或 JdkDynamicAopProxy(JDK 动态代理)的 invoke 方法中,会间接调用 AbstractFallbackTransactionAttributeSource 的 computeTransactionAttribute 方法,以获取 @Transactional 注解的事务配置信息。
然而,非 public 修饰的方法对外部是不可见的,无法被代理对象直接调用。因此,当 @Transactional 注解应用在非 public 修饰的方法上时,代理对象无法触发对应的拦截器,导致 @Transactional 注解失效。为了让 @Transactional 生效,被注解的方法需要是 public 修饰的,以确保代理对象可以正确地拦截并应用事务的配置。
protected TransactionAttribute computeTransactionAttribute(Method method,
Class<?> targetClass) {
// Don't allow no-public methods as required.
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;
}
此方法会检查目标方法的修饰符是否为 public,不是 public 则不会获取@Transactional 的属性配置信息。
2、@Transactional 注解属性 propagation 设置错误
- TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
- TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
- TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
3、@Transactional 注解属性 rollbackFor 设置错误
rollbackFor 可以指定能够触发事务回滚的异常类型。Spring 默认抛出了未检查 unchecked 异常(继承自 RuntimeException 的异常)或者 Error 才回滚事务,其他异常不会触发回滚事务。
// 希望自定义的异常可以进行回滚
@Transactional(propagation= Propagation.REQUIRED,rollbackFor= MyException.class
若在目标方法中抛出的异常是 rollbackFor 指定的异常的子类,事务同样会回滚。
4、同一个类中方法调用,导致@Transactional 失效
开发中避免不了会对同一个类里面的方法调用,比如有一个类 Test,它的一个方法 A,A 再调用本类的方法 B(不论方法 B 是用 public 还是 private 修饰),但方法 A 没有声明注解事务,而 B 方法有。则外部调用方法 A 之后,方法 B 的事务是不会起作用的。这也是经常犯错误的一个地方。
那为啥会出现这种情况?其实这还是由于使用 Spring AOP 代理造成的,因为只有当事务方法被当前类以外的代码调用时,才会由 Spring 生成的代理对象来管理。
//@Transactional
@GetMapping("/test")
private Integer A() throws Exception {
CityInfoDict cityInfoDict = new CityInfoDict();
cityInfoDict.setCityName("2");
/**
* B 插入字段为 3的数据
*/
this.insertB();
/**
* A 插入字段为 2的数据
*/
int insert = cityInfoDictMapper.insert(cityInfoDict);
return insert;
}
@Transactional()
public Integer insertB() throws Exception {
CityInfoDict cityInfoDict = new CityInfoDict();
cityInfoDict.setCityName("3");
cityInfoDict.setParentCityId(3);
return cityInfoDictMapper.insert(cityInfoDict);
}
这种情况是最常见的一种@Transactional 注解失效场景
@Transactional
private Integer A() throws Exception {
int insert = 0;
try {
CityInfoDict cityInfoDict = new CityInfoDict();
cityInfoDict.setCityName("2");
cityInfoDict.setParentCityId(2);
/**
* A 插入字段为 2的数据
*/
insert = cityInfoDictMapper.insert(cityInfoDict);
/**
* B 插入字段为 3的数据
*/
b.insertB();
} catch (Exception e) {
e.printStackTrace();
}
}
如果 B 方法内部抛了异常,而 A 方法此时 try catch 了 B 方法的异常,那这个事务就不能正常回滚了,会抛出异常:
org.springframework.transaction.UnexpectedRollbackException: Transaction rolled