请帮助我了解在哪里使用常规JOIN和JOIN FETCH。
例如,如果我们有这两个查询
FROM Employee emp
JOIN emp.department dep
和
FROM Employee emp
JOIN FETCH emp.department dep
它们之间有什么区别吗?如果是,何时使用哪个?
请帮助我了解在哪里使用常规JOIN和JOIN FETCH。
例如,如果我们有这两个查询
FROM Employee emp
JOIN emp.department dep
和
FROM Employee emp
JOIN FETCH emp.department dep
它们之间有什么区别吗?如果是,何时使用哪个?
Answers:
在这两个查询中,您正在使用JOIN查询与至少一个部门关联的所有员工。
但是,不同之处在于:在第一个查询中,您仅返回休眠的Employes。在第二个查询中,您将返回员工和所有关联的部门。
因此,如果您使用第二个查询,则无需执行新查询即可再次命中数据库以查看每个员工的部门。
当您确定需要每个员工的部门时,可以使用第二个查询。如果不需要部门,请使用第一个查询。
如果您需要应用一些WHERE条件(您可能需要什么),我建议您阅读此链接:如何将JQ 2 CriteriaQuery正确地将JPQL“ join fetch”与“ where”子句一起表达?
更新资料
如果您不使用,fetch
并且继续返回部门,是因为您在雇员和部门(a @OneToMany
)之间的映射设置为FetchType.EAGER
。在这种情况下,任何带有fetch
或不带有HQL的查询FROM Employee
都会带到所有部门。请记住,默认情况下所有映射* ToOne(@ManyToOne
和@OneToOne
)均为EAGER。
fetch
如果要(通过我们的示例)要通过某些Department属性进行订购。否则,(有效期至少为PG),你可能会得到ERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list
在 我之前在评论中提到的此链接中,请阅读以下部分:
“提取”联接允许使用单个选择将值的关联或集合及其父对象初始化。这在集合的情况下特别有用。它有效地覆盖了关联和集合的映射文件的外部联接和惰性声明。
如果您在实体内部的集合中具有(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
你会得到emp
和emp.dep
。当您不使用提取时,您仍然可以获得emp.dep
但是hibernate将处理另一个选择到数据库以获取该部门集合。
因此,这只是性能调整的问题,您想要在单个查询中获取所有结果(是否需要)(渴望获取),或者您想在需要时稍后查询(懒惰获取)。
当您需要通过一次选择(一次大查询)来获取小数据时,请使用渴望获取。或使用延迟获取来查询稍后需要的内容(许多较小的查询)。
在以下情况下使用访存:
您将要获得的实体内没有大量不需要的集合/集
从应用程序服务器到数据库服务器的通信距离太长,需要很长时间
当您无法访问该集合时(在事务方法/类之外),则可能需要后者
List
不是A 部门,还是会热切地吸引部门Set
?
FETCH
在JPQL语句中使用关键字是否暗含了急切获取的属性?
当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
允许您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
表列也被选中,而不仅仅是与FROM
JPQL子句中列出的实体相关联的列。
同样,这JOIN FETCH
是解决LazyInitializationException
使用Hibernate时的一个好方法,因为您可以使用FetchType.LAZY
获取策略以及要获取的主要实体来初始化实体关联。
如果将@oneToOne
映射设置为,FetchType.LAZY
并且使用第二个查询(因为需要将Department对象作为Employee对象的一部分加载),Hibernate将执行的操作将针对从数据库中获取的每个Employee对象发出查询以获取Department对象。
稍后,在代码中,您可以通过Employee到Department单值关联访问Department对象,并且Hibernate将不会发出任何查询以获取给定Employee的Department对象。
请记住,Hibernate仍然会发出等于已获取的Employees数量的查询。如果您希望访问所有Employee对象的Department对象,则Hibernate将在上述两个查询中发出相同数量的查询
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
都会带到所有部门。
@OneToMany(fetch = FetchType.EAGER
)时,才会发生这种情况。如果不是这种情况,将不退回各部门。
select
在HQL中完成的。尝试SELECT emp FROM Employee emp JOIN FETCH emp.department dep
。JPA /休眠有回报的这种行为List
的Object[]
,当你ommit的SELECT
一部分。