请帮我理解何时应该使用普通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
它们之间有区别吗?如果有,该在什么情况下使用哪一个?
fetch
,并且部门仍然被返回,那是因为您在Employee和Department之间的映射(一个@OneToMany
)被设置为FetchType.EAGER
。在这种情况下,任何带有FROM Employee
的HQL查询(无论是否带有fetch
)都会带来所有的部门。请记住,所有的*ToOne映射(@ManyToOne
和@OneToOne
)默认都是EAGER加载的。nativeQuery
,则不再使用 JPQL/HQL 语言来创建查询;而是使用纯 SQL。因此,fetch
不可用。 - Dherik使用JOIN(连接)
查询实体的关联关系时,JPA会在生成的SQL语句中生成父实体和子实体表之间的连接。
因此,在执行此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。
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
抓取策略初始化实体关联和您正在获取的主要实体。
@ManyToOne
和@OneToOne
关联都是可能的,最多只能有一个集合。 - Vlad Mihalcea通过“fetch”连接,可以使用单个select初始化关联或值的集合及其父对象。 这在集合的情况下特别有用。 它有效地覆盖了映射文件中的外连接和惰性声明 。
如果您在实体内有(fetch = FetchType.LAZY)属性的集合,则此“JOIN FETCH”将产生效果(例如下面的示例)。
它仅影响“查询何时发生”的方法。 您还必须了解此内容:
Hibernate具有两个正交概念:关联的获取时间和获取方式。 不要混淆它们很重要。 我们使用获取来调整性能。 我们可以使用惰性定义某个类的任何分离实例中始终可用的数据的契约。
关联获取时间-->您的“FETCH”类型
如何获取-->Join/select/Subselect/Batch
在您的情况下,FETCH仅在Employee实体内有department作为集合时才会生效,例如下面的示例:
@OneToMany(fetch = FetchType.LAZY)
private Set<Department> department;
当你使用时
FROM Employee emp
JOIN FETCH emp.department dep
当你没有使用fetch时,你会获得emp
和emp.dep
。但是,如果你需要emp.dep
,Hibernate将再次向数据库发出查询以获取该部门的集合。
因此,这只是性能调整的问题,关于你想在单个查询中获取所有结果(无论你是否需要),还是在需要时查询它们(延迟加载)。
当你需要在一个查询中获取少量数据时(一次大查询),请使用急切加载。或者使用延迟加载,在需要时进行查询(许多较小的查询)。
使用fetch的情况:
实体中不存在大的不必要的集合/ set
应用服务器与数据库服务器之间的通信距离太远,需要很长时间
当你没有访问权限时,可能需要该集合/ set(事务性方法 / 类之外)
List
而不是一个Set
,那么这些部门是否仍然会被热切地获取? - StephaneFETCH
关键字是否意味着立即检索属性? - Stephane简介:FETCH
关键字告诉entityManager也获取与之前已经关联的实体(在此之前没有关联的情况下)。
假设我们有一个用户实体和用户信息实体。用户实体有一个如下@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;
}
@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;
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
是毫无意义的,这不可能是真的...对吧?我错过了什么? - marcotama... where test_user_data.some_field = some_value
。
这样一来,你就能获取满足条件的用户,但不会加载后续不需要的数据。 - aminator@oneToOne
映射设置为FetchType.LAZY
,并且您使用第二个查询(因为您需要将Department对象作为Employee对象的一部分加载),那么Hibernate将执行查询以获取每个单独的Employee对象的Department对象。稍后,在代码中,您可能通过Employee访问Department对象以获取Department到单值关联,并且Hibernate不会发出任何查询以获取给定Employee的Department对象。
请记住,Hibernate仍会发出与其已获取的Employee数量相同的查询。如果您希望访问所有Employee对象的Department对象,则上述两个查询都将发出相同数量的查询。Dherik: 我不确定你所说的内容,如果你不使用fetch,结果将会是List<Object[ ]>
类型,这意味着一个对象表的列表而不是一个员工列表。
Object[0] refers an Employee entity
Object[1] refers a Departement entity
List<Employee>
列表。它覆盖了实体的惰性声明。fetch
,则查询将仅返回员工。即使在这种情况下,如果部门仍然被返回,则是因为您的 Employee 和 Department 之间的映射(@OneToMany)设置为 FetchType.EAGER。在这种情况下,任何带有 FROM Employee
的 HQL 查询(无论是否使用 fetch
)都会带来所有部门。 - Dherik@OneToMany(fetch = FetchType.EAGER)
)时才会发生这种情况。如果不是这种情况,部门将不会被返回。 - Dherikselect
语句的编写方式。请尝试使用SELECT emp FROM Employee emp JOIN FETCH emp.department dep
。当你省略SELECT
部分时,JPA/Hibernate会返回一个Object[]
列表。 - Dherik