SpringBootTest事务管理问题:RestAssured调用接口无法读取H2持久化数据
问题原因分析
你的问题核心是事务隔离与线程上下文不匹配导致的:
- 测试方法标注了
@Transactional,Spring会为整个测试流程(包括@BeforeEach)绑定一个线程专属事务,这个事务默认在测试结束前不会提交。 - RestAssured通过HTTP请求调用接口时,接口代码运行在独立的应用线程中,根据数据库ACID特性,未提交的事务数据对其他线程完全不可见,因此接口无法查询到测试数据。
- 当设置
@Rollback(false)时,测试方法的事务会在测试结束后提交,此时接口线程就能读取到已持久化的数据。
解决方案
方案1:移除测试方法的@Transactional注解
直接去掉测试方法上的事务注解,@BeforeEach中通过TransactionTemplate执行的插入操作会自动提交事务,接口线程可直接读取到已提交的数据:
@Test // 移除@Transactional和@Rollback注解 void test() { System.out.println(messageRepo.findAll()); // 依然能正常获取数据 get("/messages").then().assertThat().body("$", hasItems(67)).and().statusCode(eq(200)); // 接口现在可查询到数据 }
方案2:让@BeforeEach的事务独立提交
如果需要保留测试方法的@Transactional(比如测试后自动回滚数据,避免污染测试环境),可以给@BeforeEach方法添加@Transactional(propagation = Propagation.REQUIRES_NEW),强制开启独立事务并自动提交:
// 先导入Propagation类 import org.springframework.transaction.annotation.Propagation; @BeforeEach @Transactional(propagation = Propagation.REQUIRES_NEW) // 开启独立事务,执行后自动提交 void beforeAll() throws IOException, URISyntaxException { RestAssured.baseURI = "http://localhost/webservice"; RestAssured.port = port; final List<MessageEntity> messages = setupMessages(); messageRepo.saveAllAndFlush(messages); // 无需再用TransactionTemplate,事务由注解管理 }
同时可以删除测试类中PlatformTransactionManager的注入和TransactionTemplate相关代码,简化实现。
方案3:手动配置TransactionTemplate的事务传播行为
如果坚持使用TransactionTemplate,可以通过设置传播行为强制开启独立事务,确保插入操作完成后事务提交:
@BeforeEach void beforeAll() throws IOException, URISyntaxException { RestAssured.baseURI = "http://localhost/webservice"; RestAssured.port = port; TransactionTemplate transactionTemplate = new TransactionTemplate(this.platformTransactionManager); // 设置为独立事务,执行后自动提交 transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); final List<MessageEntity> messages = setupMessages(); transactionTemplate.execute((TransactionCallback<Object>) status -> { messageRepo.saveAllAndFlush(messages); return null; }); }
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

