Как сделать загрузку связанных сущностей ленивой или жадной в зависимости от запроса
Что проверяют
Понимаете ли вы, что FetchType в маппинге задаёт поведение для всех запросов сразу, и знаете ли способы выбрать загрузку под конкретный сценарий. Следом почти всегда идут N+1 и LazyInitializationException.
Как отвечать
Правило, от которого строится ответ: все связи в маппинге ленивые, а жадность включается в конкретном запросе. У @ManyToOne и @OneToOne по спецификации JPA по умолчанию EAGER, поэтому им LAZY прописывают явно, у @OneToMany и @ManyToMany он уже стоит по умолчанию.
Способы загрузить связь там, где она нужна:
JOIN FETCHв JPQL:select u from User u left join fetch u.orders where u.id = :id. Один запрос вместо N+1, Hibernate 6 сам убирает дубли корневых сущностей;@EntityGraph: в Spring Data@EntityGraph(attributePaths = "orders")на методе репозитория. Тот же запрос без графа остаётся ленивым, поэтому можно завести два метода под разные экраны. В JPA есть подсказкиjakarta.persistence.fetchgraphиjakarta.persistence.loadgraph, они различаются тем, что делать с атрибутами вне графа;- DTO-проекция: для чтения часто лучше выбрать только нужные колонки в record, тогда вопроса о загрузке связей нет вовсе;
- пакетная подгрузка:
@BatchSizeилиhibernate.default_batch_fetch_sizeпревращают N+1 в несколько запросов сIN (...), если связь всё-таки грузится лениво в цикле.
Проверять результат нужно по логу SQL, а не на глаз: включите вывод запросов или посчитайте их в тесте.
Где спотыкаются
Лечат LazyInitializationException переводом связи в EAGER. Ошибка пропадает, а связь начинает грузиться во всех запросах, включая списки, где она не нужна.
Полагаются на spring.jpa.open-in-view, который в Spring Boot включён по умолчанию. Ленивые связи догружаются при формировании ответа отдельными запросами, и N+1 уходит в прод незамеченным.
Делают JOIN FETCH коллекции вместе с пагинацией. Hibernate выбирает все строки и режет страницу в памяти, оставив в логе лишь предупреждение, а две коллекции типа List в одном fetch дают MultipleBagFetchException.