Spring事务回滚默认仅对RuntimeException和Error生效,受检异常需编程式管理;推荐用TransactionTemplate调用setRollbackOnly()或抛异常实现精准回滚,避免代理失效、吞异常等常见错误。

在 Spring 中,事务回滚默认只对 未检查异常(RuntimeException 及其子类)和 Error 自动生效;而受检异常(如 IOException、SQLException)不会触发自动回滚。若想通过捕获特定异常(包括受检异常或自定义异常)来主动控制事务回滚,需使用编程式事务管理(TransactionStatus + PlatformTransactionManager),而非仅依赖声明式注解(如 @Transactional)。
用 TransactionTemplate 主动标记回滚
这是最简洁、推荐的编程式回滚方式。它封装了事务模板逻辑,避免手动管理 commit/rollback,只需在捕获目标异常后调用 TransactionStatus.setRollbackOnly() 或直接抛出带回滚语义的异常。
- 配置
TransactionTemplate(通常通过@Autowired注入) - 在业务方法中使用
execute()执行事务性逻辑 - 在
TransactionCallback内捕获你关心的异常(如BusinessException、IOException) - 捕获后调用
status.setRollbackOnly(),或直接throw new RuntimeException("需要回滚", e)
示例:
<!-- Java 示例 -->public void transferMoney(Long fromId, Long toId, BigDecimal amount) {
transactionTemplate.execute(status -> {
try {
accountDao.withdraw(fromId, amount);
accountDao.deposit(toId, amount);
return null;
} catch (InsufficientBalanceException e) { // 自定义受检异常
status.setRollbackOnly(); // 明确标记当前事务回滚
throw new RuntimeException(e); // 或记录日志后重新包装
}
});
}手动管理 PlatformTransactionManager(更底层,慎用)
适用于需要精细控制事务生命周期(如嵌套事务、超时设置、隔离级别动态变更)的场景。但容易遗漏 commit() 或 rollback() 导致连接泄漏或数据不一致。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
立即学习“Java免费学习笔记(深入)”;
- 调用
transactionManager.getTransaction(def)获取TransactionStatus - 在
try块中执行数据库操作 - 在
catch中调用transactionManager.rollback(status) - 在
finally或try成功分支中调用transactionManager.commit(status)
注意:必须确保 rollback() 和 commit() 有且仅有一个被执行,建议配合 TransactionSynchronizationManager.isActualTransactionActive() 判断。
结合 @Transactional 的“半声明式”技巧(不推荐用于核心回滚逻辑)
若仍想用 @Transactional,可通过 rollbackFor 属性显式指定哪些异常触发回滚,包括受检异常:
@Transactional(rollbackFor = { IOException.class, BusinessException.class })
public void doSomething() throws IOException {
// ...
if (somethingWrong) {
throw new IOException("触发回滚");
}
}⚠️ 但该方式要求异常**向上抛出**,不能在方法内 catch 后吞掉——否则 Spring 无法感知异常,事务不会回滚。所以它不满足“捕获后决定是否回滚”的需求,仅适用于异常可透传的场景。
关键细节与避坑提醒
- 不要在同一个类中调用本类的 @Transactional 方法:因代理失效,事务不生效,回滚设置无效
- setRollbackOnly() 是标记式操作,不是立即回滚:它只是通知事务管理器“这个事务必须回滚”,实际回滚发生在事务边界退出时
-
捕获异常后务必重抛或标记:如果
catch住异常又不处理、不标记、也不抛出,事务会正常提交 - 避免在事务中混用 JDBC Connection 和 JPA EntityManager 手动操作:可能绕过 Spring 事务同步机制,导致回滚失败


















