Ответ
Исключение 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! Сессии уже нет.
Способы решения:
- Инициализация в рамках сессии: Использовать
Hibernate.initialize(collection)или выполнить обращение к коллекции внутри транзакции. - Использование 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(); - Изменение FetchType на EAGER: Не рекомендуется по умолчанию из-за проблем с производительностью (N+1 query).
- Паттерн Open Session/EntityManager in View: Сессия остается открытой на время рендеринга представления. Имеет свои недостатки и требует аккуратной настройки.