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 @ 2024-07-03 22:09  天启A  阅读(360)  评论(0)    收藏  举报