@transactional不是简单加注解,关键在边界清晰(service层)、传播合理(慎用requires_new/nested)、异常可控(显式配置rollbackfor)、资源隔离(避免内部调用失效)。

Java 企业级事务管理不是简单加个 @Transactional 就完事,关键在**边界清晰、传播合理、异常可控、资源隔离**。盲目套用注解容易引发事务不生效、嵌套异常丢失、连接泄漏等问题。
明确事务边界:用在 Service 层,别放在 Controller 或 DAO
事务应控制业务逻辑的原子性单元,通常对应一个完整的业务用例(如“下单并扣库存”)。Service 方法是天然的事务边界。
- Controller 层只做参数校验、协议转换,不承载事务逻辑
- DAO 层只负责单表/单数据源操作,不声明事务——它该被 Service 调用,而非自己开启事务
- 若需跨多个 Service 协同(如订单服务 + 积分服务),应通过分布式事务方案(Seata、RocketMQ 事务消息)或最终一致性,而非强行拉长本地事务
慎选传播行为:默认 REQUIRED 够用,嵌套调用要显式设计
propagation = Propagation.REQUIRED 是最常用且安全的默认值。除非有明确需求,否则避免使用 REQUIRES_NEW 或 NESTED。
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
-
REQUIRES_NEW会挂起当前事务、开新事务——适合日志记录、审计等“事后动作”,但会导致外层事务回滚时,内层已提交的数据无法回滚 -
NESTED依赖 JDBC 保存点,仅对同一数据源有效;若底层用 HikariCP + MySQL,需确保allowMultiQueries=true等配置兼容 - 内部方法调用(this.method())不会触发代理拦截,
@Transactional失效——必须通过 Spring 容器注入自身或使用AopContext.currentProxy()(不推荐)
精准控制回滚:按异常类型配置,别依赖默认规则
Spring 默认只对 RuntimeException 和 Error 回滚,对 Exception 子类(如 IOException、自定义业务异常)不回滚。
- 用
rollbackFor = {CustomBizException.class, SQLException.class}显式声明需回滚的异常类型 - 用
noRollbackFor = {ValidationException.class}排除某些异常不触发回滚(如参数校验失败应提前返回,不该进事务) - 捕获异常后又抛出新异常,可能破坏原始异常类型——建议直接抛出原异常,或封装时保留 cause
避免事务失效的典型陷阱
这些情况会让 @Transactional 形同虚设:
- 方法不是 public —— Spring AOP 代理不生效
- 类没被 Spring 管理(缺少
@Service或未被扫描) - 同一类中非事务方法调用事务方法(绕过代理)
- 事务方法内使用了
try-catch吞掉异常且未重新抛出 - 数据库引擎不支持事务(如 MySQL 的 MyISAM 表)或事务隔离级别配置错误
事务不是银弹,而是业务一致性的兜底机制。设计时优先考虑幂等、补偿、状态机等更健壮的手段,把事务当作“最后防线”。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










