@Transactional失效的场景都有哪些呢 ?

一、代理不生效导致
1、同一个类中的方法,通过this调用导致失效
同一个类中,addOrder()方法无事务,addOrder2()方法存在事务,addOrder()调用addOrder2()

我们通过外部方法调用addOrder()方法,来完成数据库的插入,通过手动的设置异常order/0,来观察addOrder2()方法中的数据是否会正常回滚。

通过数据库的结果显示,数据正常入库了,证明了我们的事务并未生效。
同一个类中,addOrder()和addOrder2()都存在事务,addOrder()调用addOrder2()。

order/0 产生异常之后,通过数据库的结果显示,发现数据并未入库,说明事务生效了。
我们发现并不是所有同一个类,方法的内部调用事务都会失效。
那我们就来了解一下事务为啥不生效 ?

看图 !外部代码调用addOrder()方法时,并没有直接进入目标方法,而是首先进入了DynamicAdvisedInterceptor的intercept()方法中。
这是因为我们的orderService是一个代理对象,这里调用addOrder()方法的,其实是代理对象,而代理对象对目标方法的调用,
会转发进入CGLIB动态代理类的intercept()方法中进行增强。
然而当调用this.addOrder2()方法,而这里this是原生对象,并不是代理,自然就没有事务控制了
解决:同一个类中的方法,通过this调用导致失效
修正方式:
-
①:将this换成代理的userService, 可以自己注入自己
@Resource private OrderService orderService, 当然也可以不用注入,直接在Spring容器中获取userService这个bean -
②:将#addOrder()方法开启事务即加上@Transactional(rollbackFor = Exception.class)
2、事务方法被final、static修饰

失效原因:CGLIB是通过生成目标类的子类方式生成代理类的,被final、static修饰的方法,无法被子类重写
3、@Transactional 应用在非 public 修饰的方法上
@Transactional(rollbackFor = Exception.class)
private void addUserRole(Long userId, List<Long> roleIds) {
if (CollectionUtils.isEmpty(roleIds)) {
return;
}
List<UserRole> userRoles = new ArrayList<>();
roleIds.forEach(roleId -> {
UserRole userRole = new UserRole();
userRole.setUserId(userId);
userRole.setRoleId(roleId);
userRoles.add(userRole);
});
userRoleDAO.insertBatch(userRoles);
throw new RuntimeException("发生异常咯");
}
idea也会提示爆红:

Spring通过CGLIB动态代理来增强生产代理对象,CGLIB 通过继承方式实现代理类,private 方法在子类不可见,自然也就无法进行事务增强。
事务在基于AOP事务控制实现原理一文中也分析过,会调用到AbstractFallbackTransactionAttributeSource的computeTransactionAttribute()方法
@Nullable
protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
// Don't allow no-public methods as required.
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;
}
......
}
4、异常被方法内部try catch捕获,没有重新抛出

当外部接口调用addOrder()方法,异常发生的时候,数据库数据正常入库了,事务未生效。

在TransactionAspectSupport.invokeWithinTransaction()方法中,我们可以看到以下逻辑。


5、嵌套事务回滚多了
示例代码:


6、rollbackFor属性设置错误
示例代码:
@Service
public class OrderServiceImpl {
@Autowired
private OrderMapper orderMapper;
@Transactional
public int addOrder(Double amount, String address) throws FileNotFoundException {
int order = orderMapper.addOrder(amount, address);
throw new FileNotFoundException("11111");
}
}
在demo中,我们手动的抛出FileNotFountExceptionshou异常(受检异常。),这是一个IOException异常。
当我们使用@Transactional,在未对rollbackFor做配置的情况下,默认是支持对Runtime和Error异常的回滚的。

但当我们的demo中的异常是IOException的时候


从源码694行else逻辑的注释上我们也能看出,无法回滚异常。
所以通常情况下,我们建议指定@Transactional(rollbackFor = Exception.class)的方式进行异常捕获。
7、设置不支持事务的传播机制
Spring支持了7种传播机制,分别为:

上面不支持事务的传播机制为:propagation_supports、propagation_not_supported、propagation_never
如果配置了这三种传播方式的话,在发生异常的时候,事务是不会回滚的。
示例代码:
@Service
public class OrderServiceImpl {
@Autowired
private OrderMapper orderMapper;
@Transactional(rollbackFor = Exception.class, propagation = Propagation.SUPPORTS)
public int addOrder(Double amount, String address) throws FileNotFoundException {
int order = orderMapper.addOrder(amount, address);
throw new FileNotFoundException("11111");
}
}

浙公网安备 33010602011771号