В каком случае возникает LazyInitializationException в Hibernate/JPA?

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

Ответ

Исключение LazyInitializationException возникает при попытке доступа к лениво загружаемой (FetchType.LAZY) коллекции или ассоциации сущности вне контекста активной сессии (EntityManager или Hibernate Session).

Почему это происходит? Hibernate для ленивых ассоциаций использует прокси-объекты. При первом обращении к такому прокси (например, вызов getItems()) он должен выполнить запрос к БД, для чего требуется живая сессия. Если сессия закрыта — возникает это исключение.

Типичный сценарий ошибки:

@Entity
public class Order {
    @Id
    private Long id;
    @OneToMany(fetch = FetchType.LAZY, mappedBy = "order")
    private List<OrderItem> items; // Ленивая коллекция
    // getters/setters
}

// В сервисе или DAO
@Transactional // Сессия открыта только внутри этого метода
public Order getOrder(Long id) {
    Order order = entityManager.find(Order.class, id);
    return order; // Сессия закрывается при выходе из @Transactional
}

// Позже, в другом месте (например, в контроллере или представлении)
Order order = orderService.getOrder(1L);
order.getItems().size(); // LazyInitializationException! Сессии уже нет.

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

  1. Инициализация в рамках сессии: Использовать Hibernate.initialize(collection) или выполнить обращение к коллекции внутри транзакции.
  2. Использование JOIN FETCH в запросе: Наиболее эффективный способ.
    String jpql = "SELECT o FROM Order o JOIN FETCH o.items WHERE o.id = :id";
    Order order = entityManager.createQuery(jpql, Order.class)
                               .setParameter("id", id)
                               .getSingleResult();
  3. Изменение FetchType на EAGER: Не рекомендуется по умолчанию из-за проблем с производительностью (N+1 query).
  4. Паттерн Open Session/EntityManager in View: Сессия остается открытой на время рендеринга представления. Имеет свои недостатки и требует аккуратной настройки.