Как join fetch помогает решить проблему LazyInitializationException?
Что проверяют
Понимаете ли вы, откуда берётся исключение, то есть жизненный цикл persistence context и прокси Hibernate, а не только рецепт «добавить fetch». Следом обычно спрашивают про альтернативы и про подводные камни JOIN FETCH с пагинацией.
Как отвечать
LazyInitializationException возникает, когда код обращается к неинициализированной ленивой ассоциации, а сессия, в которой сущность была загружена, уже закрыта. Hibernate вместо коллекции держит PersistentBag или прокси, и для загрузки ему нужен живой persistence context. Типичный случай: сервис с @Transactional вернул сущность, а контроллер уже вне транзакции полез в order.getItems().
Важная деталь про умолчания JPA: @OneToMany и @ManyToMany ленивые, а @ManyToOne и @OneToOne жадные.
JOIN FETCH решает проблему тем, что загружает ассоциацию тем же SQL-запросом: select o from Order o join fetch o.items where o.id = :id. Коллекция инициализирована ещё внутри сессии, и после её закрытия данные уже в памяти. Заодно уходит проблема N+1.
Альтернативы, которые стоит назвать:
@EntityGraph: то же самое декларативно, удобно в репозиториях Spring Data JPA;- DTO-проекция: запрос сразу возвращает нужные поля, сущности и ленивость вообще не участвуют;
@BatchSizeилиhibernate.default_batch_fetch_size: ленивая загрузка остаётся, но идёт пачками черезIN, без N+1;Hibernate.initialize(): явная инициализация внутри транзакции.
Где спотыкаются
Ставят FetchType.EAGER на всё подряд. Исключение пропадает, но каждая загрузка заказа тянет за собой весь граф связей, даже там, где он не нужен.
Полагаются на spring.jpa.open-in-view, который в Spring Boot включён по умолчанию, или включают hibernate.enable_lazy_load_no_trans. Первый держит сессию и соединение до конца обработки HTTP-запроса, второй открывает новую сессию на каждое обращение к ленивому полю, и N+1 возвращается в худшем виде.
Не знают ограничений JOIN FETCH. С пагинацией по коллекции Hibernate выбирает все строки и режет их в памяти с предупреждением в логе, а два join fetch по коллекциям типа List дают MultipleBagFetchException и декартово произведение.