Что делает стратегия загрузки FetchType.LAZY в Hibernate?

«Что делает стратегия загрузки FetchType.LAZY в Hibernate?» — вопрос из категории Hibernate, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

FetchType.LAZY — это стратегия отложенной загрузки, при которой связанная сущность или коллекция загружается из базы данных не сразу, а только при первом прямом обращении к ней. Это оптимизация производительности по умолчанию для ассоциаций @ManyToOne, @OneToMany и @ManyToMany.

Как это работает:

  • При загрузке родительской сущности Hibernate создает для lazy-ассоциации прокси-объект или пустую коллекцию-обертку.
  • Фактический SQL-запрос для загрузки данных выполняется только при вызове геттера или при попытке обхода коллекции.

Пример с аннотацией:

@Entity
public class Order {
    @Id
    private Long id;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "order")
    private List<OrderItem> items; // Коллекция не загрузится сразу
    // ...
}

Ключевые моменты и ограничения:

  1. Требует активного контекста persistence: Обращение к lazy-полю после закрытия сессии Hibernate (EntityManager / Session) вызовет LazyInitializationException. Работать с такими данными нужно внутри транзакции или с открытой сессией.
  2. N+1 проблема: Неосторожное обращение к коллекциям в цикле может привести к множеству дополнительных запросов. Решается через JOIN FETCH в JPQL или @EntityGraph.
  3. Не для всех типов: Для @OneToOne и @ManyToOne по умолчанию используется EAGER, но часто это меняют на LAZY.

Практическое правило: Используйте LAZY для всех ассоциаций и явно загружайте данные с помощью FETCH JOIN только когда они действительно нужны в конкретном сценарии.