问题描述
正如我们上面所说, LazyCollectionOption.EXTRA注解有一个问题——它为每个对象对数据库执行单独的请求。我们需要以某种方式向 Hibernate 解释我们希望它立即为我们的父对象加载所有子对象。
Hibernate 的开发者想出了解决这个问题的方法, HQL 中的join fetch operator 。
HQL查询示例:
select distinct task from Task t left join fetch t.employee order by t.deadline
在这个查询中,一切都既简单又复杂。让我们尝试一点一点地构建它。
选项1
我们要下载所有对象任务, 按截止日期排序。该请求如下所示:
select task from Task t order by t.deadline
虽然一切都很清楚。但是场员工Task类的 将包含一个用EXTRA注释注释的 Employees 集合。并且不会加载此集合的对象。
选项 2
强制Hibernate为一个对象加载子对象任务.
select task from Task t join fetch t.employee order by t.deadline
在帮助下,我们在查询中明确绑定了 Task 和 Employee 实体。Hibernate 已经知道这一点,因为我们在这些字段上使用了@ManyToMany注释。
但是我们需要一个join语句来完成它,用一个fetch语句来获取一个 join fetch。这就是我们如何告诉 Hibernate 在执行我们的请求时需要从数据库中加载 Task.employee 集合中的对象。
选项 3
以前的解决方案有一些错误。一、使用join后,SQL不会返回对象给我们任务,在 Employee 表中没有与之关联的对象。这正是inner join 的工作原理。
所以我们需要用左运算符来扩充我们的连接,并将其变成左连接。例子:
select task from Task t left join fetch t.employee order by t.deadline
选项 4
但这还不是全部。如果在你的代码中,实体之间的关系是多对多的,那么查询结果中就会出现重复。同一个对象任务可以在不同的员工(员工对象)上找到。
因此,您需要在选择词后添加 distinct 关键字以摆脱重复的 Task 对象。
select distinct task from Task t left join fetch t.employee order by t.deadline
这就是我们如何在 4 个步骤中达到我们开始的请求。好吧,Java 代码看起来很符合预期:
String hql = " select distinct task from Task t left join fetch t.employee order by t.deadline";
Query<Task> query = session.createQuery( hql, Task.class);
return query.list();
JOIN FETCH 限制
没有人是完美的。JOIN FETCH 语句也是如此。它有很多限制。第一个是使用setMaxResults()和setFirstResult()方法。
对于 JOIN FETCH 语句,我们的 Hibernate 将生成一个非常复杂的查询,其中我们将三个表合并为一个:employee、task 和 employee_task。事实上,这不是对员工或任务的请求,而是对所有已知的员工-任务对的请求。
SQL 可以将其 LIMIT 和 OFFSET 语句准确地应用于员工-任务对的查询。同时,从HQL查询中可以清楚地看出,我们要准确获取任务(Task),如果我们重新分配我们的FirstResult和MaxResult参数,那么它们应该具体指代Task对象。
如果你写这样的代码:
String hql = " select distinct task from Task t left join fetch t.employee order by t.deadline";
Query<Task> query = session.createQuery( hql, Task.class);
query.setFirstResult(0);
query.setMaxResults(1);
return query.list();
那么 Hibernate 将无法正确地将 FirstResult 和 MaxResult 转换为 SQL 查询的 OFFSET 和 LIMIT 参数。
相反,它会做三件事:
- SQL 查询通常会从表中选择所有数据并将其返回给 Hibernate
- Hibernate 会在它的内存中选择必要的记录并返回给你
- Hibernate 会发出警告
警告将是这样的:
WARN [org.hibernate.hql.internal.ast.QueryTranslatorImpl] HHH000104:
firstResult/maxResults specified with collection fetch; applying in memory!
GO TO FULL VERSION