首页 > 解决方案 > 使用 HQL 急切加载嵌套关联

问题描述

我有以下模型:

public class BaseModel {
  List<DataA> lazyCollectionA;
  List<DataB> lazyCollectionB;
}

public class DataA {
  OtherEntity otherEntity;
}

public class OtherEntity {
  List<DataC> lazyCollectionC;
}

当我访问特定页面时,我需要使用所有这些数据。这正在创建一个性能选择 n+1 问题。

我已经通过急切地使用以下方法获取集合来部分解决了这个问题:

List<BaseModel> result = entityManager.createQuery(
    "select m from BaseModel m " +
    "left join fetch m.lazyCollectionA " +
    "where m.id in (:ids) ", BaseModel.class)
.setParameter("ids", ids)
.getResultList();

result = entityManager.createQuery(
    "select m from BaseModel m " +
    "left join fetch m.lazyCollectionB " +
    "where m.id in (:ids) ", BaseModel.class)
.setParameter("ids", ids)
.getResultList();

请注意,我必须执行 2 个查询而不是仅 1 个,否则我会得到一个MultipleBagFetchException.

但是,我急切地加载lazyCollectionA.otherEntity.lazyCollectionC. 我尝试了几种查询变体来尝试急切地获取结果,但是当otherEntity.lazyCollectionC被访问时,选择 n+1 问题不断浮出水面。

我认为这应该可行,但不幸的是它不是:

entityManager.createQuery(
    "select a from BaseModel m " +
    "left join m.lazyCollectionA a " +
    "left join fetch a.otherEntity o " +
    "left join fetch o.lazyCollectionC " +
    "where m.id in (:ids) ", BaseModel.class)
.setParameter("ids", ids)
.getResultList();

你有什么想法为什么这不起作用?

此外,我不完全了解我的前 2 个查询是如何加载lazyCollectionAlazyCollectionB工作的。我的意思是,由于它们是在不同时间加载的,我希望只有最后一个查询才会有加载的实例。是不是因为hibernate正在缓存结果,因此不需要再次查询数据库?

感谢您的任何帮助,您可以提供!

标签: javahibernatejpaone-to-manyeager-loading

解决方案


我假设您的模型之间的所有连接都是@OneToMany。在这种情况下,您可以尝试这样的事情:

@Autowired
private EntityManager em;

@Transactional
public List<BaseModel> getAllByThreeQueries() {
    List<Long> ids = Arrays.asList(1L);
    List<BaseModel> first = em.createQuery(
            "select distinct m from BaseModel m " +
                    "left join fetch m.lazyCollectionB " +
                    "where m.id in (:ids) ", BaseModel.class)
            .setParameter("ids", ids)
            .getResultList();
    List<BaseModel> second = em.createQuery(
            "select distinct m from BaseModel m " +
                    "left join fetch m.lazyCollectionA a " +
                    "left join fetch a.otherEntity o " +
                    "where m in (:models) ", BaseModel.class)
            .setParameter("models", first)
            .getResultList();
    em.createQuery("select distinct a from BaseModel m " +
            "left join m.lazyCollectionA a " +
            "left join fetch a.otherEntity o " +
            "left join fetch o.lazyCollectionC " +
            "where m in (:models) ", DataA.class)
            .setParameter("models", second)
            .getResultList();
    return second;
}

完整代码

你有什么想法为什么这不起作用?

entityManager.createQuery(
    "select a from BaseModel m " +
    "left join m.lazyCollectionA a " +
    "left join fetch a.otherEntity o " +
    "left join fetch o.lazyCollectionC " +
    "where m.id in (:ids) ", BaseModel.class)
.setParameter("ids", ids)
.getResultList();

因为在这种情况下你会得到一个 MultipleBagFetchException。你需要再做一个请求。


推荐阅读