Как ленивая загрузка помогает улучшить производительность? - PullRequest
0 голосов
/ 08 января 2019

Допустим, я установил lazy = "true", и я пытаюсь получить дочерние записи для Employee (Employee является родительским, а FamilyMember является его дочерним)

Мой код продолжает сборку всех дочерних записей:

List<FamilyMember> familyMembers = new ArrayList<FamilyMember>();
Query query = session.createQuery("From FamilyMembers ...");
Iterator<FamilyMember> it = query.iterate();
while (it.hasNext()) {
    familyMembers.add(it.next());
}

Как я понимаю, вышеприведенная итерация просто создает коллекцию "прокси" объектов (скажем, список из 5 объектов FamilyMember). Мой вопрос - даже для создания коллекции необходимо знать размер результатов (И также значения ключевых полей дочерних записей для дальнейшего использования при фактической выборке дочерних записей из прокси-сервера), которые будут возвращены каким-либо запросом (который он получает явно ударив по БД), как hibernate помогает снизить накладные расходы на производительность?

Далее, предположим некоторый код, который получает физическую запись:

FamilyMember member = familyMembers.get(2);

Какой объект FamilyMember должен получить указанная выше строка? Означает ли это, что какое-то неявное отображение с ключевым полем для FamilyMember происходит за сценой? Если да, то почему это повышает производительность, когда мы на самом деле попадаем в таблицу для получения соответствующих ключей? Пожалуйста, уточните.

Ответы [ 2 ]

0 голосов
/ 11 января 2019

Итак, что вы имеете в виду, у вас есть Employee, и у него есть отношение ~ ToMany (которое лениво выбирается) к FamilyMember. У вас есть код, который перебирает всех членов семьи сотрудников, правильно?

т.е. как то так:

class Employee {
    @OneToMany(fetch=LAZY)
    List<FamilyMember> familyMembers;
}

и в вашем коде:

for (Employee employee: someEmployees) {
    for (FamilyMember member: employee.getFamilyMembrs()) {
        // do something
    }
}

что-то подобное? (В вашем примере кода вы просто выбираете FamilyMember, который не имеет ничего общего с отложенной выборкой)

Если это так, то Lazy Fetch НЕ собирается улучшать производительность.

Основная ситуация, в которой Lazy Fetch помогает в производительности, заключается в том, что отношения lazy-fetched не используются. Поэтому, когда Hibernate создает объект, он просто устанавливает в качестве отношения прокси-сервер, фактически не извлекая связанные объекты, тем самым сохраняя доступ к БД, передачу данных и построение объектов.

Однако в вашем случае вам необходимо получить доступ к отношениям, поэтому Hibernate не спасет вас от вышеуказанной работы. И, если ленивая выборка выполняется лениво, это означает, что ваша основная сущность будет сначала извлечена, а затем связь будет восстановлена, когда вы получите к ней доступ. Обычно это даже медленнее по сравнению с правильным соединением-выборкой для извлечения основного объекта + отношения одновременно.


Обновление: к вашему дополнительному вопросу о: как Hibernate справляется с такой ленивой загрузкой без предварительного запроса дочерних записей. Вот что происходит на высоком уровне:

Во-первых, hibernate по-разному работает с ленивой выборкой для разных типов отношений. В данном случае это OneToMany отношение. Что знает Hibernate, основываясь на сопоставлении: @OneToMany List<FamilyMember> familyMembers; это:

Таблица

FamilyMember (сторона "to many") будет иметь столбец (предположим, по умолчанию он называется EmployeeId), содержащий идентификатор текущей сущности (Employee, сторона "one to") )

Следовательно, прокси-сервер lazy-fetch, который собирается создать Hibernate: прокси-сервер коллекции при обращении к нему запросит SQL select * from FamilyMember where EmployeeId = :id, для которого :id будет предоставлен идентификатор текущей сущности.

Таким образом, при создании Employee вам не нужно запрашивать что-либо из его дочерних записей (FamilyMember).

Когда вы, скажем, выполните employee.getFamilyMembers().get(2);, метод .get(...) запустит прокси-сервер сбора данных с отложенной выборкой, чтобы заполнить данные, что приведет к получению ВСЕХ связанных FamilyMember записей (вместо только 3-й) и заполните коллекцию данными, а затем верните вам третий.

Чтобы понять поведение извлечения Hibernate, я бы посоветовал вам включить ведение журнала для SQL, выпущенного Hibernate (с помощью таких инструментов, как jdbcdslog или log4jdbc и т. Д.), И вы можете визуализировать, когда и что запрашивается Hibernate в ситуации.

0 голосов
/ 08 января 2019

Ленивая загрузка применяется к коллекциям / объектам, присутствующим в объекте, где отложенная загрузка обычно применяется посредством аннотации соединения.

Например, если ваш класс Employee соответствует приведенному ниже:

class Employee{
.......
@OneToMany(fetch=FetchType.LAZY)
private List<FamilyMember> familyList;
.......
}

Если вы извлекаете сущность Employee с помощью спящего режима, familyList заполняется данными только в том случае, если вы пытаетесь получить доступ к этим данным или явно вызываете hibernate.initialize для них. Это ускоряет запрос на выборку нескольких сотрудников, поскольку не загружает все списки FamilyMember, связанные с каждым из них.

Подробнее об этом можно прочитать на: Спящая ленивая загрузка

...