JPA & Hibernate: готовая загрузка, выполняющая последующие запросы для извлечения всех данных, вместо того, чтобы делать это только в одном запросе - PullRequest
2 голосов
/ 20 февраля 2020

У меня есть следующие сомнения. Я хотел бы знать, почему при использовании JPA и Hibernate при выполнении загрузки Eager в отношениях ManyToOne или OneToMany он вызывает 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 он выполняет один запрос для получения информации от себя и последующие запросы для получения информации о каждом ученике, содержащейся в StudentsList для каждого classRoom. объект.

Первый запрос:

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

Ответы [ 3 ]

0 голосов
/ 20 февраля 2020

Поскольку у вас есть отношение OneToMany от Classroom до Student, использование одного запроса приведет к тому, что поля Classroom будут повторяться для каждой строки. Теперь представьте, что у вас есть второе OneToMany отношение от Classroom до, скажем, Course; если для данного Classroom у вас есть N Student s и M Course s, у вас будет запрос, возвращающий N + M строк, каждая из которых содержит одинаковые поля класса Classroom.

0 голосов
/ 20 февраля 2020

Я обнаружил, что это описано в https://vladmihalcea.com/eager-fetching-is-a-code-smell/ в EAGER извлечения несоответствий :

В запросах 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_selects

Hibernate игнорирует мой параметр external-join = "true" или fetch = "join" и лениво выбирает ассоциацию, используя n + 1 выбор!

HQL-запросы всегда игнорируют параметр external-join или fetch = "join", определенный в метаданных отображения. Этот параметр применяется только к ассоциациям, полученным с использованием get () или load (), запросов Criteria и навигации по графику. Если вам нужно активировать выборку для запроса HQL, используйте явную LEFT JOIN FETCH.

0 голосов
/ 20 февраля 2020

По умолчанию fetchtype является ленивым, это означает, что если вы не запрашиваете список в своем запросе, Hibernate не будет собирать его.

В первом запросе вы запрашиваете все атрибуты Classroom r, включая список Student, поэтому Hibernate будет загружать их лениво (после того, как узнает, что они вам нужны).

Но когда fetchtype настроен на нетерпеливый режим гибернации, собирайте его, даже если вы его не спрашиваете.

...