如何用@DataJpaTest测试@Transactional事务回滚机制?
问题:@DataJpaTest中无法测试@Transactional的事务回滚机制
场景复现
在本地PostgreSQL环境中运行时,带有@Transactional注解的Controller方法会在抛出RuntimeException时正确回滚事务(数据库表为空),但使用@DataJpaTest搭配H2数据库测试时,事务回滚未生效,测试失败——执行完方法后能查询到状态为FAIL的实体。
核心代码示例
Controller类
@RestController public class MyController { private final MyRepository myRepository; private final MyService myService; public MyController(MyRepository myRepository, MyService myService) { this.myRepository = myRepository; this.myService = myService; } @GetMapping @Transactional public String justForTest() { var myEntity = new MyEntity().setStatus("NEW"); myRepository.save(myEntity); try { myService.throwsAnRuntimeException(); // 服务调用成功时更新状态 myEntity.setStatus("SUCCESS"); } catch (Exception e) { // 服务调用失败时更新状态 myEntity.setStatus("FAIL"); throw e; } finally { myRepository.save(myEntity); } return "this is the end"; } }
实体类
@Data @Entity @Accessors(chain = true) public class MyEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String status; }
Repository接口
@Repository public interface MyRepository extends JpaRepository<MyEntity, Long> { }
Service类
@Service public class MyService { public void throwsAnRuntimeException() { throw new RuntimeException("Rollbacks not working!"); } }
测试类(原失败版本)
@DataJpaTest class MyControllerTest { @Autowired private MyRepository repository; @Test void justForTest() { var controller = new MyController(repository, new MyService()); assertThrows(RuntimeException.class, controller::justForTest); var afterTestEntities = repository.findAll(); System.out.println(afterTestEntities.getFirst()); assertEquals(0, afterTestEntities.size()); } }
失败原因
@DataJpaTest是Spring的切片测试注解,仅加载JPA相关组件(Repository、EntityManager等),不会自动加载@RestController和@Service类。你手动new出来的MyController对象并非Spring容器管理的代理对象,因此方法上的@Transactional注解完全不生效——所有数据库操作都在测试方法自带的事务中执行,而测试方法的事务默认会在测试结束后回滚,但在测试执行过程中,同一个事务内可以查询到未提交的实体数据,导致断言失败。
解决方案
要让@Transactional注解生效,必须让MyController和MyService被Spring容器管理,从而生成带有事务代理的Bean。修改测试类如下:
@DataJpaTest @AutoConfigureWebMvc // 加载WebMvc相关配置,支持Controller注入 @Import({MyController.class, MyService.class}) // 手动导入需要的Bean class MyControllerTest { @Autowired private MyController controller; // 注入Spring管理的代理对象 @Autowired private MyRepository repository; @Test void justForTest() { assertThrows(RuntimeException.class, controller::justForTest); // Controller的事务已回滚,数据库无数据 assertEquals(0, repository.findAll().size()); } }
原理说明
@Import注解将MyController和MyService纳入Spring容器管理,Spring会为带有@Transactional注解的方法生成代理对象;- 调用代理对象的
justForTest方法时,Spring会开启独立事务,方法抛出RuntimeException后自动回滚所有数据库操作; - 测试方法查询数据库时,由于Controller的事务已回滚,因此查询结果为空,断言通过。
内容的提问来源于stack exchange,提问作者snej0keee
相关产品推荐
相关产品推荐

