Spring DataJpaTest中测试懒加载集合是否加载遇到问题求助
解决Spring Data JpaTest中懒加载集合测试的问题
嗨,我来帮你捋清楚这个问题——你之所以检测到懒加载集合已经被加载,核心原因是整个测试方法运行在同一个事务中,而且保存实体时已经给集合赋了值,导致查询到的是一级缓存里已经初始化过的实例。
为什么会出现这个情况?
@DataJpaTest默认会让整个测试方法跑在一个事务内,全程共享同一个持久化上下文(也就是Hibernate的一级缓存)。- 你在保存
Example时,已经通过example.setCollection(Utils.generateCollection())给集合塞了数据,Hibernate会把这个集合包装成PersistentBag,并且直接标记为已初始化(因为集合本身已经有内容了,根本不需要触发懒加载去数据库查)。 - 当你调用
repository.findAllActive()时,Spring Data JPA会优先从一级缓存里拿实体,而不是去数据库重新查询,所以拿到的还是那个集合已经初始化好的实例,自然所有检测方法都返回true。
正确测试懒加载的几种方法
要验证懒加载是否真的生效,你得让查询操作脱离之前的持久化上下文,确保拿到的是未初始化的集合。这里给你几个可行的方案:
方案1:拆分保存和查询的事务
把保存实体和查询实体的操作放到不同的事务里,这样查询时会从数据库重新获取实体,而不是用缓存里的:
@RunWith(SpringRunner.class) @DataJpaTest @AutoConfigureTestDatabase(replace = Replace.NONE) public class ExampleRepositoryTest { @Autowired private ExampleRepository repository; @Autowired private TestEntityManager testEntityManager; @Test public void testLazyCollectionNotLoaded() { // 第一步:在独立事务中保存实体 Long exampleId; var transaction = testEntityManager.getEntityManager().getTransaction(); transaction.begin(); try { Example example = new Example(); example.setCollection(Utils.generateCollection()); Example saved = repository.save(example); exampleId = saved.getId(); transaction.commit(); } finally { if (transaction.isActive()) { transaction.rollback(); } } // 第二步:在新的上下文查询实体 Example fetched = repository.findById(exampleId).orElseThrow(); // 检测集合是否未初始化 var persistenceUnitUtil = testEntityManager.getEntityManager().getEntityManagerFactory().getPersistenceUnitUtil(); Assertions.assertFalse(persistenceUnitUtil.isLoaded(fetched, "collection")); Assertions.assertFalse(Hibernate.isInitialized(fetched.getCollection())); } }
方案2:手动清除一级缓存
在查询之前,把持久化上下文的缓存清空,强制查询去数据库捞新的实体:
@Test public void testLazyCollectionNotLoaded() { Example example = new Example(); example.setCollection(Utils.generateCollection()); Example saved = repository.save(example); Long exampleId = saved.getId(); // 清除一级缓存,让后续查询从数据库获取 testEntityManager.getEntityManager().clear(); // 重新查询实体 Example fetched = repository.findById(exampleId).orElseThrow(); var persistenceUnitUtil = testEntityManager.getEntityManager().getEntityManagerFactory().getPersistenceUnitUtil(); Assertions.assertFalse(persistenceUnitUtil.isLoaded(fetched, "collection")); Assertions.assertFalse(Hibernate.isInitialized(fetched.getCollection())); }
方案3:分离已保存的实体
把保存后的实体从持久化上下文中分离出去,这样后续查询就不会用缓存里的实例了:
@Test public void testLazyCollectionNotLoaded() { Example example = new Example(); example.setCollection(Utils.generateCollection()); Example saved = repository.save(example); Long exampleId = saved.getId(); // 把实体从一级缓存中移除 testEntityManager.getEntityManager().detach(saved); // 重新查询实体 Example fetched = repository.findById(exampleId).orElseThrow(); var persistenceUnitUtil = testEntityManager.getEntityManager().getEntityManagerFactory().getPersistenceUnitUtil(); Assertions.assertFalse(persistenceUnitUtil.isLoaded(fetched, "collection")); Assertions.assertFalse(Hibernate.isInitialized(fetched.getCollection())); }
额外小提示
- 你看到的
PersistentBag是Hibernate对集合的包装类,但如果集合在持久化时已经被赋值,它就会直接包含实际数据,而不是未初始化的代理对象。 - 懒加载的本质是:当实体处于分离状态,或者在新的持久化上下文中首次被获取时,集合不会自动加载,只有当你调用集合的方法(比如
size()、get(0))时,才会触发数据库查询。
内容的提问来源于stack exchange,提问作者Janar
相关产品推荐
相关产品推荐

