当使用JPA和Hibernate时,JOIN和JOIN FETCH之间的区别是什么

请帮助我了解在哪里使用常规JOIN和在哪里使用JOIN FETCH。例如,如果我们有这两个查询

FROM Employee emp
JOIN emp.department dep

而且

FROM Employee emp
JOIN FETCH emp.department dep

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

281361 次浏览

在我之前在注释中提到的这个链接中,阅读这部分:

< p >“fetch"Join允许值的关联或集合 使用单个选择连同它们的父对象一起初始化。 这在集合的情况下特别有用。<强>它 类的外部连接和惰性声明 映射文件用于关联和集合

这&;JOIN FETCH"将有它的效果,如果你有(fetch = FetchType.LAZY)属性的集合在实体(示例如下)。

当查询应该发生时,它只影响“;”的方法。你还必须知道:

hibernate有两个正交的概念:何时获取关联和如何获取关联 这是牵强附会吗?重要的是不要把它们弄混。我们使用 获取来调优性能。我们可以使用lazy来定义契约 什么数据总是在一个特定的分离实例中可用 类。< / p >

什么时候联想到的——>你的“FETCH"类型

它是怎么来的——>加入/选择/ Subselect /批量

在你的例子中,FETCH只会在你有department作为Employee内部的集合时起作用,在实体中就像这样:

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

当你使用

FROM Employee emp
JOIN FETCH emp.department dep

你会得到empemp.dep。当你不使用fetch时,你仍然可以得到emp.dep,但hibernate将处理另一个select到数据库来获得那组department。

所以这只是一个性能调优的问题,关于你想要在一个查询中获得所有的结果(你需要它或不需要它)(即时抓取),或者你想在以后需要它时查询它(惰性抓取)。

当您需要通过一个选择(一个大查询)获取小数据时,使用即时抓取。或者使用惰性抓取来查询后面需要的东西(许多更小的查询)。

在以下情况使用fetch:

  • 在你要获取的实体中没有大的不需要的集合/集

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

  • 你可能需要该集合,当你没有访问它(事务方法/类的)

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

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

因此,如果使用第二个查询,您将不需要再次执行新的查询来访问数据库以查看每个Employee的部门。

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

我建议阅读这个链接,如果你需要应用一些WHERE条件(你可能会需要):如何正确表达JPQL的“join fetch”与“where"条款作为JPA 2 CriteriaQuery?

更新

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

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

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

当使用fetch时,只有一个select,结果是Employee List<Employee>的列表,其中包含部门列表。它覆盖了实体的惰性声明。

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

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

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

加入

当对实体关联使用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子句中列出的实体相关联的列。

此外,在使用Hibernate时,JOIN FETCH是解决LazyInitializationException的一个很好的方法,因为你可以使用FetchType.LAZY抓取策略和你要抓取的主实体来初始化实体关联。

差异连接和连接FETCH在JPQL

FETCH关键字告诉entityManager也要获取急切连接的相关实体(当情况还不是这样时)。


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

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;
}

假设我们有以下查询(这是Spring数据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();

第一个只有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;

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

enter image description here

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


现在让我们用JOIN FETCH检查生成的查询SQL:

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

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

enter image description here

可以观察到,我们现在可以访问User对象中的List<userData>