使用JPA和Hibernate时JOIN和JOIN FETCH有什么区别


182

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

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

FROM Employee emp
JOIN emp.department dep

FROM Employee emp
JOIN FETCH emp.department dep

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


2
您可以在此处找到链接阅读14.3。协会和加入
Angga 2013年

4
我已经完成了该文档说明,但是仍然不知道我应该在哪里使用JOIN和JOIN FETCH。
阿巴斯2013年

2
如果您将@oneToOne映射设置为FetchType.LAZY并使用第二个查询(因为需要将Department对象作为Employee对象的一部分加载),Hibernate将执行的操作将发出查询以获取每个单个Employee对象的Department对象它从数据库获取。在代码的后面,您可能会通过Employee到Department单值关联来访问Department对象,并且Hibernate将不会发出任何查询以获取给定Employee的Department对象。请记住,Hibernate仍然会发出等于已获取的Employees数量的查询。
邦蒂2015年

为了协助文档搜寻〜提取策略
Eddie B

1
@ShameeraAnuranga我认为在这种情况下,您将需要左外部联接。
阿巴斯(Abbas)

Answers:


179

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

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

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

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

如果您需要应用一些WHERE条件(您可能需要什么),我建议您阅读此链接:如何将JQ 2 CriteriaQuery正确地将JPQL“ join fetch”与“ where”子句一起表达?

更新资料

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


1
如果我们在不获取数据的情况下执行语句并获得结果,将会是哪种行为。那么在会议期间,我们将请教部门?
gstackoverflow

1
@gstackoverflow,是的
Dherik '16

我在关系的两面都使用了本地查询和Lazy fetch,但仍加载子关系的层次结构。
Badamchi

值得一提的是,fetch如果要(通过我们的示例)要通过某些Department属性进行订购。否则,(有效期至少为PG),你可能会得到ERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list

60

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

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

如果您在实体内部的集合中具有(fetch = FetchType.LAZY)属性(例如,波纹管),则此“ JOIN FETCH”将起作用。

它仅影响“何时应进行查询”的方法。而且您还必须知道这一点

休眠有两个正交的概念:何时获取关联以及如何获取关联。不要混淆它们,这一点很重要。我们使用访存来调整性能。我们可以使用lazy来定义一个契约,该契约规定在特定类的任何分离实例中哪些数据始终可用。

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

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

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

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

当你使用

FROM Employee emp
JOIN FETCH emp.department dep

你会得到empemp.dep。当您不使用提取时,您仍然可以获得emp.dep但是hibernate将处理另一个选择到数据库以获取该部门集合。

因此,这只是性能调整的问题,您想要在单个查询中获取所有结果(是否需要)(渴望获取),或者您想在需要时稍后查询(懒惰获取)。

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

在以下情况下使用访存:

  • 您将要获得的实体内没有大量不需要的集合/集

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

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


您能为我刚才在更新的问题中写的查询解释一下吗?
阿巴斯2013年

有用的注意事项:“您要获取的实体内部没有大量不需要的集合/集”
Divs

如果员工内部的部门List不是A 部门,还是会热切地吸引部门Set
斯蒂芬·

FETCH在JPQL语句中使用关键字是否暗含了急切获取的属性?
斯蒂芬

15

加入

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

加入FETCH

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

因此,在您的示例中,执行此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表列也被选中,而不仅仅是与FROMJPQL子句中列出的实体相关联的列。

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


是否可以在同一查询中使用多个JOIN FETCH?
A.OnurÖzcan

2
您可以加入FETCH多个多对一和一对一关联,并且最多一个集合。获取多个集合(如一对多或多对多关联)将最终形成笛卡尔积。但是,如果要获取多个集合,则可以对第二,第三,第n个集合使用辅助查询。请查看本文以获取更多详细信息。
Vlad Mihalcea

5

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

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

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


2

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

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

当使用提取时,只有一个选择,结果是List<Employee>包含部门列表的Employee 列表。它会覆盖实体的惰性声明。


不知道您是否担心 如果您不使用fetch,则查询将仅返回雇员。如果即使在这种情况下仍继续返回部门,是因为您在Employee和Department之间的映射(@OneToMany)是使用FetchType.EAGER设置的。在这种情况下,任何带有fetch或不带有HQL的查询FROM Employee都会带到所有部门。
Dherik

如果不使用访存(仅用于联接项),结果将是一个集合数组,分为两行,第一行是Employees的集合,第二行是Departments的集合。使用渴望获取或懒惰获取,将获取部门。
Bilal BBB 2015年

如果不提取HQL,则只有当您在Employee和Department之间的映射为EAGER(@OneToMany(fetch = FetchType.EAGER)时,才会发生这种情况。如果不是这种情况,将不退回各部门。
Dherik

@Dherik自己尝试,您将获得ClassCastException。
Bilal BBB 2015年

我找出问题所在。不是获取问题,而是如何select在HQL中完成的。尝试SELECT emp FROM Employee emp JOIN FETCH emp.department dep。JPA /休眠有回报的这种行为ListObject[],当你ommit的SELECT一部分。
Dherik
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.