springboot中事务失效的一些场景以及如何应对

@Transactional是基于AOP的,因此事务发生需要两个条件:

1.添加@Transactional注解

2.使用代理对象

 

失效场景:同一个类中直接调用的类方法,而被调方法带有@Transactional

下面这段代码会抛出runtimeException异常,但是事务不会回滚。即insert生效了

因为b()在调用a()方法时,对象已经不再是代理对象,而是普通对象。而aop都是基于代理对象实现的,即aop是不关心普通对象头顶上注解的,因此事务会失效

复制代码
@Transactional
public void a(){
int insert = mapper.insert(Entity);
if(insert<=0)
throw new RuntimeException("插入信息失败!");

}
public void b(){
a();
}
复制代码

如果我们将代码块修改为下面这样,那么他是可以正常回滚的。此时其实只是发生了b()的回滚,因为a()抛出了异常给b()。如果这样写,其实a()的注解是无效的。达不到预期缩减事务代码块的目的

复制代码

@Transactional
public void a(){
int insert = mapper.insert(Entity);
if(insert<=0)
throw new RuntimeException("插入信息失败!");

}
@Transactional
public void b(){
a();
}

复制代码

解决办法:

那么如何让a()方法事务能够回滚呢?触发的两个条件,我们已经具备了第一个添加@Transactional注解,但是目前是普通的对象,而非代理对象。因此我们需要让a()由代理对象来实现

下例中,我们将a()方法写入service接口,然后通过注册一个本方法的bean,实现了代理对象,满足上述条件,可以对a()单独实现回滚

复制代码
@Autowired
Service service;
@Transactional
public void a(){
int insert = mapper.insert(Entity);
if(insert<=0)
throw new RuntimeException("插入信息失败!");

}
public void b(){
service.a();
}
复制代码

 

除此以外,还有一些其他让事务不能回滚的原因:

1.a()方法是private或者final的

我们知道,aop的实现是基于代理对象的。上述解决办法中,如果我们没有区分出impl。

那么一旦a方法私有化或者不可变的,会导致代理对象作为普通对象的子类,没有权限去通过AOP重写方法,会导致爆出空指针异常

2.数据库不支持事务、没有bean化的类:神仙难救

3.如果没有捕获到runtime或者error,spring是不会触发回滚的。

关于这点阿里规范中要求在@Transactional中指定rollbackFor,合理使用一般不会出现这个问题

还有如果仅仅是捕获了异常而没有抛出,那么也是不会触发回滚的

posted @   天启A  阅读(229)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示