不加rollbackfor时,自定义受检异常(如bizexception extends exception)抛出后事务不会回滚,导致数据不一致;因spring默认仅对runtimeexception及其子类和error自动回滚。

不加 rollbackFor,最直接的问题是:**自定义受检异常(即继承 Exception 的异常)抛出后,事务不会回滚,数据库变更被错误提交,导致数据不一致。**
为什么没回滚?默认规则只认 RuntimeException
Spring 事务默认只对 RuntimeException 及其子类(比如 NullPointerException、IllegalArgumentException)和 Error 自动回滚。而像 IOException、SQLException,以及你自己写的 BizException extends Exception,都属于受检异常(checked exception),默认不触发回滚,事务会照常提交。
举个例子:
- 转账方法里先扣款、再入账;
- 入账时抛出
SQLException(受检异常); - 因为没配
rollbackFor = SQLException.class,扣款操作已生效并提交; - 用户钱被扣了,但收款方没到账——典型的“半截事务”脏数据。
怎么改?三步定位+两步修复
第一步:确认异常是不是受检异常
- 看你的自定义异常是否继承
Exception(如public class BizException extends Exception); - 如果是继承
RuntimeException(如BizRuntimeException),默认就回滚,不用加rollbackFor; - 不确定就查源码或 IDE 提示,看它父类是不是
Exception。
第二步:在 @Transactional 中显式声明 rollbackFor
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- 单个异常:
@Transactional(rollbackFor = BizException.class) - 多个异常:
@Transactional(rollbackFor = {BizException.class, IOException.class}) - 想让所有受检异常都回滚(慎用):
@Transactional(rollbackFor = Exception.class),但可能掩盖本该由业务处理的异常。
第三步:确保异常真的“抛出去”了
- 方法内不能
try-catch后静默吞掉异常; - 如果 catch 了,必须
throw new BizException(...)重新抛出原类型; - 不能捕获后抛出另一个没在
rollbackFor里声明的新异常。
常见改错对比
错误写法:
-
@Transactional public void transfer() throws BizException { ... throw new BizException(); }→ 没写rollbackFor,不回滚 -
try { ... } catch (BizException e) { log.error(...); }→ 异常被吃掉,事务根本感知不到
正确写法:
@Transactional(rollbackFor = BizException.class) public void transfer() throws BizException { ... throw new BizException("余额不足"); }- 或者把
BizException改成extends RuntimeException,去掉rollbackFor(语义上不推荐,弱化了业务异常与系统异常的区分)
要不要全局配置?一般不建议
Spring 不支持通过配置让所有受检异常默认回滚。有人试图用编程方式统一设置,但容易误伤——比如把网络超时、文件不存在这类可重试的业务异常也强制回滚,反而降低系统健壮性。更稳妥的做法是:在每个明确需要回滚的业务方法上,精准声明 rollbackFor。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










