1、运行时异常和error
spring事务默认回滚 error和运行时异常
为何不回滚 非运行时异常?
![]()
Spring 事务默认仅回滚 Error 和运行时异常(Unchecked Exception),不回滚非运行时异常(Checked Exception),核心原因是:Spring 遵循 “异常语义设计”+“Java 异常体系的约定”,将 Checked Exception 视为 “可预期、可处理的业务异常”,而非 “需要中断事务的系统异常”。下面从根源拆解原因、异常体系划分、以及如何调整回滚规则:
要理解 Spring 的设计,首先要分清 Java 两类异常的语义差异(这是 Spring 事务规则的底层依据):
Java 设计 Checked Exception 的初衷是:让开发者必须显式处理这类异常(捕获 / 声明抛出),因为它们是 “业务执行中可预见的失败场景”,而非 “系统崩溃级错误”。
Spring 延续了这一语义:
- 比如
SQLException(数据库操作失败):开发者在代码中可以捕获它,做重试、降级、记录日志等处理,不一定需要回滚整个事务;
- 比如
IOException(文件读取失败):可能只是业务流程中 “文件不存在”,开发者可选择跳过该步骤,而非中断整个事务;
- 而运行时异常(如 NPE)、Error(如 OOM)是 “不可预见、无法处理的系统错误”,此时事务已无继续执行的意义,Spring 默认回滚。
Spring 事务的设计目标是:仅在 “事务无法继续执行” 的极端情况下回滚,避免因可处理的业务异常导致事务回滚,降低业务逻辑的灵活性。
举个典型场景:
@Transactional
public void createOrder() throws IOException {
- 若
writeLog 抛 IOException(Checked Exception):Spring 不回滚事务 → 订单数据保留,开发者可捕获异常后手动补写日志;
- 若
writeLog 抛 NullPointerException(运行时异常):Spring 自动回滚 → 订单数据删除,因为这是不可预期的系统错误,继续保留订单会导致数据不一致。
数据库自身的事务回滚,也仅在 “致命错误(如连接中断、SQL 语法错误导致执行失败)” 时触发,而 “可预期的 SQL 异常(如主键重复)” 需要开发者手动决定是否回滚。Spring 对 Checked Exception 的处理,与数据库的这一行为保持一致。
Spring 事务的回滚逻辑由 TransactionAspectSupport 类的 completeTransactionAfterThrowing 方法实现,核心代码逻辑简化如下:
protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {
if (txInfo != null && txInfo.getTransactionStatus() != null) {
其中 rollbackOn(ex) 方法的默认实现(DefaultTransactionAttribute 类):
public boolean rollbackOn(Throwable ex) {
可见:Spring 源码中明确将 Checked Exception(非 RuntimeException/Error)排除在默认回滚范围外。
若业务需要让 Checked Exception 触发事务回滚,有两种常用方式:
@Transactional
public void createOrder() throws IOException {
try {
若想让某些运行时异常不回滚,可使用 noRollbackFor:
Spring 的这一设计,本质是将 “异常是否需要回滚” 的决策权交给开发者:
- 对于不可控的系统异常(运行时异常 / Error),自动回滚以保证数据一致性;
- 对于可控的业务异常(Checked Exception),由开发者决定是否回滚(通过
rollbackFor 或手动回滚),兼顾灵活性。
这也是为什么在实际开发中,规范会建议:使用 @Transactional 时显式指定 rollbackFor = Exception.class(避免因遗漏 Checked Exception 导致事务未回滚)。