请帮助我了解在哪里使用常规JOIN和在哪里使用JOIN FETCH。例如,如果我们有这两个查询
FROM Employee emp
JOIN emp.department dep
and
FROM Employee emp
JOIN FETCH emp.department dep
它们之间有什么区别吗?如果是,什么时候用哪一个?
请帮助我了解在哪里使用常规JOIN和在哪里使用JOIN FETCH。例如,如果我们有这两个查询
FROM Employee emp
JOIN emp.department dep
and
FROM Employee emp
JOIN FETCH emp.department dep
它们之间有什么区别吗?如果是,什么时候用哪一个?
当前回答
在这两个查询中,您使用JOIN查询至少有一个部门关联的所有员工。
但是,不同之处在于:在第一个查询中,您只返回Hibernate的employees。在第二个查询中,您将返回employees和所有相关的department。
因此,如果使用第二个查询,您将不需要再次执行新的查询来访问数据库以查看每个Employee的部门。
当您确定需要每个Employee的Department时,可以使用第二个查询。如果不需要Department,请使用第一个查询。
我建议阅读这个链接,如果你需要应用一些WHERE条件(你可能会需要):如何正确表达JPQL“join fetch”与“WHERE”子句作为JPA 2 CriteriaQuery?
更新
如果您不使用fetch而继续返回Departments,是因为您的Employee和Department(一个@OneToMany)之间的映射是使用FetchType.EAGER设置的。在这种情况下,使用FROM Employee进行的任何HQL(带fetch或不带fetch)查询都将带来所有department。记住,所有的映射*ToOne (@ManyToOne和@OneToOne)默认情况下都是EAGER。
其他回答
Dherik:我不确定你说的是什么,当你不使用fetch时,结果将是类型:List<Object[]>,这意味着对象表的列表,而不是雇员的列表。
Object[0] refers an Employee entity
Object[1] refers a Departement entity
当您使用fetch时,只有一个选择,结果是Employee list <Employee>的列表,其中包含部门列表。它覆盖了实体的惰性声明。
如果你有@oneToOne映射设置为FetchType。LAZY,你使用第二个查询(因为你需要Department对象作为Employee对象的一部分被加载)Hibernate将做的是,它将为从DB中获取的每个Employee对象发出查询来获取Department对象。
稍后,在代码中,您可以通过Employee到Department单值关联访问Department对象,Hibernate将不会发出任何查询来获取给定Employee的Department对象。
请记住,Hibernate仍然发出与它所获取的employee数量相等的查询。如果您希望访问所有Employee对象的Department对象,Hibernate将在上述两个查询中发出相同数量的查询
差异连接和连接FETCH在JPQL
TLDR: 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表中获取数据。在调试器中也可以看到,如下所示:
可以看到,我们的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表中获取数据。在调试器中也可以看到,如下所示:
可以看到,我们现在可以访问User对象中的List<userData>。
在这两个查询中,您使用JOIN查询至少有一个部门关联的所有员工。
但是,不同之处在于:在第一个查询中,您只返回Hibernate的employees。在第二个查询中,您将返回employees和所有相关的department。
因此,如果使用第二个查询,您将不需要再次执行新的查询来访问数据库以查看每个Employee的部门。
当您确定需要每个Employee的Department时,可以使用第二个查询。如果不需要Department,请使用第一个查询。
我建议阅读这个链接,如果你需要应用一些WHERE条件(你可能会需要):如何正确表达JPQL“join fetch”与“WHERE”子句作为JPA 2 CriteriaQuery?
更新
如果您不使用fetch而继续返回Departments,是因为您的Employee和Department(一个@OneToMany)之间的映射是使用FetchType.EAGER设置的。在这种情况下,使用FROM Employee进行的任何HQL(带fetch或不带fetch)查询都将带来所有department。记住,所有的映射*ToOne (@ManyToOne和@OneToOne)默认情况下都是EAGER。
JOIN
当对实体关联使用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子句只包含员工表列,而不包含部门表列。要获取部门表列,我们需要使用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
请注意,这一次也选择了部门表列,而不仅仅是与FROM JPQL子句中列出的实体相关联的列。
另外,当使用Hibernate时,JOIN FETCH是解决LazyInitializationException的好方法,因为您可以使用FetchType初始化实体关联。LAZY抓取策略和你要抓取的主实体。