Hibernate中JOIN和JOIN FETCH之间的区别

abb*_*bas 158 java hibernate join hql fetch

请帮助我了解在哪里使用常规JOIN以及JOIN FETCH的位置.

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

FROM Employee emp
JOIN emp.department dep
Run Code Online (Sandbox Code Playgroud)

FROM Employee emp
JOIN FETCH emp.department dep
Run Code Online (Sandbox Code Playgroud)

它们之间有什么区别吗?如果是的话,哪一个使用的时候?

Dhe*_*rik 162

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

但是,区别在于:在第一个查询中,您只返回Hibernate的Employes.在第二个查询中,您将返回Employes 所有Departments关联.

因此,如果您使用第二个查询,则无需再次执行新查询来查看数据库以查看每个Employee的Departments.

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

如果你需要应用一些WHERE条件(你可能需要什么),我建议阅读这个链接:如何正确表达JPQL"join fetch"和"where"子句作为JPA 2 CriteriaQuery?

更新

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


Ang*_*gga 58

在我之前提到的评论链接中,请阅读此部分:

"fetch"连接允许使用单个select来初始化值的关联或集合及其父对象.这在集合的情况下特别有用.它有效地覆盖了关联和集合的映射文件的外连接和延迟声明.

如果你在实体内部有一个集合的(fetch = FetchType.LAZY)属性,那么这个"JOIN FETCH"会产生影响(例如波纹管).

它只会影响"何时应该发生查询"的方法.你也必须知道这个:

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

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

如何获取 - >加入/选择/子选择/批量

在您的情况下,如果您将部门作为Employee中的集合,FETCH将只有它的效果,在实体中是这样的:

@OneToMany(fetch = FetchType.LAZY)
private Set<Department> department;
Run Code Online (Sandbox Code Playgroud)

当你使用

FROM Employee emp
JOIN FETCH emp.department dep
Run Code Online (Sandbox Code Playgroud)

你会得到empemp.dep.当你没有使用fetch时你仍然可以得到emp.dep但是hibernate将处理另一个select到数据库以获得那组部门.

所以它只是性能调优的问题,你想要得到的所有结果(你需要与否)在单个查询(预先抓取),或要查询其后者,当你需要它(延迟抓取).

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

使用fetch时:

  • 你要获得的实体内没有大量不需要的收集/设置

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

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


Vla*_*cea 52

加入

JOIN针对实体关联使用时,JPA 将在生成的 SQL 语句中的父实体和子实体表之间生成一个 JOIN。

因此,以您为例,在执行此 JPQL 查询时:

FROM Employee emp
JOIN emp.department dep
Run Code Online (Sandbox Code Playgroud)

Hibernate 将生成以下 SQL 语句:

SELECT emp.*
FROM employee emp
JOIN department dep ON emp.department_id = dep.id
Run Code Online (Sandbox Code Playgroud)

请注意,SQLSELECT子句仅包含employee表列,而不包含表列department。要获取department表列,我们需要使用JOIN FETCH代替JOIN

加入获取

因此,与 相比JOINJOIN FETCH允许您SELECT在生成的 SQL 语句的子句中投影连接表列。

因此,在您的示例中,在执行此 JPQL 查询时:

FROM Employee emp
JOIN FETCH emp.department dep
Run Code Online (Sandbox Code Playgroud)

Hibernate 将生成以下 SQL 语句:

SELECT emp.*, dept.*
FROM employee emp
JOIN department dep ON emp.department_id = dep.id
Run Code Online (Sandbox Code Playgroud)

请注意,这次department还选择了表列,而不仅仅是与FROMJPQL 子句中列出的实体关联的列。

此外,这JOIN FETCH是解决LazyInitializationException使用 Hibernate 时的好方法,因为您可以使用FetchType.LAZY获取策略以及您正在获取的主要实体来初始化实体关联。

  • 是否可以在同一查询中使用多个 JOIN FETCH? (3认同)
  • 是的,可以有任意数量的“@ManyToOne”和“@OneToOne”关联,并且最多有一个集合。 (2认同)

Wil*_*een 6

JPQL 中 JOIN 和 JOIN FETCH 的区别

TLDR:关键字FETCH告诉实体管理器也获取急切加入的关联实体(当情况还不是这样时)。


假设我们有一个用户实体和 userInfo 实体。用户实体@OneToMany与userInfo的关系如下:

import javax.persistence.*;

@Entity
@Table(name= "test_user")
public class User {

    // ... user properties id etc

    @OneToMany(mappedBy = "user" fetch = FetchType.LAZY)
    private List<UserInfo> infoList;
}
Run Code Online (Sandbox Code Playgroud)

假设我们有以下查询(这是 Spring data JPA 语法,但 JPQL 的构造方式并不重要):

@Query("SELECT user FROM User user JOIN user.infoList info")
public List<User> getUsersJoin();

@Query("SELECT user FROM User user JOIN FETCH user.infoList info")
public List<User> getUsersJoinFetch();
Run Code Online (Sandbox Code Playgroud)

第一个仅包含JOIN关键字的查询将生成以下 SQL:

select u.id, u.email, u.name from test_user u
inner join test_user_data on u.id=test_user_data.user_id;
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,它只会从 test_user 表中获取数据,而不是从 test_user_data 表中获取数据。这也可以在调试器中看到,如下所示:

在此输入图像描述

List<userData>可以看出,我们的对象上没有 ,User因为默认情况下不会加载它。


现在让我们检查一下为查询生成的 SQL JOIN FETCH

select test_user.id, data.id, test_user.email, test_user.name, 
data.data, data.user_id, data.user_id, data.id from test_user test_user 
inner join test_user_data data on test_user.id=data.user_id
Run Code Online (Sandbox Code Playgroud)

可以看到,我们现在正在从连接中的 test_user 和 test_user_data 表中获取数据。这也可以在调试器中看到,如下所示:

在此输入图像描述

可以看出,我们现在可以访问对象List<userData>内部User

  • 由此看来,“join”毫无意义,这不可能是真的……对吗?我缺少什么? (2认同)

Bun*_*nti 5

如果您将@oneToOne映射设置为FetchType.LAZY并使用第二个查询(因为您需要将 Department 对象作为 Employee 对象的一部分加载),Hibernate 将执行的操作是,它将发出查询来为它从 DB 获取的每个 Employee 对象获取 Department 对象。

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

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