使用JPA和Hibernate时JOIN和JOIN FETCH有啥区别

Posted

技术标签:

【中文标题】使用JPA和Hibernate时JOIN和JOIN FETCH有啥区别【英文标题】:What is the difference between JOIN and JOIN FETCH when using JPA and Hibernate使用JPA和Hibernate时JOIN和JOIN FETCH有什么区别 【发布时间】:2013-06-30 03:48:19 【问题描述】:

请帮助我了解在哪里使用常规 JOIN 以及在哪里使用 JOIN FETCH。

例如,如果我们有这两个查询

FROM Employee emp
JOIN emp.department dep

FROM Employee emp
JOIN FETCH emp.department dep

它们之间有什么区别吗?如果是,什么时候使用?

【问题讨论】:

你可以在这里找到它link阅读14.3。关联和连接 我已经阅读了该文档,但仍然不知道应该在哪里使用 JOIN 以及在哪里使用 JOIN FETCH。 如果您将@oneToOne 映射设置为 FetchType.LAZY 并且您使用第二个查询(因为您需要将 Department 对象作为 Employee 对象的一部分加载)Hibernate 会做的是,它将发出查询到为从 DB 中获取的每个 Employee 对象获取 Department 对象。稍后在代码中,您可能会通过 Employee 到 Department 单值关联访问 Department 对象,并且 Hibernate 不会发出任何查询来获取给定 Employee 的 Department 对象。请记住,Hibernate 仍会发出与其获取的员工数量相等的查询。 协助寻找文档~Fetching Strategies @ShameeraAnuranga 我认为在这种情况下你需要一个左外连接。 【参考方案1】:

在这两个查询中,您使用 JOIN 来查询至少关联一个部门的所有员工。

但是,不同之处在于:在第一个查询中,您只返回 Hibernate 的 Employes。在第二个查询中,您将返回 Employes 所有关联的部门。

因此,如果您使用第二个查询,则无需执行新查询即可再次访问数据库以查看每个员工的部门。

当您确定需要每个员工的部门时,您可以使用第二个查询。如果您不需要部门,请使用第一个查询。

如果您需要应用一些 WHERE 条件(您可能需要),我建议阅读此链接:How to properly express JPQL "join fetch" with "where" clause as JPA 2 CriteriaQuery?

更新

如果您不使用fetch 并且Department 继续返回,是因为您在Employee 和Department 之间的映射(@OneToMany)设置为FetchType.EAGER。在这种情况下,任何带有FROM Employee 的HQL(是否带有fetch)查询都会带来所有部门。请记住,所有映射 *ToOne(@ManyToOne@OneToOne)在默认情况下都是 EAGER。

【讨论】:

如果我们在没有 fetch 的情况下执行语句并获取结果会是什么行为。那么在会议期间我们将对待部门? @g***,是的 我在关系的两边都使用带有延迟获取的本机查询,但仍然加载子关系的层次结构。 值得一提的是,如果(使用我们的示例)您想按某个 Department 属性进行排序,则必须使用 fetch。否则,(至少对 PG 有效)你可能会得到 ERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list【参考方案2】:

在this link我之前在评论中提到过,请阅读这部分:

“获取”连接允许关联或值集合 使用单个选择与其父对象一起初始化。 这在集合的情况下特别有用。 它 有效地覆盖了外部连接和惰性声明 用于关联和集合的映射文件

如果实体内部的集合具有 (fetch = FetchType.LAZY) 属性(示例如下),则此“JOIN FETCH”将生效。

而且它只影响“何时查询应该发生”的方法。而且你一定也知道this:

hibernate 有两个正交的概念:何时获取关联以及如何获取关联 是不是捡来的。重要的是不要混淆它们。我们用 fetch 来调整性能。我们可以使用惰性来定义合约 什么数据在特定的任何分离实例中始终可用 类。

何时获取关联 --> 您的“FETCH”类型

它是如何获取的 --> Join/select/Subselect/Batch

在您的情况下,只有当您在 Employee 中设置部门作为集合时,FETCH 才会生效,在实体中是这样的:

@OneToMany(fetch = FetchType.LAZY)
private Set<Department> department;

当你使用

FROM Employee emp
JOIN FETCH emp.department dep

您将获得empemp.dep。当你不使用 fetch 时,你仍然可以得到 emp.dep 但hibernate 会处理另一个对数据库的选择来获取那组部门。

所以这只是性能调整的问题,关于您想在单个查询中获得所有结果(您需要与否)(急切获取),或者您想在需要时查询它(延迟获取) .

当您需要通过一次选择(一次大查询)获取小数据时,请使用急切获取。或者使用延迟获取来查询您需要的内容(许多较小的查询)。

在以下情况下使用 fetch:

在您将要获得的实体中没有大型不需要的集合/集合

从应用服务器到数据库服务器的通信距离太远,需要很长时间

当您无权访问该集合时(事务性方法/类之外),您可能需要该集合

【讨论】:

你能解释一下我刚刚在更新问题中写的查询吗? 有用的考虑:“在你将要获得的实体中没有大量不需要的集合/集合” 如果员工内部的部门是List而不是Set,是否仍会急于获取部门? 在 JPQL 语句中使用 FETCH 关键字是否意味着急切检索的属性?【参考方案3】:

加入

当对实体关联使用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 相比,JOIN FETCH 允许您在生成的 SQL 语句的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 是在使用 Hibernate 时解决 LazyInitializationException 的好方法,因为您可以使用 FetchType.LAZY 获取策略以及要获取的主要实体来初始化实体关联。

【讨论】:

是否可以在同一个查询中使用多个 JOIN FETCH? 是的,任何数量的@ManyToOne@OneToOne 关联和最多一个集合都是可能的。【参考方案4】:

如果您将 @oneToOne 映射设置为 FetchType.LAZY 并且您使用第二个查询(因为您需要将 Department 对象作为 Employee 对象的一部分加载)Hibernate 会做的是,它将发出查询以获取 Department 对象它从数据库中获取的每个单独的 Employee 对象。

稍后,在代码中,您可以通过 Employee 到 Department 单值关联访问 Department 对象,并且 Hibernate 不会发出任何查询来获取给定 Employee 的 Department 对象。

请记住,Hibernate 仍会发出与其已获取的员工数量相等的查询。如果您希望访问所有 Employee 对象的 Department 对象,Hibernate 将在上述两个查询中发出相同数量的查询

【讨论】:

【参考方案5】:

Dherik:我不确定你说的是什么,当你不使用 fetch 时,结果将是类型:List&lt;Object[ ]&gt;,这意味着对象表列表而不是员工列表。

Object[0] refers an Employee entity 
Object[1] refers a Departement entity 

当您使用 fetch 时,只有一个选择,结果是包含部门列表的 Employee List&lt;Employee&gt; 列表。它覆盖了实体的惰性声明。

【讨论】:

我不知道我是否理解您的担忧。如果您不使用fetch,您的查询将只返回员工。如果部门,即使在这种情况下,继续返回,是因为您在员工和部门(@OneToMany)之间的映射是使用 FetchType.EAGER 设置的。在这种情况下,任何带有FROM Employee 的HQL(带有fetch 或不带有)查询都会带来所有部门。 不使用 fetch(仅限连接项),结果将是一个集合数组,两行,第一行是员工集合,第二行是部门集合。使用 eager fetch 或lazy fetch,将获取部门。 如果没有在 HQL 上获取,只有当您的 Employee 和 Department 之间的映射是 EAGER (@OneToMany(fetch = FetchType.EAGER) 时才会发生这种情况。如果不是这种情况,部门将不会被退回。 @Dherik 自己尝试一下,你会得到一个 ClassCastException。 我找出了问题所在。不是获取问题,而是select 是如何在 HQL 中生成的。试试SELECT emp FROM Employee emp JOIN FETCH emp.department dep。当您省略 SELECT 部分时,JPA/Hibernate 会返回 Object[]List

以上是关于使用JPA和Hibernate时JOIN和JOIN FETCH有啥区别的主要内容,如果未能解决你的问题,请参考以下文章

即使使用 @Fetch(FetchMode.JOIN),JPA + Hibernate 也会出现太多查询问题

直接加入 JPA 或 HIBERNATE

Spring JPA/Hibernate Repository findAll 在 Kotlin 中默认执行 N+1 个请求而不是 JOIN

Jpa Join 查询与来自两个表的数据,org.hibernate.MappingException:没有 JDBC 类型的方言映射:2002

Hibernate 中 LEFT JOIN 和 LEFT JOIN FETCH 的区别?

如何在 JPA Criteria API 上正确使用 JOIN