簡體   English   中英

JPA 和 Hibernate:急切加載執行后續查詢以獲取所有數據,而不是僅在一個查詢中執行

[英]JPA & Hibernate: Eager loading performing subsequent queries to fetch all data, instead of doing it in just one query

我有以下疑問。 我想知道為什么在使用 JPA 和 Hibernate 時,在ManyToOneOneToMany關系中執行 Eager 加載時,它調用 DB 以獲取實體信息,但另外生成后續查詢以獲取每個子項。

另一方面,當使用帶有 JOIN FETCH 的查詢時,它會按照我的預期執行查詢,一次性獲取所有信息,因為 fetchType 表示為“EAGER”。

這是一個簡單的例子:

我有班級學生,它與班級教室有多對一關系。

@Entity
@Table(name = "STUDENT")
public class Student {

@ManyToOne(optional = true, fetch = FetchType.EAGER)
        @JoinColumn(name = "ClassroomID")
        private Classroom mainClass;

在另一邊有一個名為 Classroom 的類,如下所示:

@Entity
public class Classroom {

@OneToMany(cascade = CascadeType.ALL, mappedBy = "mainClass", fetch = FetchType.EAGER)
private List<Student> studentsList;

當獲得課堂對象,它執行一個查詢,以獲得從本身和后續查詢的信息獲取包含在studentsList為每間教室對象每個學生的信息。

第一個查詢:

Hibernate: 
/* SELECT
         r 
     FROM
         Classroom r 
     LEFT JOIN
         r.classStudents */ 

     select
         classroom0_.id as id1_0_,
         classroom0_.number as number2_0_ 
     from
         Classroom classroom0_ 
     left outer join
         STUDENT classstude1_ 
             on classroom0_.id=classstude1_.ClassroomID

然后它執行下一個查詢的次數與分配給每個教室的學生一樣多。

  Hibernate: 
      /* load one-to-many com.hw.access.Classroom.classStudents */ 
      select
          classstude0_.ClassroomID as Classroo4_0_1_,
          classstude0_.id as id1_1_1_,
          classstude0_.id as id1_1_0_,
          classstude0_.FIRST_NAME as FIRST_NA2_1_0_,
          classstude0_.LAST_NAME as LAST_NAM3_1_0_,
          classstude0_.ClassroomID as Classroo4_1_0_ 
      from
          STUDENT classstude0_ 
      where
          classstude0_.ClassroomID=?

問題是:為什么不一次性獲取所有信息? 為什么不只在一個查詢中獲取信息? 因為它已經在那里執行 Join 子句

為什么在查詢中顯式添加Fetch時,它會執行它所要求的操作?

例如:

SELECT
         r 
     FROM
         Classroom r 
     LEFT JOIN FETCH
         r.classStudents */ 

然后,輸出查詢確實在一個查詢中獲取所有信息:

Hibernate: 

        select
              classroom0_.id as id1_0_0_,
              classstude1_.id as id1_1_1_,
              classroom0_.number as number2_0_0_,
              classstude1_.FIRST_NAME as FIRST_NA2_1_1_,
              classstude1_.LAST_NAME as LAST_NAM3_1_1_,
              classstude1_.ClassroomID as Classroo4_1_1_,
              classstude1_.ClassroomID as Classroo4_0_0__,
              classstude1_.id as id1_1_0__ 
          from
              Classroom classroom0_ 
          left outer join
              STUDENT classstude1_ 
                  on classroom0_.id=classstude1_.ClassroomID

由於您有一個從ClassroomStudentOneToMany關系,使用單個查詢會導致Classroom字段對每一行重復。 現在假設您有第二個OneToMany關系,從ClassroomCourse 如果對於給定的Classroom您有 N 個Student和 M 個Course ,您將有一個返回 N+M 行的查詢,每行都包含Classroom的相同字段。

我發現它在https://vladmihalcea.com/eager-fetching-is-a-code-smell/下的EAGER fetching inconsistencies 中描述:

JPQL 和 Criteria 查詢默認選擇提取,因此為每個單獨的 EAGER 關聯發出輔助選擇。 關聯的數量越大,附加的單個 SELECTS 越多,它對我們的應用程序性能的影響就越大。

另請注意,Hibernate 同樣忽略了 HQL 查詢的提取注釋: https : //developer.jboss.org/wiki/HibernateFAQ-AdvancedProblems#jive_content_id_Hibernate_ignores_my_outerjointrue_or_fetchjoin_setting_and_fetches_an_association_lazily_using_n1_s

Hibernate 會忽略我的 outer-join="true" 或 fetch="join" 設置並使用 n+1 選擇懶惰地獲取關聯!

HQL 查詢始終忽略映射元數據中定義的外連接或 fetch="join" 設置。 此設置僅適用於使用 get() 或 load() 獲取的關聯、條件查詢和圖形導航。 如果您需要為 HQL 查詢啟用預取,請使用顯式 LEFT JOIN FETCH。

默認情況下 fetchtype 是惰性的,這意味着如果您不在請求中請求列表,Hibernate 將不會收集它。

在第一個請求中,您要求 Classroom r 的所有屬性,包括學生列表,以便 Hibernate 會延遲加載它們(在發現您需要它們之后)。

但是當 fetchtype 設置為eager hibernate 時,即使你不問它也會收集它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM