@DataJpaTest中Insert语句未执行的SpringBoot测试问题求助
首先咱们拆解下问题的核心:
@DataJpaTest默认会给每个测试方法包裹一个事务,测试结束后自动回滚。而JPA的save()只是把实体加入EntityManager的持久化上下文,不会立刻触发数据库的Insert操作——只有事务提交或者调用flush()时,排队的SQL才会真正执行到数据库里。
你遇到的情况是:同一个事务内save()后直接findAll(),此时如果JPA直接执行数据库查询(而非从一级缓存取数据),就会因为Insert还没落地数据库而返回空列表;但手动调用flush()又报错“无活跃事务”,这大概率是事务上下文没有正确绑定导致的。
下面给你几个可行的解决思路:
方法一:用TestEntityManager的persistAndFlush一步到位
TestEntityManager是专门为测试场景设计的,它的persistAndFlush()方法能直接完成实体持久化并刷新到数据库,不需要额外手动flush:
@Test public void shouldReturnAllEntities() { MyEntity testEntity = new MyEntity(); entityManager.persistAndFlush(testEntity); // 替换myRepository.save() assertEquals(1, myRepository.findAll().size()); }
这样操作后,Insert语句会立刻执行到数据库,后续的findAll()查询就能拿到数据。
方法二:调用Repository自带的flush方法
你之前调用flush报错,可能是直接操作EntityManager导致的?试试用Spring Data JPA Repository自带的flush()方法,它会确保操作在当前事务上下文内执行:
@Test public void shouldReturnAllEntities() { MyEntity testEntity = new MyEntity(); myRepository.save(testEntity); myRepository.flush(); // 用Repository的flush,避免无事务问题 assertEquals(1, myRepository.findAll().size()); }
方法三:显式用TransactionTemplate控制事务
如果上面的方法还是不行,可以显式用TransactionTemplate来包裹操作,确保所有持久化和查询都在同一个事务里:
@Autowired private TransactionTemplate transactionTemplate; @Test public void shouldReturnAllEntities() { transactionTemplate.execute(status -> { MyEntity testEntity = new MyEntity(); myRepository.save(testEntity); myRepository.flush(); assertEquals(1, myRepository.findAll().size()); return null; }); }
方法四:检查@AutoConfigureEmbeddedDatabase的配置
确认你使用的@AutoConfigureEmbeddedDatabase是否显式指定了PostgreSQL类型,避免默认用H2带来的兼容性问题:
@RunWith(SpringRunner.class) @DataJpaTest @AutoConfigureEmbeddedDatabase(type = EmbeddedDatabaseType.POSTGRESQL) // 显式指定数据库类型 public class MyRepositoryTest { // ... 测试代码 }
有些嵌入式数据库的事务配置可能影响SQL执行时机,显式指定类型能规避这类问题。
内容的提问来源于stack exchange,提问作者Cécile Fecherolle

