@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,合理使用一般不会出现这个问题
还有如果仅仅是捕获了异常而没有抛出,那么也是不会触发回滚的
标签:insert,回滚,场景,springboot,对象,void,事务,失效,Transactional From: https://www.cnblogs.com/kun1790051360/p/18282645