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

FetchMode в спящем режиме

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

1. Введение

В этом кратком руководстве мы рассмотрим различные значения FetchMode , которые мы можем использовать в аннотации @ org.hibernate.annotations.Fetch .

2. Настройка примера

В качестве примера мы будем использовать следующую сущность Customer всего с двумя свойствами — идентификатором и набором заказов:

@Entity
public class Customer {

@Id
@GeneratedValue
private Long id;

@OneToMany(mappedBy = "customer")
@Fetch(value = FetchMode.SELECT)
private Set<Order> orders = new HashSet<>();

// getters and setters
}

Кроме того, мы создадим сущность Order , состоящую из идентификатора, имени и ссылки на Customer .

@Entity
public class Order {

@Id
@GeneratedValue
private Long id;

private String name;

@ManyToOne
@JoinColumn(name = "customer_id")
private Customer customer;

// getters and setters
}

В каждом из следующих разделов мы будем извлекать клиента из базы данных и получать все его заказы:

Customer customer = customerRepository.findById(id).get();
Set<Order> orders = customer.getOrders();

3. FetchMode.SELECT

В нашей сущности Customer мы аннотировали свойство orders аннотацией @Fetch :

@OneToMany
@Fetch(FetchMode.SELECT)
private Set<Orders> orders;

Мы используем @Fetch , чтобы описать, как Hibernate должен извлекать свойство при поиске клиента.

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

Это означает, что для первой строки:

Customer customer = customerRepository.findById(id).get();

Мы не увидим соединение с таблицей заказов:

Hibernate: 
select ...from customer
where customer0_.id=?

И это для следующей строки:

Set<Order> orders = customer.getOrders();

Мы увидим последующие запросы для связанных заказов:

Hibernate: 
select ...from order
where order0_.customer_id=?

Hibernate FetchMode.SELECT генерирует отдельный запрос для каждого заказа , который необходимо загрузить.

В нашем примере это дает один запрос для загрузки клиентов и пять дополнительных запросов для загрузки коллекции заказов.

Это известно как проблема выбора n + 1. Выполнение одного запроса вызовет n дополнительных запросов.

3.1. @ Размер партии

FetchMode.SELECT имеет необязательную аннотацию конфигурации с использованием аннотации @BatchSize :

@OneToMany
@Fetch(FetchMode.SELECT)
@BatchSize(size=10)
private Set<Orders> orders;

Hibernate попытается загрузить коллекцию заказов партиями, определяемыми параметром размера .

В нашем примере у нас всего пять заказов, поэтому одного запроса достаточно.

Мы по-прежнему будем использовать тот же запрос:

Hibernate:
select ...from order
where order0_.customer_id=?

Но он будет запущен только один раз. Теперь у нас всего два запроса: один для загрузки клиента и один для загрузки коллекции заказов.

4. FetchMode.JOIN

В то время как FetchMode.SELECT загружает отношения лениво, FetchMode.JOIN загружает их с готовностью, скажем, через соединение:

@OneToMany
@Fetch(FetchMode.JOIN)
private Set<Orders> orders;

В результате получается всего один запрос как для Customer , так и для их Order s:

Hibernate: 
select ...
from
customer customer0_
left outer join
order order1
on customer.id=order.customer_id
where
customer.id=?

5. FetchMode.SUBSELECT

Поскольку свойство orders является коллекцией, мы могли бы также использовать FetchMode.SUBSELECT :

@OneToMany
@Fetch(FetchMode.SUBSELECT)
private Set<Orders> orders;

Мы можем использовать SUBSELECT только с коллекциями.

С этой настройкой мы возвращаемся к одному запросу для клиента:

Hibernate: 
select ...
from customer customer0_

И один запрос для Order s, на этот раз с использованием подзапроса:

Hibernate: 
select ...
from
order order0_
where
order0_.customer_id in (
select
customer0_.id
from
customer customer0_
)

6. FetchMode против FetchType

В общем, FetchMode определяет, как Hibernate будет извлекать данные (путем выбора, объединения или подвыборки). FetchType , с другой стороны, определяет, будет ли Hibernate загружать данные быстро или лениво.

Точные правила между этими двумя таковы:

  • если код не устанавливает FetchMode , по умолчанию используется JOIN , а FetchType работает как определено
  • с установленным FetchMode.SELECT или FetchMode.SUBSELECT FetchType также работает, как определено ``
  • с установленным FetchMode.JOIN FetchType игнорируется, и запрос всегда активен

Для получения дополнительной информации см . Нетерпеливая/ленивая загрузка в спящем режиме .

7. Заключение

В этом руководстве мы узнали о различных значениях FetchMode , а также о том, как они связаны с FetchType .

Как всегда, весь исходный код доступен на GitHub .