Что такое FetchType.LAZY в Hibernate и с какими проблемами он связан?

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

Ответ

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

Основные характеристики:

  • Используется по умолчанию для ассоциаций @OneToMany и @ManyToMany.
  • Цель — оптимизация производительности, чтобы избежать загрузки ненужных данных и лишних JOIN-запросов.

Пример объявления:

@Entity
public class Order {
    @OneToMany(fetch = FetchType.LAZY, mappedBy = "order")
    private List<OrderItem> items;
    // ...
}

Ключевая проблема — LazyInitializationException: Исключение возникает при попытке доступа к ленивой коллекции или прокси-объекту вне контекста открытой сессии Hibernate (например, после возврата из метода @Transactional или закрытия EntityManager).

Способы решения:

  1. Управление контекстом: Обеспечить открытую сессию в месте обращения (аннотация @Transactional в сервисном слое).
  2. Явная инициализация: Использовать Hibernate.initialize(collection) до закрытия сессии.
  3. JOIN FETCH в запросе: Использовать JOIN FETCH в JPQL или Criteria API для единовременной загрузки связанных данных.
    @Query("SELECT o FROM Order o JOIN FETCH o.items WHERE o.id = :id")
    Order findOrderWithItems(@Param("id") Long id);
  4. Entity Graphs: Использовать @EntityGraph для определения графа загружаемых атрибутов.

Важно: Слепое использование FetchType.EAGER часто приводит к проблеме N+1 select и снижению производительности.