@Transactional在private方法上不生效,因Spring事务基于代理机制,仅public方法可被AOP拦截;private方法调用绕过代理,事务逻辑无法织入。

为什么@Transactional在private方法上不生效
Spring事务基于代理机制,只有被代理对象的public方法才能被AOP拦截并织入事务逻辑。private方法无法被代理调用,即使加了@Transactional注解,也完全不会触发事务管理器。
- 调用链中若从本类内部直接调用
private void doUpdate(),事务不会开启 - 解决办法是把逻辑抽到另一个
@Service类中,通过Spring容器注入调用 - 或改用
TransactionTemplate手动控制事务,但会牺牲声明式事务的简洁性
同一类中this调用导致事务失效
当一个@Service类里,methodA()(带@Transactional)调用本类的methodB()(也带@Transactional),实际执行的是this.methodB()——走的是原始对象调用,绕过了代理对象,事务不会传播。
- 现象:
methodB抛出异常,methodA的事务不回滚 - 根本原因:Spring代理只对“外部Bean调用”生效,
this指向当前实例,不是代理对象 - 验证方式:在
methodB开头打印this.getClass(),会看到类似UserService$$EnhancerBySpringCGLIB$$xxx以外的原始类名
RuntimeException之外的异常未配置rollbackFor
Spring默认只对RuntimeException及其子类、Error触发回滚。如果业务抛出的是Exception(比如IOException、自定义检查异常),且没显式配置rollbackFor,事务会正常提交。
- 常见错误写法:
@Transactional
public void transfer() throws BusinessException {
// ...
throw new BusinessException("余额不足");
} - 正确做法:
@Transactional(rollbackFor = BusinessException.class)
public void transfer() throws BusinessException {
// ...
} - 注意:
rollbackFor支持数组,可同时指定多个异常类型;noRollbackFor用于排除特定异常
数据库引擎不支持事务或事务已提交
事务失效有时和Spring无关,而是底层环境问题。最典型的是MySQL表使用MyISAM引擎——它根本不支持事务,无论注解怎么写都无效。
立即学习“Java免费学习笔记(深入)”;
- 检查方式:
SHOW CREATE TABLE user;
确认引擎是否为InnoDB - 另一个隐蔽情况:事务方法内调用了
JdbcTemplate.update()后又手动调用connection.commit(),会导致Spring事务管理器失去控制权 - 此外,若事务方法被标记为
propagation = Propagation.NOT_SUPPORTED,当前操作会挂起事务,也不受事务保护










