Spring Boot调用类内嵌套@Transactional方法事务失效报错问题
问题根本原因
这是Spring声明式事务基于AOP代理实现的固有特性导致的,具体逻辑如下:
- Spring的
@Transactional注解依赖动态代理(JDK动态代理/CGLIB代理)实现事务增强,只有被@Transactional标注的方法从代理对象外部直接调用时,事务增强逻辑才会触发生效。 - 你当前的写法是
addPlayerToTeam方法内部直接调用同一个类的addPlayerToTeamInsert方法,这种类内部的自调用走的是当前类的原生对象,而非Spring生成的增强代理对象,因此addPlayerToTeamInsert上的@Transactional注解完全不会生效,不会开启新事务。 - 后续调用
historyService.addPlayerHistoryToNew时,该方法的事务传播级别是Propagation.MANDATORY,要求调用方必须已经存在事务上下文,但此时没有可用事务,所以直接抛出IllegalTransactionStateException异常。
入口方法加注解可正常运行的原因
当你把@Transactional加在入口方法addPlayerToTeam上时,控制器层调用的是Spring生成的代理对象的addPlayerToTeam方法,事务增强逻辑会在进入该方法时就开启事务,后续内部调用的所有方法都会运行在这个已开启的事务上下文中,调用historyService.addPlayerHistoryToNew时可以匹配到已存在的事务,因此不会报错。
可选解决方案
如果要保留你原本「部分逻辑无事务、部分逻辑带事务」的拆分设计,可以通过两种方式修复:
- 注入自身代理对象调用:在
TeamPlayerServiceBestCase中注入自己的代理实例,通过代理实例调用addPlayerToTeamInsert触发事务增强
@Service public class TeamPlayerServiceBestCase { @Autowired private TeamPlayerServiceBestCase proxySelf; // 注入自身代理对象 public void addPlayerToTeam() { // 1. 无事务校验参数及合法性 proxySelf.addPlayerToTeamInsert(); // 走代理调用,事务生效 // 3. 无事务发送欢迎通知 } @Transactional public void addPlayerToTeamInsert() { // 球员入队+生成历史记录逻辑 } }
- 用
AopContext获取当前代理:启动类添加@EnableAspectJAutoProxy(exposeProxy = true)注解,内部调用时替换为((TeamPlayerServiceBestCase) AopContext.currentProxy()).addPlayerToTeamInsert()即可。
内容的提问来源于stack exchange,提问作者silenthill
相关产品推荐
相关产品推荐

