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 .