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传播行为,会加入外层测试事务。当业务方法抛出异常标记回滚时,外层测试事务因未抛出异常,最终会提交事务,导致业务方法的回滚标记被忽略,数据被保留。
解决方案
让测试方法脱离事务控制
在测试方法上添加@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()); }显式配置事务传播行为
也可修改业务方法的事务传播行为为REQUIRES_NEW,强制创建独立事务,不受外层测试事务影响:@Transactional(propagation = Propagation.REQUIRES_NEW) public void startTransaction(boolean throwException) { // 原有逻辑不变 }验证基础配置
- 确认
TransactionA实体已正确添加@Entity、@Id等JPA注解,确保save操作有效写入数据库。 - 检查
@Sql脚本中无手动提交事务的语句,避免干扰事务逻辑。
- 确认
内容的提问来源于stack exchange,提问作者theMyth
相关产品推荐
相关产品推荐

