JPA 和 Hibernate:急切加载执行后续查询以获取所有数据,而不是仅在一个查询中执行此操作

Dav*_*tel 7 java hibernate jpa eager-loading

我有以下疑问。我想知道为什么在使用 JPA 和 Hibernate 时,在ManyToOneOneToMany关系中执行预加载时,它会调用数据库以获取实体信息,但另外还会生成后续查询来获取每个子项。

另一方面,当使用带有 JOIN FETCH 的查询时,它会按照我的预期执行查询,一次获取所有信息,因为 fetchType 表示为“EAGER”。

这是一个简单的例子:

我有一个班级学生,它与班级教室有多对一的关系。

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

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

另一边有一个名为 Classroom 的类,如下:

@Entity
public class Classroom {

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

在获取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
Run Code Online (Sandbox Code Playgroud)

然后,它执行下一个查询的次数与分配给每个教室的学生的次数相同。

  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=?
Run Code Online (Sandbox Code Playgroud)

问题是:为什么不一次性获取所有信息?为什么它不在一次查询中获取信息?因为它已经在那里执行 Join 子句

为什么在查询中显式添加Fetch时,它会执行所请求的操作?

例如:

SELECT
         r 
     FROM
         Classroom r 
     LEFT JOIN FETCH
         r.classStudents */ 
Run Code Online (Sandbox Code Playgroud)

然后,输出查询确实在一个查询中获取所有信息:

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
Run Code Online (Sandbox Code Playgroud)

Les*_*iak 1

我发现它在https://vladmihalcea.com/eager-fetching-is-a-code-smell/\n在EAGER 获取不一致下进行了描述:

\n
\n

JPQL 和 Criteria 查询都默认选择获取,因此为每个单独的 EAGER 关联发出辅助选择。Associations\xe2\x80\x99 数字越大,额外的单独 SELECT 就越多,对我们应用程序性能的影响就越大。

\n
\n

另请注意,Hibernate 同样会忽略 HQL 查询的获取注释:\n 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

\n
\n

Hibernate 忽略我的 external-join="true" 或 fetch="join" 设置并使用 n+1 选择来延迟获取关联!

\n

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

\n
\n