事务@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
(数据库异常)ClassNotFoundException
ParseException
➡️ 编译器会强制你写 throws IOException
或者 try...catch
,否则代码编译不过。
2. 非受检异常(Unchecked Exception / RuntimeException)¶
- 特点:编译器不强制你处理,可以随便抛。
- 继承体系:
RuntimeException
及其子类。 -
常见例子:
-
NullPointerException
IndexOutOfBoundsException
IllegalArgumentException
ArithmeticException
➡️ 编译时不会提示你要处理异常,但运行时可能挂掉。
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:外层事务里开一个子事务(依赖保存点)。