【问题标题】:JPA & Hibernate: Eager loading performing subsequent queries to fetch all data, instead of doing it in just one queryJPA 和 Hibernate:渴望加载执行后续查询以获取所有数据,而不是仅在一个查询中执行
【发布时间】:2020-06-04 20:34:24
【问题描述】:

我有以下疑问。我想知道为什么在使用 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;

在获取 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=?

问题是:为什么不一次性获取所有信息?为什么它不在一个查询中获取信息?因为它已经在那里执行了 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

【问题讨论】:

    标签: java hibernate jpa eager-loading


    【解决方案1】:

    默认情况下 fetchtype 是惰性的,这意味着如果您不在请求中请求 List,Hibernate 将不会收集它。

    在第一个请求中,您要求提供 Classroom r 的所有属性,包括学生列表,这样 Hibernate 会延迟加载它们(在发现您需要它们之后)。

    但是当 fetchtype 设置为 eager hibernate 时,即使你不问它也会收集它。

    【讨论】:

      【解决方案2】:

      由于您有从ClassroomStudentOneToMany 关系,因此使用单个查询会导致Classroom 字段在每一行中重复。 现在假设您有第二个OneToMany 关系,从Classroom 到,比如说Course;如果,对于给定的Classroom,您有 N 个Students 和 M 个Courses,您将有一个返回 N+M 行的查询,每行都包含类 Classroom 的相同字段。

      【讨论】:

        【解决方案3】:

        我发现它在https://vladmihalcea.com/eager-fetching-is-a-code-smell/ 中有描述 在EAGER 获取不一致下:

        JPQL 和 Criteria 查询默认为 select 获取,因此为每个单独的 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_selects

        Hibernate 会忽略我的 outer-join="true" 或 fetch="join" 设置并使用 n+1 选择懒惰地获取关联!

        HQL 查询总是忽略映射元数据中定义的外连接或 fetch="join" 设置。此设置仅适用于使用 get() 或 load()、条件查询和图形导航获取的关联。如果您需要为 HQL 查询启用即时提取,请使用显式 LEFT JOIN FETCH。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-09-29
          • 2014-12-15
          • 2017-10-23
          相关资源
          最近更新 更多