FetchType в Hibernate
FetchType — подсказка провайдеру о моменте загрузки связи.
LAZY— связь загружается при первом использовании;EAGER— связь должна быть доступна после загрузки владельца, но способ получения не задан.
Стандартные значения по умолчанию:
| Связь | По умолчанию |
|---|---|
@ManyToOne, @OneToOne | EAGER |
@OneToMany, @ManyToMany | LAZY |
На практике связи обычно объявляют LAZY и задают fetch-план на уровне конкретного запроса.
FetchType не равен SQL-стратегии
EAGER не гарантирует один JOIN: провайдер может выполнить дополнительные запросы. LAZY также не гарантирует прокси для каждого случая. То, как получить данные, выбирают через:
join fetchв JPQL;- entity graph;
- DTO-проекцию;
- batch fetching;
- явную инициализацию внутри транзакции.
select distinct o
from Order o
left join fetch o.lines
where o.id = :idДва fetch join по коллекциям могут создать декартово размножение. Пагинация по коллекционному fetch join также часто выполняется неверно или в памяти.
Типичные сбои
- Кейс N plus 1 при обходе иерархии;
LazyInitializationExceptionпосле закрытия контекста;- огромный граф из-за EAGER;
- дубли корневых объектов при join;
- неожиданная сериализация всего графа и циклических связей.
Вопросы для повторения
- EAGER означает один запрос? Нет.
- Почему LAZY не решает N+1? Каждое обращение к связи всё равно может вызвать отдельный SQL.
- Где загружать нужный граф? В транзакционном сервисе запросом, соответствующим use case.
- Можно ли лениво загружать после закрытия контекста? Нет без повторного присоединения или специального анти-паттерна.
- Чем DTO полезнее сущности для чтения? Он выбирает ровно нужные поля и не несёт ленивого графа.
Связано: Persistence Context, Entity Graph, Проблема N plus 1 Select Hibernate.