1. Обзор
При работе с ORM выборку/загрузку данных можно разделить на два типа: нетерпеливую и ленивую.
В этом кратком руководстве мы собираемся указать на различия и показать, как мы можем использовать их в Hibernate.
2. Зависимости Maven
Чтобы использовать Hibernate, давайте сначала определим основную зависимость в нашем pom.xml
:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.2.2.Final</version>
</dependency>
Последнюю версию Hibernate можно найти здесь .
3. Нетерпеливая и ленивая загрузка
Первое, что мы должны обсудить здесь, это то, что такое отложенная загрузка и нетерпеливая загрузка:
- Eager 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> 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
}
У одного пользователя
может быть несколько OrderDetails
. В стратегии быстрой загрузки, если мы загружаем данные пользователя
, он также загружает все связанные с ним заказы и сохраняет их в памяти.
Но когда мы включаем ленивую загрузку, если мы подтягиваем UserLazy
, данные OrderDetail
не будут инициализированы и загружены в память, пока мы не выполним явный вызов.
В следующем разделе мы увидим, как мы реализуем пример в Hibernate.
4. Загрузка конфигурации
Давайте посмотрим, как настроить стратегии выборки в Hibernate.
Мы можем включить отложенную загрузку, используя этот параметр аннотации:
fetch = FetchType.LAZY
Для Eager Fetching мы используем этот параметр:
fetch = FetchType.EAGER
Чтобы настроить Eager Loading, мы использовали класс -близнец UserLazy под названием
UserEager
.
В следующем разделе мы рассмотрим различия между двумя типами выборки.
5. Отличия
Как мы уже упоминали, основное различие между двумя типами выборки заключается в моменте загрузки данных в память.
Давайте посмотрим:
List<UserLazy> users = sessionLazy.createQuery("From UserLazy").list();
UserLazy userLazyLoaded = users.get(3);
return (userLazyLoaded.getOrderDetail());
При ленивой инициализации orderDetailSet
будет инициализирован только тогда, когда мы явно вызовем его, используя геттер или какой-либо другой метод:
UserLazy userLazyLoaded = users.get(3);
Но при нетерпеливом подходе в UserEager
он будет инициализирован сразу в первой строке:
List<UserEager> user = sessionEager.createQuery("From UserEager").list();
Для ленивой загрузки мы используем прокси-объект и запускаем отдельный SQL-запрос для загрузки orderDetailSet
.
Идея отключения прокси или отложенной загрузки считается плохой практикой в Hibernate. Это может привести к извлечению и хранению большого количества данных, независимо от необходимости в этом.
Мы можем использовать следующий метод для проверки функциональности:
Hibernate.isInitialized(orderDetailSet);
Теперь давайте посмотрим на запросы, сгенерированные в любом случае:
<property name="show_sql">true</property>
Приведенный выше параметр в файле fetching.hbm.xml
показывает сгенерированные запросы SQL. Если мы посмотрим на вывод консоли, мы увидим сгенерированные запросы.
Для отложенной загрузки вот запрос, сгенерированный для загрузки данных пользователя
:
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=?
Приведенный выше запрос генерируется для всех пользователей
, что приводит к гораздо большему использованию памяти, чем при другом подходе.
6. Преимущества и недостатки
6.1. Ленивая загрузка
Преимущества:
- Гораздо меньшее время начальной загрузки, чем в другом подходе
- Меньшее потребление памяти, чем в другом подходе
Недостатки:
- Отложенная инициализация может повлиять на производительность в нежелательные моменты.
- В некоторых случаях нам нужно обращаться с лениво инициализированными объектами с особой осторожностью, иначе мы можем получить исключение.
6.2. Нетерпеливая загрузка
Преимущества:
- Отсутствие влияния на производительность, связанного с задержкой инициализации
Недостатки:
- Долгое время первоначальной загрузки
- Загрузка слишком большого количества ненужных данных может повлиять на производительность
7. Ленивая загрузка в спящем режиме
Hibernate применяет ленивую загрузку сущностей и ассоциаций, предоставляя прокси-реализацию классов.
Hibernate перехватывает вызовы объекта, заменяя его прокси-сервером, полученным из класса объекта. В нашем примере отсутствующая запрошенная информация будет загружена из базы данных до того, как управление будет передано реализации класса User .
Следует также отметить, что когда ассоциация представлена как класс коллекции (в приведенных выше примерах она представлена как Set<OrderDetail> orderDetailSet
), создается оболочка и заменяется исходной коллекцией.
Чтобы узнать больше о шаблоне проектирования прокси, обратитесь сюда .
8. Заключение
В этой статье мы показали примеры двух основных типов выборки, используемых в Hibernate.
Для получения дополнительной информации посетите официальный сайт Hibernate.
Чтобы получить код, обсуждаемый в этой статье, загляните в этот репозиторий .