Hibernate 中的动态预加载和延迟加载

Dynamic Eager & Lazy Loading in Hibernate

我是 Hibernate 的新手,但对 C# 中的 Entity Framework 有丰富的经验。我喜欢的一个特性是能够动态决定在查询中立即加载什么。例如,考虑 Class 和学生实体的一对多关系。

在 "View Classes" 页面上我可以这样做:

context.Configuration.EnableLazyLoading = true; //default option
List<Classes> classes = context.Classes.ToList();

现在我可以愉快地只显示 class 信息,而不用浪费资源收集学生数据。仅当用户单击 "View Classes with Rosters" 时,我才会执行以下操作:

context.Configuration.EnableLazyLoading = true;
List<Classes> classes = context.Classes.Include(c => c.Students).ToList();

有了这句话,我就可以决定在这种特殊情况下,我想一次获取所有信息。不是两个查询。不是一百个查询。只需一个查询。尽管只加载了 classes 前几秒。

我所有关于 Hibernate 的阅读都解释了如何在关系的配置文件中指定 lazy="true|false",但我真的想要决定何时动态加载集合的选项。毕竟,我对购买时速仅为 30 英里/小时或 60 英里/小时的汽车不感兴趣。我需要根据我所在的位置选择速度。

也许使用获取模式作为 JOIN 的选项是可以接受的,因为在这种情况下它只会有两个查询(一个用于 class,一个用于学生),但我真的很喜欢拥有在一个查询中完成所有操作的选项,特别是如果我有多个子集合要加载并且不想对每个关系执行查询。我意识到一次性加入会创建需要流式传输的额外数据,但令我感到惊讶的是,这种级别的控制并不容易完成,或者可能完全不可用。

Hibernate 没有非常方便的动态获取方式。你可以通过

来控制它
  1. Dynamic fetching via HQL queriesjoin fetch 结合使用(正如@ThibaultClement 所建议的)。
  2. Dynamic association fetchingCriteria.setFetchMode() 结合使用。
  3. 使用 Dynamic fetching via profiles@FetchProfile 注释。

您也可以参考 HQL joined query to eager fetch a large number of relationships 了解更多想法。

如@v.ladynev所述,querydsl 是一个不错的选择。但是,在我看来,仍然没有一种干净的方法来处理它。

  1. 如果你使用 .select(...) ,结果将是一个元组而不是原始实体(未提及的字段默认为空),这将导致编写一些样板代码将其映射到原始实体。
  2. 与 querydsl 相关的另一种可能的解决方案是使用 QueryProjection,但同样需要您编写一个具有不同可能性的单独 class。
  3. 第三种解决方案是将所有连接保留为Fetch.LAZY,然后根据需要使用JpaUtils.initialize()(或hibernate 自己的方法)获取实体图。 (但就性能而言,这比直接使用 join fetch 只会进行 1 次 sql 调用更糟糕。
  4. 另一种值得关注的第三种选择是使用 NamedEntityGraph 或 EntityGraph 来获取所需的部分。