[SpringBoot]注解@Transactional
一、 工作原理
@Transactional 的本质是基于 Spring AOP 实现的。当 Spring 容器启动时,它会扫描标注了该注解的类或方法,并为这些 Bean 生成一个 代理对象(Proxy)。
执行流程
当调用方请求一个被 @Transactional 修饰的方法时,实际上是在调用代理对象。执行顺序如下:
- 拦截请求:代理对象拦截对目标方法的调用。
- 开启事务:代理对象向事务管理器(Transaction Manager)申请,通过数据库连接设置
conn.setAutoCommit(false)。 - 执行业务:执行目标类中的实际业务方法。
- 结果处理:
- Commit:如果方法正常返回,代理对象执行事务提交。
- Rollback:如果方法抛出特定异常,代理对象执行事务回滚。
- 资源清理:关闭数据库连接或将其归还连接池。
核心提示:理解“代理对象”是理解事务机制的关键。如果调用没有经过代理对象,事务逻辑就不会执行。
二、 核心属性详解
虽然 @Transactional 开箱即用,但在生产环境中,我们必须关注以下两个关键参数以确保数据一致性。
1. rollbackFor(回滚规则)
这是最容易被忽视的属性。
- 默认行为:Spring 默认仅在抛出
RuntimeException(运行时异常)或Error时回滚。对于Checked Exception(受检异常,如IOException、SQLException),默认不回滚。 - 最佳实践:务必显式指定回滚异常类型,确保所有异常均能触发回滚。
1 | // 推荐写法:覆盖所有异常情况 |
2. propagation(传播行为)
该属性定义了当事务方法被另一个事务方法调用时,事务该如何传播。Spring 共支持 7 种 传播行为:
| 属性值 | 行为描述 | 适用场景 |
|---|---|---|
| REQUIRED (默认) | 如果当前存在事务,则加入该事务;如果当前无事务,则新建一个事务。 | 绝大多数增删改业务场景 |
| REQUIRES_NEW | 无论当前是否存在事务,都挂起当前事务,开启一个独立的新事务。 | 记录日志、审计流水(即使主业务失败,日志也必须入库) |
| NESTED | 如果当前存在事务,则在嵌套事务内执行(基于 JDBC SavePoint);如果无事务,行为同 REQUIRED。 | 复杂的子模块回滚需求(主事务回滚会回滚子事务,但子事务回滚不影响主事务) |
| SUPPORTS | 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务方式执行。 | 纯查询操作(适合“能有最好,没有也行”的场景) |
| NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 | 发送邮件、短信等不涉及数据库的耗时操作,避免占用数据库连接 |
| MANDATORY | 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。 | 必须在已有事务上下文中执行的方法 |
| NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 | 严禁在事务中执行的操作 |
总结
@Transactional 是一个强大的工具。深入理解其背后的 AOP 代理机制,并正确配置 回滚规则 (rollbackFor) 和 传播行为 (propagation),是确保后端业务数据一致性的基础。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 DuckLing's Blog!
