Перейти к основному содержимому

Нетерпеливая/ленивая загрузка в спящем режиме

· 5 мин. чтения

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.

Чтобы получить код, обсуждаемый в этой статье, загляните в этот репозиторий .