JPA和Hibernate:及早加载执行后续查询以获取所有数据,而不是仅在一次查询中执行

7
我有如下疑问。当使用JPA和Hibernate时,进行ManyToOneOneToMany关系的Eager loading时,为什么会调用DB来获取实体信息,但同时还会产生后续查询以获取每个子元素。
另一方面,在使用JOIN FETCH查询时,它按照我的预期执行查询,并且一次性获取所有信息,因为fetchType被指定为“EAGER”。
以下是一个简单的例子:
我有一个学生类,它与教室类存在ManyToOne关系。
@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;

在获取Classroom对象时,它会执行一个查询来获取自身的信息,以及针对每个Classroom对象所包含的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=?

问题是:为什么它不一次性获取所有信息?为什么不在一个查询中获取所有信息?因为它已经执行了连接子句。
为什么只有在查询中显式添加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
3个回答

1
由于你在ClassroomStudent之间有一个OneToMany的关系,使用单个查询会导致Classroom字段在每行中重复出现。 现在假设你有第二个OneToMany的关系,从ClassroomCourse;如果对于给定的Classroom,你有N个Student和M个Course,那么你将得到一个返回N+M行的查询结果,每行都包含Classroom类的相同字段。

1

我在https://vladmihalcea.com/eager-fetching-is-a-code-smell/中找到了相关描述,其中提到EAGER fetching inconsistencies

JPQL和Criteria查询默认使用select fetching,因此对于每个单独的EAGER关联都会发出第二个SELECT。关联数量越大,额外的单独SELECT就越多,这将影响我们应用程序的性能。

此外,请注意Hibernate同样忽略HQL查询的fetching注释: https://developer.jboss.org/wiki/HibernateFAQ-AdvancedProblems#jive_content_id_Hibernate_ignores_my_outerjointrue_or_fetchjoin_setting_and_fetches_an_association_lazily_using_n1_selects

Hibernate忽略了我的outer-join="true"或fetch="join"设置,并使用n+1个selects懒惰地获取关联!HQL查询总是忽略映射元数据中定义的outer-join或fetch="join"设置。此设置仅适用于使用get()或load()、Criteria查询和图形导航获取的关联。如果您需要为HQL查询启用急切获取,请使用显式LEFT JOIN FETCH。

-1

默认情况下,fetchtype是懒加载的,这意味着如果您在请求中不要求列表,Hibernate将不会收集它。

在第一个请求中,您请求Classroom r的所有属性,包括学生列表,因此Hibernate将以惰性方式加载它们(在发现您需要它们后)。

但是当fetchtype设置为eager时,即使您没有要求它,Hibernate也会收集它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接