Ответ
LazyInitializationException — это исключение времени выполнения в Hibernate, которое возникает при попытке доступа к лениво загружаемой (LAZY) коллекции или прокси-объекту вне активной сессии (Session) или транзакции.
Причина
Hibernate использует прокси-объекты для ленивых ассоциаций (например, @OneToMany(fetch = FetchType.LAZY)). Когда сессия закрыта, прокси не может выполнить SQL-запрос для подгрузки реальных данных, что и вызывает исключение.
Типичный пример:
@Entity
public class Order {
@Id
private Long id;
@OneToMany(mappedBy = "order", fetch = FetchType.LAZY)
private List<OrderItem> items; // Ленивая коллекция
// getters/setters
}
// В сервисном слое или контроллере:
Order order = session.get(Order.class, orderId);
session.close(); // Сессия закрывается здесь (например, после @Transactional)
// Попытка доступа к ленивой коллекции ВНЕ сессии:
order.getItems().size(); // LazyInitializationException!
Способы решения
-
Инициализация в рамках сессии: Используйте
Hibernate.initialize()до закрытия сессии.Order order = session.get(Order.class, orderId); Hibernate.initialize(order.getItems()); // Явная инициализация session.close(); order.getItems().size(); // OK -
Использование
JOIN FETCHв JPQL/HQL: Загрузите связанные данные одним запросом.String jpql = "SELECT o FROM Order o JOIN FETCH o.items WHERE o.id = :id"; Order order = entityManager.createQuery(jpql, Order.class) .setParameter("id", orderId) .getSingleResult(); -
Паттерн "Open Session in View" (OSIV): Сессия остается открытой на время всего HTTP-запроса (до рендеринга представления). Внимание: Этот подход может приводить к долгим соединениям с БД и считается антипаттерном в некоторых контекстах.
-
DTO-проекции или
@EntityGraph: Загружайте только необходимые данные, используя проекции запросов или аннотацию@EntityGraphв Spring Data JPA.
Важно: Изменение стратегии загрузки на EAGER (fetch = FetchType.EAGER) обычно не рекомендуется, так как это может привести к проблемам производительности (N+1 query) и загрузке ненужных данных.