请帮助我了解在哪里使用常规 JOIN 以及在哪里使用 JOIN FETCH。例如,如果我们有这两个查询
FROM Employee emp
JOIN emp.department dep
和
FROM Employee emp
JOIN FETCH emp.department dep
它们之间有什么区别吗?如果是,什么时候使用?
在这两个查询中,您使用 JOIN 来查询至少关联一个部门的所有员工。
但是,不同之处在于:在第一个查询中,您只返回 Hibernate 的 Employes。在第二个查询中,您将返回 Employes和所有关联的 Departments。
因此,如果您使用第二个查询,则无需执行新查询以再次访问数据库以查看每个员工的部门。
当您确定需要每个员工的部门时,您可以使用第二个查询。如果您不需要部门,请使用第一个查询。
如果您需要应用一些 WHERE 条件(您可能需要),我建议您阅读此链接:如何将带有“where”子句的 JPQL“join fetch”正确表达为 JPA 2 CriteriaQuery?
更新
如果您不使用fetch
并且Departments 继续返回,是因为您在Employee 和Department (a @OneToMany
) 之间的映射设置为FetchType.EAGER
。在这种情况下,任何 HQL(有fetch
或没有)查询FROM Employee
都会带来所有部门。请记住,所有映射 *ToOne (@ManyToOne
和@OneToOne
) 默认情况下都是 EAGER。
在我之前在评论中提到的这个链接中,阅读这部分:
“获取”连接允许使用单个选择来初始化值的关联或集合以及它们的父对象。这在集合的情况下特别有用。它有效地覆盖了关联和集合的映射文件的外部连接和惰性声明。
如果实体内部的集合具有 (fetch = FetchType.LAZY) 属性,则此“JOIN FETCH”将生效(示例如下)。
它只影响“何时应该发生查询”的方法。你还必须知道这一点:
hibernate 有两个正交的概念:何时获取关联以及如何获取关联。重要的是不要混淆它们。我们使用 fetch 来调整性能。我们可以使用惰性来定义特定类的任何分离实例中始终可用的数据的协定。
何时获取关联->您的“FETCH”类型
它是如何获取的——>加入/选择/子选择/批处理
在您的情况下,只有当您在 Employee 中设置部门作为集合时,FETCH 才会生效,在实体中是这样的:
@OneToMany(fetch = FetchType.LAZY)
private Set<Department> department;
当你使用
FROM Employee emp
JOIN FETCH emp.department dep
你会得到emp
和emp.dep
。当您不使用 fetch 时,您仍然可以获取emp.dep
,但 hibernate 将处理另一个对数据库的选择以获取该组部门。
所以它只是性能调整的问题,关于你想在一个查询中获得所有结果(你需要与否)(渴望获取),或者你想在需要时查询它(延迟获取)。
当您需要通过一次选择(一次大查询)获取小数据时,请使用急切获取。或者使用延迟获取来查询您需要的内容(许多较小的查询)。
在以下情况下使用 fetch:
在您将要获得的实体内没有大量不需要的集合/集合
从应用服务器到数据库服务器的通信太远并且需要很长时间
当您无权访问它时,您可能需要该集合(在事务方法/类之外)
当JOIN
针对实体关联使用时,JPA 将在生成的 SQL 语句中生成父实体和子实体表之间的 JOIN。
因此,以您为例,在执行此 JPQL 查询时:
FROM Employee emp
JOIN emp.department dep
Hibernate 将生成以下 SQL 语句:
SELECT emp.*
FROM employee emp
JOIN department dep ON emp.department_id = dep.id
请注意,SQL
SELECT
子句仅包含employee
表列,而不包含表列department
。要获取department
表列,我们需要使用JOIN FETCH
而不是JOIN
.
因此,与 相比JOIN
,允许您在生成的 SQL 语句JOIN FETCH
的子句中投影连接表列。SELECT
因此,在您的示例中,执行此 JPQL 查询时:
FROM Employee emp
JOIN FETCH emp.department dep
Hibernate 将生成以下 SQL 语句:
SELECT emp.*, dept.*
FROM employee emp
JOIN department dep ON emp.department_id = dep.id
请注意,这一次,
department
也选择了表列,而不仅仅是与FROM
JPQL 子句中列出的实体相关联的列。
此外,JOIN FETCH
这是解决LazyInitializationException
使用 Hibernate 时的一个好方法,因为您可以使用FetchType.LAZY
获取策略以及要获取的主要实体来初始化实体关联。
如果您@oneToOne
设置了映射FetchType.LAZY
并使用第二个查询(因为您需要将 Department 对象作为 Employee 对象的一部分加载),Hibernate 会做的是,它将发出查询以获取从 DB 中获取的每个 Employee 对象的 Department 对象。
稍后,在代码中,您可以通过 Employee 到 Department 单值关联访问 Department 对象,并且 Hibernate 不会发出任何查询来获取给定 Employee 的 Department 对象。
请记住,Hibernate 仍然会发出与其获取的员工数量相等的查询。如果您希望访问所有 Employee 对象的 Department 对象,Hibernate 将在上述两个查询中发出相同数量的查询
Dherik:我不确定你在说什么,当你不使用 fetch 时,结果将是 type :List<Object[ ]>
这意味着 Object 表的列表而不是 Employee 的列表。
Object[0] refers an Employee entity
Object[1] refers a Departement entity
当您使用 fetch 时,只有一个选择,结果是List<Employee>
包含部门列表的 Employee 列表。它覆盖了实体的惰性声明。