Skip to content

概念卡片: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)

属性默认说明
传播行为 propagationREQUIRED已有事务则加入,无则新建(见 [Spring AOP与声明式事务](./Spring AOP与声明式事务))
隔离级别 isolationDEFAULT跟随数据库默认(MySQL=REPEATABLE_READ,Oracle=READ_COMMITTED)
超时 timeout-1(不超时)超时自动回滚,单位秒
只读 readOnlyfalse只读事务,数据库可优化
回滚规则 rollbackForRuntimeException/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)实现,保证对外行为统一、方便扩展。

常见误解(避坑)

  1. ❌ "加了 @Transactional 就一定有事务"。→ 自调用、非 public、异常被吞、MyISAM 都会让它失效。
  2. ❌ "抛异常就一定回滚"。→ 默认只回滚 RuntimeException/Error,Checked 异常(IOException/SQLException)不回滚,需 rollbackFor 显式指定。
  3. ❌ "事务失败只影响数据库操作"。→ 事务是逻辑整体,回滚只回滚 DB 操作,内存/缓存/消息等副作用不会自动回滚,需自行补偿。
  4. ❌ "只读事务没意义"。→ 多条查询要保证「整体读一致性」(如统计报表),必须放一个只读事务里,否则中间数据被改会读不一致。

关联

最近更新