Ответ
LazyInitializationException — это исключение времени выполнения в Hibernate/JPA, которое возникает при попытке доступа к лениво (LAZY) загруженной ассоциации (коллекции или прокси-объекту) вне контекста открытой сессии (или EntityManager).
Причина: Hibernate использует механизм прокси для ленивой загрузки. Прокси — это "обертка" над реальным объектом, которая при первом обращении к данным выполняет SQL-запрос. Этот запрос требует активной сессии. Если сессия закрыта, запрос выполнить невозможно.
Типичный пример:
@Entity
public class Order {
@Id
private Long id;
@OneToMany(mappedBy = "order", fetch = FetchType.LAZY) // Загрузка по требованию
private List<OrderItem> items;
// getters/setters
}
// Сервисный слой (транзакция закрыта на уровне DAO/Repository)
public Order getOrder(Long id) {
Order order = entityManager.find(Order.class, id); // Сессия открыта здесь
// entityManager.close() или транзакция завершается
return order;
}
// В вызывающем коде (например, контроллере или Thymeleaf шаблоне):
Order order = orderService.getOrder(1L);
order.getItems().size(); // LazyInitializationException! Сессии уже нет.
Способы решения:
- Инициализация в рамках сессии: Использовать
Hibernate.initialize(collection)перед закрытием сессии.Order order = em.find(Order.class, 1L); Hibernate.initialize(order.getItems()); // Выполняет запрос к БД сейчас - Использование JOIN FETCH в JPQL/HQL: Загрузить ассоциацию одним запросом.
String jpql = "SELECT o FROM Order o JOIN FETCH o.items WHERE o.id = :id"; Order order = em.createQuery(jpql, Order.class).setParameter("id", 1L).getSingleResult(); - Использование
@EntityGraph: Аннотация для динамического определения графа загружаемых сущностей. - Паттерн Open Session/EntityManager in View: Продление жизни сессии до конца HTTP-запроса (например, через фильтр). Используйте с осторожностью, может привести к проблемам с производительностью и долгим транзакциям.
- DTO-проекции: Загружать только нужные данные в специальный объект, минуя проблемы ленивой загрузки.
@Query("SELECT new com.example.OrderSummary(o.id, COUNT(i)) FROM Order o JOIN o.items i WHERE o.id = :id") OrderSummary findOrderSummary(@Param("id") Long id);