为何EntityGraph在@DataJpaTest环境下无法正常工作?
使用@DataJpaTest时@OneToMany关联集合为null,但@SpringBootTest正常的原因及解决办法
核心原因分析
你的问题源于两个关键问题:双向关联映射配置错误和**@DataJpaTest的事务缓存特性**,二者共同导致了关联集合无法被正确填充。
1. @OneToMany的mappedBy配置错误
ParentEntity中@OneToMany(mappedBy = "parentId")的配置是错误的:
mappedBy属性需要指定子实体中关联父实体的实体字段名,而非数据库列名。- 你的ChildEntity中关联父实体的字段是
parentEntity,而非parentId(parentId只是单独的数据库映射列)。
这个错误会让JPA无法正确识别双向关联关系,在@SpringBootTest中可能因事务提交后重新加载实体的巧合生效,但在@DataJpaTest的同事务缓存场景下,关联绑定会直接失败。
2. @DataJpaTest的事务缓存特性
@DataJpaTest默认会为每个测试方法包裹一个事务,且整个测试流程都在同一个事务上下文内:
- 你创建并保存ParentEntity时,该实例会被存入Hibernate的一级缓存(会话缓存),此时它的
childEntities为null。 - 后续保存关联的ChildEntity时,缓存中的Parent实例不会自动更新关联集合(除非手动维护或配置级联)。
- 执行
findById时,Hibernate会直接返回缓存中的旧实例,即便查询语句包含左外连接,也不会用新的查询结果更新缓存对象的关联字段。
而@SpringBootTest场景下,要么事务提前提交导致查询在新会话中执行,要么上下文加载特性让缓存失效,因此能从数据库重新加载完整的关联数据。
解决方案
方案1:修正双向关联映射(优先推荐)
修改ParentEntity中的@OneToMany注解,将mappedBy指向正确的实体字段:
@OneToMany(mappedBy = "parentEntity") Collection<ChildEntity> childEntities;
这是双向关联的标准配置,能让JPA正确识别并维护父子实体的关联关系。
方案2:清除一级缓存,强制从数据库加载
注入EntityManager,在查询前清除缓存,让JPA重新从数据库加载实体:
@Autowired EntityManager entityManager; @Test void test(){ // ... 原有创建、保存代码 // 清除一级缓存 entityManager.clear(); ParentEntity returnedParent = parentRepository.findById(parentId).get(); assertThat(returnedParent.getChildEntities()).hasSize(1); }
或者在获取实体后调用refresh方法刷新缓存:
ParentEntity returnedParent = parentRepository.findById(parentId).get(); entityManager.refresh(returnedParent);
方案3:调整事务传播策略
给测试方法添加@Transactional(propagation = Propagation.REQUIRES_NEW),让查询操作在新事务中执行,绕过原有缓存:
@Commit @Rollback(false) @Test @Transactional(propagation = Propagation.REQUIRES_NEW) void test(){ // ... 原有代码 }
内容的提问来源于stack exchange,提问作者Nico S.
相关产品推荐
相关产品推荐

