在 Spring 事务中,如果抛出普通的 Exception(非 RuntimeException),事务默认会回滚吗?
默认情况下,不会回滚。
在 Spring 事务(@Transactional)中,默认的回滚机制如下:
会触发回滚的异常:
- Unchecked Exception(非受检异常):即
RuntimeException及其所有子类(如NullPointerException,IllegalArgumentException等)。 - Error:如
OutOfMemoryError等系统级错误。
- Unchecked Exception(非受检异常):即
不会触发回滚的异常:
- Checked Exception(受检异常/普通 Exception):即直接继承自
Exception但不是RuntimeException的异常(如IOException,SQLException,ClassNotFoundException等)。如果抛出了这类异常,Spring 默认会提交事务,而不是回滚。
- Checked Exception(受检异常/普通 Exception):即直接继承自
为什么 Spring 默认这样设计?
Spring 的这一设计理念源自 EJB(Enterprise JavaBeans)规范和 Java 的异常设计哲学:
- Checked Exception:被认为是“可预见的、可恢复的”业务级异常,程序应当捕获并采取补偿措施,所以不强制回滚事务。
- RuntimeException / Error:被认为是“不可恢复的、非预期的”程序 BUG 或系统故障,因此必须回滚以保证数据一致性。
如何让普通的 Exception 也触发回滚?
在实际的业务开发中,我们通常希望只要发生任何异常,都直接回滚事务。为了改变默认行为,你需要使用 @Transactional 注解的 rollbackFor 属性:
java
// 显式指定 rollbackFor 为 Exception.class
// 这样无论是 RuntimeException 还是普通的 Exception 都会触发事务回滚
@Transactional(rollbackFor = Exception.class)
public void myServiceMethod() throws Exception {
// 数据库操作 1
// 抛出普通的受检异常,事务将会回滚
throw new Exception("发生了一个普通异常");
// 数据库操作 2
}
最佳实践:
在绝大多数现代项目的日常开发中,强烈建议在类或方法上统一加上 @Transactional(rollbackFor = Exception.class),以避免因为抛出 Checked Exception 而导致的数据不一致问题(比如常见的由于 SQLException 没有回滚导致产生了脏数据)。
右滑查看面试常问