使用 HQL 急切加载嵌套关联
Posted
技术标签:
【中文标题】使用 HQL 急切加载嵌套关联【英文标题】:Eagerly load nested association using HQL 【发布时间】:2020-10-11 13:44:00 【问题描述】:我有以下型号:
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();
您知道为什么这不起作用吗?
另外,我不完全了解加载 lazyCollectionA
和 lazyCollectionB
的前 2 个查询是如何工作的。我的意思是,由于它们是在不同时间加载的,我希望只有最后一个查询才会有加载的实例。是不是因为hibernate正在缓存结果,所以不需要再次查询数据库?
感谢您提供的任何帮助!
【问题讨论】:
【参考方案1】:我假设您的模型之间的所有连接都是@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;
Full code
您知道为什么这不起作用吗?
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。 You need to do one more request.
【讨论】:
感谢您的贡献!不幸的是,它对我不起作用。用于获取lazyCollectionC
的select n+1 查询仍在执行。我注意到这些查询使用WHERE dataC.otherEntity_id = ?
。我试图复制这个 where 子句,但仍然出现 n+1 个查询。
我也尝试访问您发布的完整代码链接,但它给出了 404 错误:/
对不起,我忘了打开 repo。现在我已经做到了。您可以查看它。
您能否提供有关您的环境的更多信息?例如 - 完整的请求方法、模型之间的关系、类或方法或字段注释?以上是关于使用 HQL 急切加载嵌套关联的主要内容,如果未能解决你的问题,请参考以下文章
Sequelize嵌套的渴望加载找到所有嵌套关联不匹配的地方
laravel eloquent - 在嵌套急切加载的关系上不使用