主题
概念卡片:Spring 事务失效与避坑
与 [Spring AOP与声明式事务](./Spring AOP与声明式事务)(讲 7 种传播行为)互补:本页聚焦「事务为什么失效」+「事务属性(隔离/回滚/只读)」+「编程式 vs 声明式」。传播行为不重复。
一句话机制
@Transactional 基于 AOP 动态代理实现:容器为标注类生成代理,TransactionInterceptor 在目标方法前开事务、异常回滚、完成后提交。 因此「事务是否生效」取决于「是否真的走代理」+「异常是否满足回滚规则」。
事务失效的 5 大场景(面试高频)
| 失效场景 | 根因 | 解法 |
|---|---|---|
| 同类自调用 | 同类方法内调用 this.method(),绕过了代理 | 拆到不同 Bean,或注入自身代理/AopContext,或 AspectJ |
| 非 public 方法 | AOP 代理只能拦截 public 方法 | 改为 public |
| 异常被 catch 吞掉 | 异常没抛出,代理感知不到 | 别吞异常,或手动 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly() |
| Checked 异常不回滚 | 默认只回滚 RuntimeException/Error | @Transactional(rollbackFor = Exception.class) |
| 数据库引擎不支持事务 | MyISAM 等引擎无事务 | 换 InnoDB |
java
@Service
public class MyService {
public void method1() { method2(); } // ❌ 自调用,method2 事务失效
@Transactional
public void method2() { /* ... */ }
}事务属性(TransactionDefinition)
| 属性 | 默认 | 说明 |
|---|---|---|
| 传播行为 propagation | REQUIRED | 已有事务则加入,无则新建(见 [Spring AOP与声明式事务](./Spring AOP与声明式事务)) |
| 隔离级别 isolation | DEFAULT | 跟随数据库默认(MySQL=REPEATABLE_READ,Oracle=READ_COMMITTED) |
| 超时 timeout | -1(不超时) | 超时自动回滚,单位秒 |
| 只读 readOnly | false | 只读事务,数据库可优化 |
| 回滚规则 rollbackFor | RuntimeException/Error | 默认只回滚运行时异常 |
隔离级别与并发问题
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| READ_UNCOMMITTED | 可能 | 可能 | 可能 |
| READ_COMMITTED | 防 | 可能 | 可能 |
| REPEATABLE_READ | 防 | 防 | 可能 |
| SERIALIZABLE | 防 | 防 | 防 |
InnoDB 保证 ACID 的机制:Redo Log 保证持久性、Undo Log 保证原子性、锁 + MVCC 保证隔离性,三样齐了才有一致性。
编程式 vs 声明式
| 方式 | 实现 | 特点 |
|---|---|---|
| 编程式 | TransactionTemplate / PlatformTransactionManager | 灵活、精确控边界,但侵入性强 |
| 声明式 | @Transactional(AOP) | 无侵入、可维护,但不够灵活 |
不变量:常规业务用声明式;需要精确控制事务边界(如局部提交、动态条件)才用编程式。
事务体系接口
PlatformTransactionManager(核心,各平台实现)
├─ DataSourceTransactionManager(JDBC)
├─ HibernateTransactionManager
└─ JpaTransactionManager
TransactionDefinition(事务定义:传播/隔离/超时/只读)
TransactionStatus(事务运行状态)PlatformTransactionManager 是抽象:Spring 不直接管事务,而是让各平台(JDBC/Hibernate/JPA)实现,保证对外行为统一、方便扩展。
常见误解(避坑)
- ❌ "加了 @Transactional 就一定有事务"。→ 自调用、非 public、异常被吞、MyISAM 都会让它失效。
- ❌ "抛异常就一定回滚"。→ 默认只回滚 RuntimeException/Error,Checked 异常(IOException/SQLException)不回滚,需
rollbackFor显式指定。 - ❌ "事务失败只影响数据库操作"。→ 事务是逻辑整体,回滚只回滚 DB 操作,内存/缓存/消息等副作用不会自动回滚,需自行补偿。
- ❌ "只读事务没意义"。→ 多条查询要保证「整体读一致性」(如统计报表),必须放一个只读事务里,否则中间数据被改会读不一致。
关联
- 原始资料:✅Spring事务详解
- 补充:[Spring AOP与声明式事务](./Spring AOP与声明式事务)(7 种传播行为)
- 总览:Spring技术栈总览
- 域地图:A00-百科/Java后端/Java后端