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

Spring Boot中@Transactional抛出RuntimeException后未回滚问题排查

Spring Boot事务回滚失败排查与解决

问题描述

在Spring Boot中为方法添加@Transactional注解后,抛出RuntimeException时事务未自动回滚。已排除同一类内调用、非RuntimeException抛出等常见问题,不确定是否需在测试中显式配置PlatformTransactionManager,或遗漏了关键配置逻辑。

相关代码

Service类

@Service
public class BasicTransactionExample {

    private TransactionARepository aRepository;
    private TransactionBRepository bRepository;

    public BasicTransactionExample(TransactionARepository aRepository, TransactionBRepository bRepository) {
        this.aRepository = aRepository;
        this.bRepository = bRepository;
    }

    @Transactional
    public void startTransaction(boolean throwException) {
        TransactionA t1 = new TransactionA();
        t1.setName("TransactionA name");
        t1.setValue(10);
        aRepository.save(t1);

        if (throwException) {
            throw new RuntimeException("Deliberate exception thrown!");
        }
    }
}

测试类

@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Sql(scripts = "classpath:db/MiscSchema.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Import(BasicTransactionExample.class)
class BasicTransactionExampleTest {

    @Autowired
    private TransactionARepository aRepository;
    @Autowired
    private TransactionBRepository bRepository;
    @Autowired
    private BasicTransactionExample basicTransactionExample;

    @BeforeEach
    void setUp() { }

    @Test
    void testOneWithoutException() {
        basicTransactionExample.startTransaction(false);
        List<TransactionA> aList = aRepository.findAll();
        assertEquals(1, aList.size()); // 执行正常
    }

    @Test
    void testThrows() {
        assertThrows(RuntimeException.class, () -> basicTransactionExample.startTransaction(true));
        List<TransactionA> aList = aRepository.findAll();
        assertEquals(0, aList.size()); // 测试失败:实际数量为1,预期为0
    }
}

问题分析与解决

核心原因:@DataJpaTest的事务默认行为

@DataJpaTest会为每个测试方法自动创建外层事务,默认在测试结束后回滚。但业务方法的@Transactional默认使用REQUIRED传播行为,会加入外层测试事务。当业务方法抛出异常标记回滚时,外层测试事务因未抛出异常,最终会提交事务,导致业务方法的回滚标记被忽略,数据被保留。

解决方案

  1. 让测试方法脱离事务控制
    在测试方法上添加@Transactional(propagation = Propagation.NOT_SUPPORTED),使测试不参与事务,业务方法的事务独立执行,抛出异常时正常回滚:

    @Test
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    void testThrows() {
        assertThrows(RuntimeException.class, () -> basicTransactionExample.startTransaction(true));
        List<TransactionA> aList = aRepository.findAll();
        assertEquals(0, aList.size());
    }
    
  2. 显式配置事务传播行为
    也可修改业务方法的事务传播行为为REQUIRES_NEW,强制创建独立事务,不受外层测试事务影响:

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void startTransaction(boolean throwException) {
        // 原有逻辑不变
    }
    
  3. 验证基础配置

    • 确认TransactionA实体已正确添加@Entity、@Id等JPA注解,确保save操作有效写入数据库。
    • 检查@Sql脚本中无手动提交事务的语句,避免干扰事务逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 15:17:44