事务@Transactional(rollbackFor = Exception.class)和事务传播
事务@Transactional(rollbackFor = Exception.class)和事务传播¶
你问的这个注解:
默认行为¶
Spring 的事务管理有个 默认的回滚规则:
- 运行时异常(RuntimeException)和 Error 会触发回滚。
例如:
NullPointerException,IllegalArgumentException。 - 受检异常(Checked Exception)不会回滚。
例如:
IOException,SQLException。
也就是说,如果你抛的是受检异常(Exception),Spring 默认 不会帮你回滚。
rollbackFor 的作用¶
rollbackFor = Exception.class 就是告诉 Spring:
不管是运行时异常还是受检异常,只要抛出
Exception(及其子类),都要回滚事务。
等价于把 回滚范围扩大了。
举例¶
假设你写的代码里有个方法:
- 如果没写
rollbackFor,Spring 默认不会因为IOException回滚,数据就可能删除一半。 - 如果加了
rollbackFor = Exception.class,Spring 会在抛出IOException时也帮你回滚,保证事务一致性。
常见写法¶
- 常见业务:
→ 默认只对 RuntimeException 回滚。 - 保险写法(所有异常都回滚):
Java 异常(Exception)¶
1. 受检异常(Checked Exception)¶
- 特点:编译器强制要求你处理(必须
try...catch或throws抛出)。 - 继承体系:继承自
Exception,但 不是RuntimeException的子类。 -
常见例子:
-
IOException(文件读写异常) SQLException(数据库异常)ClassNotFoundExceptionParseException
➡️ 编译器会强制你写 throws IOException 或者 try...catch,否则代码编译不过。
2. 非受检异常(Unchecked Exception / RuntimeException)¶
- 特点:编译器不强制你处理,可以随便抛。
- 继承体系:
RuntimeException及其子类。 -
常见例子:
-
NullPointerException IndexOutOfBoundsExceptionIllegalArgumentExceptionArithmeticException
➡️ 编译时不会提示你要处理异常,但运行时可能挂掉。
3. 区别总结¶
| 异常类型 | 是否必须处理 | 继承体系 | 常见例子 |
|---|---|---|---|
| 受检异常 (Checked) | ✅ 必须try...catch或throws |
继承Exception,但不是RuntimeException子类 |
IOException,SQLException |
| 非受检异常 (Unchecked) | ❌ 不强制 | 继承RuntimeException |
NullPointerException,IndexOutOfBoundsException |
所以回到你之前的 @Transactional:
- Spring 默认只在 非受检异常(RuntimeException) 才回滚。
- 如果你抛的是 受检异常(如 SQLException) ,那就要写
rollbackFor = Exception.class,不然不会回滚。
要不要我给你写一个小 demo 程序,分别演示受检异常和非受检异常在事务中的回滚差异?
事务传播¶

“事务传播”就是 当一个事务方法调用另一个事务方法时,Spring 应该如何处理事务。
1. 为什么要有传播行为?¶
比如你有两个方法:
问题是:createUser() 调用 createOrder() 时,要用同一个事务,还是开启新事务?
➡️ 这就由 事务传播机制 决定。
2. Spring 的事务传播类型(Propagation 枚举)¶
| 传播行为 | 说明 |
|---|---|
| REQUIRED(默认) | 如果当前有事务,就加入当前事务;没有就新建一个事务。 |
| REQUIRES_NEW | 总是新建一个事务,如果外层有事务,就挂起外层事务。 |
| SUPPORTS | 如果当前有事务,就加入事务;如果没有,就以非事务方式运行。 |
| NOT_SUPPORTED | 不支持事务,总是以非事务方式运行,如果外层有事务,就挂起外层事务。 |
| NEVER | 不允许在事务中运行,如果调用时有事务,就抛异常。 |
| MANDATORY | 必须运行在事务中,如果外层没有事务,就抛异常。 |
| NESTED | 如果外层有事务,就在外层事务里再嵌套一个子事务(可以独立回滚);如果没有事务,就新建一个。 |
3. 举例理解¶
✅ REQUIRED(默认)¶
- A、B 在 同一个事务 里。
- 如果 B 抛异常,A 也会回滚。
✅ REQUIRES_NEW¶
- 调用 B 时,会新建一个事务。
- 外层事务 A 会被 挂起,等 B 执行完再继续。
- B 的回滚不会影响 A,反之亦然。
✅ NESTED¶
- 在 A 的事务里创建一个“子事务”。
- B 回滚时,只会回滚自己,不影响 A。
- 但前提是数据库要支持 保存点(savepoint) ,比如 MySQL 就支持。
4. 对比总结¶
| 传播行为 | 是否复用外层事务 | 外层事务挂起 | 子事务回滚独立性 |
|---|---|---|---|
| REQUIRED | ✅ 复用 | ❌ | ❌ |
| REQUIRES_NEW | ❌ 新建 | ✅ | ✅ |
| NESTED | ✅ 复用 + 保存点 | ❌ | ✅(基于保存点) |
✅ 总结一句话:
- REQUIRED:最常用,外层事务有就加入,没有就新建。
- REQUIRES_NEW:外层事务挂起,自己开新事务。
- NESTED:外层事务里开一个子事务(依赖保存点)。