You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

@Transactional方法执行后抛RuntimeException致未提交的原因及解决

问题:非事务方法调用事务方法后抛异常,事务未提交的原因与解决方案

场景还原

非事务方法调用被@Transactional修饰的事务方法,事务方法执行完成后抛出RuntimeException,出现更新SQL已执行flush但未提交的异常情况。

代码结构

ValidateCodeService类:

public void callingMethod(){
   // 业务逻辑
   if (codeReceivedFromUser != codeStoredInDB){
      codeDTO.setNoOfRetries(codeDTO.getNoOfRetries() - 1);
      codeRepository.update(codeDTO);
      throw new RuntimeException(CodeError.CDE1);
   }
}

CodeRepository类:

public class CodeRepository extends SuperRepository<Code, Integer> {
    @Transactional 
    public CodeDTO update(CodeDTO dto){
         // 业务逻辑
         super.merge(TransformDTOToEntity(dto));
         return dto;
    }
}

SuperRepository类:

public class SuperRepository<T> {
    public void merge(T entity){
         getEm.merge(entity);
         getEm.flush();
    }
}

日志现象

  • 抛异常场景日志:

    hibernate flush
    [my update query]
    log error
    [my CodeError.CDE1]
    SQL已执行flush但未提交,直接触发报错。

  • 无异常场景日志:

    hibernate flush
    [my update query]
    hibernate commit
    JDBC commit
    更新操作正常提交。

问题原因

Spring @Transactional的默认规则是:方法正常执行完毕时提交事务;若方法抛出未捕获的RuntimeException,则触发事务回滚。

核心在于事务传播机制:非事务方法callingMethod调用带@Transactional的update方法时,Spring会为update创建新事务。但update执行完成后事务尚未提交,上层callingMethod就抛出了RuntimeException——该异常会被Spring事务管理器捕获,触发事务回滚,因此即使执行了flush,最终也不会提交。

而无异常场景下,update执行完成后无异常抛出,Spring会正常提交事务,因此能看到commit日志。

解决方法:确保更新在抛异常前提交

要让update的事务独立于上层异常完成提交,可采用以下两种方式:

1. 修改事务传播级别(推荐)

将update方法的@Transactional设置为REQUIRES_NEW传播级别,强制创建独立新事务,该事务会在update执行完成后立即提交,不受上层方法异常影响:

public class CodeRepository extends SuperRepository<Code, Integer> {
    @Transactional(propagation = Propagation.REQUIRES_NEW) 
    public CodeDTO update(CodeDTO dto){
         // 原有业务逻辑
         super.merge(TransformDTOToEntity(dto));
         return dto;
    }
}

REQUIRES_NEW的作用是:每次调用该方法都会开启新的独立事务,若当前存在其他事务则会被挂起;新事务执行完毕后立即提交,与上层事务或异常完全隔离。

2. 手动控制事务提交(不推荐)

若无法修改事务传播级别,可在update方法中手动提交事务,但这种方式会脱离Spring声明式事务管理,增加维护成本:

public class CodeRepository extends SuperRepository<Code, Integer> {
    @Transactional 
    public CodeDTO update(CodeDTO dto){
         // 原有业务逻辑
         super.merge(TransformDTOToEntity(dto));
         // 手动提交事务
         getEm.getTransaction().commit();
         return dto;
    }
}

注意:手动提交后,后续事务操作需重新开启,易引发事务管理混乱,仅适用于特殊场景。


内容的提问来源于stack exchange,提问作者Kusai

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 21:13:27