You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:43:06