Eager/Lazy Loading In Hibernate
1. Вступление
При работе с ORM выборка / загрузка данных может быть классифицирована на два типа: нетерпеливый и ленивый.
В этой быстрой статье мы собираемся указать на различия и показать, что они могут быть использованы в Hibernate.
2. Maven Зависимости
Чтобы использовать Hibernate, давайте сначала определим основную зависимость в нашемpom.xml:
org.hibernate
hibernate-core
5.2.2.Final
Последнюю версию Hibernate можно найтиhere.
3. Стремительная и ленивая загрузка
Первое, что мы должны обсудить здесь, это то, что ленивая загрузка и нетерпеливая загрузка:
-
Eager Loading - это шаблон проектирования, в котором инициализация данных происходит на месте
-
Lazy Loading - это шаблон проектирования, который используется для отсрочки инициализации объекта, насколько это возможно.
Давайте посмотрим, как это работает на некоторых примерах:
КлассUserLazy:
@Entity
@Table(name = "USER")
public class UserLazy implements Serializable {
@Id
@GeneratedValue
@Column(name = "USER_ID")
private Long userId;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "user")
private Set orderDetail = new HashSet();
// standard setters and getters
// also override equals and hashcode
}
КлассOrderDetail:
@Entity
@Table (name = "USER_ORDER")
public class OrderDetail implements Serializable {
@Id
@GeneratedValue
@Column(name="ORDER_ID")
private Long orderId;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="USER_ID")
private UserLazy user;
// standard setters and getters
// also override equals and hashcode
}
ОдинUser может иметь несколькоOrderDetails. In eager loading strategy, if we load the User data, it will also load up all orders associated with it and will store it in a memory.
Но если включена отложенная загрузка, если мы извлечемUserLazy, данныеOrderDetail не будут инициализированы и загружены в память, пока к ним не будет сделан явный вызов.
В следующем разделе мы увидим, как приведенный выше пример реализован в Hibernate.
4. Загрузка конфигурации
В этом разделе мы рассмотрим, как мы можем настроить стратегии выборки в Hibernate. Мы будем использовать примеры из предыдущего раздела.
Ленивая загрузка может быть просто включена с помощью следующего параметра аннотации:
fetch = FetchType.LAZY
Для использования Eager Fetching используется следующий параметр:
fetch = FetchType.EAGER
Для настройки нетерпеливой загрузки мы использовали класс-близнецUserLazy под названиемUserEager.
В следующем разделе мы рассмотрим различия между двумя типами выборки.
5. Различия
Как мы уже упоминали, основное различие между двумя типами выборки - это момент, когда данные загружаются в память.
Давайте посмотрим на этот пример:
List users = sessionLazy.createQuery("From UserLazy").list();
UserLazy userLazyLoaded = users.get(3);
return (userLazyLoaded.getOrderDetail());
При ленивом подходе к инициализацииorderDetailSet будет инициализирован только тогда, когда он явно вызывается с помощью геттера или какого-либо другого метода, как показано в приведенном выше примере:
UserLazy userLazyLoaded = users.get(3);
Но при активном подходе вUserEager он будет немедленно инициализирован в первой строке приведенного выше примера:
List user = sessionEager.createQuery("From UserEager").list();
Для ленивой загрузки используется прокси-объект и запускается отдельный запрос SQL для загрузкиorderDetailSet.
Идея отключения прокси или отложенной загрузки считается плохой практикой в Hibernate. Это может привести к тому, что большое количество данных будет извлечено из базы данных и сохранено в памяти, независимо от необходимости в этом.
Следующий метод может быть использован для проверки вышеуказанной функциональности:
Hibernate.isInitialized(orderDetailSet);
Теперь важно взглянуть на запросы, которые генерируются в любом случае:
true
Приведенная выше настройка вfetching.hbm.xml показывает сгенерированные запросы SQL. Если вы посмотрите на вывод консоли, то сможете увидеть сгенерированные запросы.
Для отложенной загрузки запрос, который создается для загрузки данныхUser:
select user0_.USER_ID as USER_ID1_0_, ... from USER user0_
Однако при активной загрузке мы увидели соединение, выполняемое с помощьюUSER_ORDER:
select orderdetai0_.USER_ID as USER_ID4_0_0_, orderdetai0_.ORDER_ID as ORDER_ID1_1_0_, orderdetai0_ ...
from USER_ORDER orderdetai0_ where orderdetai0_.USER_ID=?
Вышеупомянутый запрос генерируется для всехUsers, в результате чего используется гораздо больше памяти, чем при другом подходе.
6. Преимущества и недостатки
6.1. Ленивая Загрузка
Преимущества:
-
Время начальной загрузки намного меньше, чем при другом подходе
-
Меньшее потребление памяти, чем в другом подходе
Недостатки:
-
Отложенная инициализация может повлиять на производительность в нежелательные моменты
-
В некоторых случаях вам нужно обращаться с лениво инициализированными объектами с особой тщательностью, иначе вы можете получить исключение
6.2. Жадная загрузка:
Преимущества:
-
Отсутствие влияния на производительность при отложенной инициализации
Недостатки:
-
Длительное время начальной загрузки
-
Загрузка слишком большого количества ненужных данных может повлиять на производительность
7. Ленивая загрузка в гибернации
Hibernate applies lazy loading approach on entities and associations by providing a proxy implementation классов.
Hibernate перехватывает вызовы объекта, заменяя его прокси-сервером, полученным из класса объекта. В нашем примере, когда запрошенная информация отсутствует, она будет загружена из базы данных до того, как управление будет передано реализации классаUser.
Следует также отметить, что когда ассоциация представлена как класс коллекции (в приведенных выше примерах она представлена какSet<OrderDetail> orderDetailSet), тогда создается оболочка и заменяется исходной коллекцией.
Чтобы узнать больше о шаблоне проектирования прокси, вы можете обратиться кhere.
8. Заключение
В этой статье мы показали примеры двух основных типов выборки, которые используются в Hibernate.
Для продвинутого уровня экспертизы вы можете посмотреть на официальном сайте Hibernate. Чтобы получить код, обсуждаемый в этой статье, взгляните на этотrepository.