两个事务方法导致异常抛出Transaction rolled back because it has been marked as rollback-only
异常现场(背景)
在springboot事物操作开发中,如果A方法调用B方法,A和B方法都在不同的类中,且A和B都加了@Transactional注解,A调用B方法时,将B方法try catch了。
代码:
@Service
public class BService {
@Transactional(rollbackFor = Exception.class)
public void bMethod() {
// ... 业务逻辑 ...
throw new RuntimeException("B方法异常");
}
}
@Service
public class AService {
@Autowired
private BService bService;
@Transactional(rollbackFor = Exception.class)
public void aMethod() {
try {
bService.bMethod();
} catch (Exception e) {
// 处理B方法的异常
// 可以记录日志或采取其他措施
}
// A方法的其他逻辑,继续执行
}
}
问题产生原因
在Spring框架中,当使用@Transactional注解时,事务的边界通常是由Spring的AOP代理来控制的。如果在事务中发生异常并且没有被捕获,Spring会标记当前事务为“只回滚”(rollback-only),并在事务的边界处(通常是方法的退出点)执行回滚。
当A方法调用B方法,并且B方法因为异常而回滚了,但由于A方法捕获了异常并没有重新抛出,所以A方法的事务不会自动回滚,但由于B方法的事务已经被标记为“只回滚”,当A方法的事务试图提交时,它会发现它处于“只回滚”状态,并抛出一个异常,提示:Transaction rolled back because it has been marked as rollback-only
解决方式
现在的需求是,想要A继续执行不抛出异常,如果A异常,A的方法也会回滚,B异常也正常回滚,但B是单独的事务。很简单,如果B方法是一个可以独立处理其事务逻辑的服务,并且不需要A方法的事务上下文,可以让B方法独立地处理其事务。这样,B方法的异常就不会影响A方法的事务。
代码:
@Service
public class BService {
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public void bMethod() {
// ... 业务逻辑 ...
// 如果这里抛出异常,只有B的事务会回滚
throw new RuntimeException("B方法异常");
}
}
@Service
public class AService {
@Autowired
private BService bService;
@Transactional(rollbackFor = Exception.class)
public void aMethod() {
try {
bService.bMethod();
} catch (Exception e) {
// 处理B方法的异常,但A方法的事务不会受到影响
// 可以记录日志或采取其他措施
}
// A方法的其他逻辑,继续执行
}
}