事务问题往往不是注解写错,而是我们对“谁负责拦截这次调用”理解得不够清楚。下面用代理边界作为起点,建立一套更可靠的排查顺序。
为什么事务会悄悄失效?
@Transactional 不是方法本身的能力,而是 Spring 在调用进入代理时附加的一层行为。只有调用经过代理,事务管理器才能在进入目标方法前开启事务,并在方法结束后决定提交还是回滚。
自调用绕开代理
同一个类里的 this.someTransactionalMethod() 属于普通 Java 方法调用,它不会回到 Spring 创建的代理对象。因此,标了事务的方法可能像普通方法一样执行。
优先把需要独立事务边界的操作拆到另一个服务中;若必须保留在同一类,也要明确通过代理入口调用,而不是把实现细节隐藏在一次自调用里。
异常被吞掉
事务默认依赖异常信号决定回滚。如果捕获异常后只记录日志并继续返回成功结果,代理看到的就是一次正常完成的调用。需要恢复时,应重新抛出可回滚的异常,或明确标记当前事务为 rollback-only。
一套更快的排查顺序
- 确认调用是否从 Spring 管理的 Bean 进入;
- 确认目标方法是否被代理规则覆盖;
- 检查异常是否被转换或吞掉;
- 最后再看传播行为、只读配置与数据库引擎能力。
这套顺序把“事务注解为什么没生效”的问题,收敛为可观察的调用边界,而不是盲目调整配置。