junior
Что такое FetchType.LAZY и FetchType.EAGER?
FetchType определяет, когда Hibernate загружает связанные сущности из БД. EAGER загружает сразу вместе с основной сущностью, LAZY — только при первом обращении.
Поведение
Пример
// EAGER — загрузка сразу
@ManyToOne(fetch = FetchType.EAGER)
private Department department;
// При загрузке User → сразу загружается Department
// LAZY — загрузка по требованию
@ManyToOne(fetch = FetchType.LAZY)
private Department department;
// При загрузке User → department НЕ загружается
// department загрузится при вызове user.getDepartment().getName()
Значения по умолчанию
| Тип связи | FetchType по умолчанию |
|---|---|
@ManyToOne |
EAGER |
@OneToOne |
EAGER |
@OneToMany |
LAZY |
@ManyToMany |
LAZY |
Как LAZY работает технически
Hibernate подставляет вместо реальной сущности прокси-объект (наследник сущности, сгенерированный через Byte Buddy). При первом вызове метода прокси выполняет SQL-запрос:
Пример
User user = session.find(User.class, 1L);
// user.department — это прокси (не настоящий Department)
String deptName = user.getDepartment().getName();
// ↑ Здесь прокси выполняет SQL: SELECT * FROM departments WHERE id = ?
Важное
- Всегда явно указывайте
FetchType.LAZYдля@ManyToOneи@OneToOne(по умолчанию EAGER!) - LAZY — это подсказка, не требование; Hibernate может загрузить eager при необходимости
- LAZY работает через прокси-объекты — вызов метода на прокси инициирует SQL-запрос
- LAZY требует открытой Session — обращение к прокси после закрытия Session → LazyInitializationException
Частые ошибки
- Оставлять
@ManyToOneбез явногоfetch = LAZY— по умолчанию EAGER, загрузка всех связей каскадно - EAGER для коллекций — загрузка
List<Order>при каждом чтении User — катастрофа для производительности - Множественные EAGER-коллекции — Hibernate не может выполнить один запрос для двух EAGER-коллекций; получается Cartesian product или MultipleBagFetchException
- Проверка
== nullдля LAZY-прокси — прокси никогда не null (даже если записи в БД нет); используйтеHibernate.isInitialized()
Как используется в 2026
- Правило: всё LAZY, загружать через JOIN FETCH или EntityGraph по необходимости
- Spring Boot
spring.jpa.open-in-view=false— отключение OSIV заставляет явно загружать данные в сервисном слое - Hibernate 6 bytecode enhancement — позволяет LAZY для
@Basicполей (не загружать тяжёлые BLOB/CLOB до обращения)
На собеседовании: правило номер один — всё LAZY. Покажите, что знаете дефолты:
@ManyToOneи@OneToOneпо умолчанию EAGER. Объясните, как работает прокси и что произойдёт при обращении к LAZY-связи после закрытия Session. Упомяните JOIN FETCH как основной способ загрузки нужных данных.