
本文详解 spring `@transactional` 注解默认的回滚机制,说明为何无需显式配置 `rollbackfor`,强调 spring 管理的 `entitymanager` 是事务生效的前提,并提供安全、规范的代码实践。
在 Spring 数据访问层中,确保数据库操作的原子性是事务管理的核心目标。当一个被 @Transactional 标注的方法中执行多条 SQL(如本例中的两条级联删除),若其中任意一条抛出异常,整个事务必须自动回滚——否则将导致数据不一致。
幸运的是,Spring 的声明式事务默认已具备该能力:只要方法内抛出未被捕获的 unchecked exception(即继承自 RuntimeException 的异常),事务将自动标记为 rollback-only,并在方法退出时回滚所有变更。JPA 原生查询(createNativeQuery().executeUpdate())在执行失败时(如 SQL 语法错误、约束冲突、连接中断等)通常抛出 PersistenceException 及其子类(如 SQLGrammarException、ConstraintViolationException),而这些均属于 RuntimeException 体系,因此完全匹配 Spring 默认回滚策略。
✅ 正确做法:移除冗余 try-catch,让异常自然向上抛出
❌ 错误做法:如原代码中用空 catch 捕获异常并调用 e.getCause() 却不重新抛出——这将导致事务无法感知异常,从而跳过回滚,造成静默数据损坏。
此外,事务生效依赖关键前提:EntityManager 必须由 Spring 容器管理并注入(而非手动 new 或静态获取)。否则,即使加了 @Transactional,也无法绑定到当前事务上下文。推荐使用 @PersistenceContext 注入:
@Service
@Transactional
public class ExecucaoFinanceiraService {
@PersistenceContext
private EntityManager entityManager;
public void deleteDadosSExecReenvCancelada(Long nuSeqConsecao) {
// 第一条删除:子表 s_execucao_financ_gru_reenv
String sql1 = """
DELETE FROM sigpc_fnde.s_execucao_financ_gru_reenv
WHERE NU_SEQ_EXEC_FINANC_REENV IN (
SELECT NU_SEQ_EXECUCAO_FINANCEIRA
FROM sigpc_fnde.s_exec_financ_reenv
WHERE NU_SEQ_CONCESSAO = ?
)
""";
entityManager.createNativeQuery(sql1).setParameter(1, nuSeqConsecao).executeUpdate();
// 第二条删除:主表 s_exec_financ_reenv
String sql2 = "DELETE FROM sigpc_fnde.s_exec_financ_reenv WHERE NU_SEQ_CONCESSAO = ?";
entityManager.createNativeQuery(sql2).setParameter(1, nuSeqConsecao).executeUpdate();
// 若任一 executeUpdate() 抛出 RuntimeException,事务自动回滚
}
}⚠️ 注意事项:
- 不要捕获 RuntimeException 并“吞掉”它(如 catch (Exception e) { }),这是最常见的回滚失效原因;
- @Transactional(rollbackFor = Exception.class) 仅在需对 checked exception(如 IOException)触发回滚时才需显式声明,本例完全不需要;
- 确保该方法由 Spring 代理对象调用(即避免同类内 self-invocation),否则事务注解不生效;
- 原生 SQL 删除建议添加 WHERE 条件验证与日志记录,便于问题追溯;
- 生产环境应配合 @Transactional(timeout = 30) 设置超时,防止长事务阻塞资源。
总结:Spring 的事务回滚机制开箱即用,关键在于信任默认行为、杜绝异常屏蔽、确保资源受 Spring 管理。遵循以上原则,即可零配置实现强一致性保障。










