使用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
您将获得emp
和emp.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<Object[ ]>
,这意味着对象表列表而不是员工列表。
Object[0] refers an Employee entity
Object[1] refers a Departement entity
当您使用 fetch 时,只有一个选择,结果是包含部门列表的 Employee List<Employee>
列表。它覆盖了实体的惰性声明。
【讨论】:
我不知道我是否理解您的担忧。如果您不使用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 也会出现太多查询问题
Spring JPA/Hibernate Repository findAll 在 Kotlin 中默认执行 N+1 个请求而不是 JOIN
Jpa Join 查询与来自两个表的数据,org.hibernate.MappingException:没有 JDBC 类型的方言映射:2002