Загружаешь список из N сущностей, а потом для каждой Hibernate делает отдельный SELECT за связанной коллекцией. 1 запрос на список + N запросов на детали = катастрофа на проде.
// Загружаем 100 заказов — получаем 101 запрос
List<Order> orders = orderRepo.findAll();
orders.forEach(o -> o.getItems().size()); // LAZY — N запросов здесь
JOIN FETCH vs @EntityGraph
JOIN FETCH — пишешь в JPQL явно:
@Query("SELECT o FROM Order o JOIN FETCH o.items WHERE o.status = :s")
List<Order> findWithItems(@Param("s") Status s);Гибко, но размножается: для каждой комбинации связей нужен свой метод.
@EntityGraph — декларативно, переиспользуемо:
@EntityGraph(attributePaths = {"items", "items.product"})
List<Order> findByStatus(Status status);⚠️ Узкие места
JOIN FETCH с коллекциями → если fetch двух коллекций одновременно → MultipleBagFetchException. Hibernate не умеет делать два JOIN FETCH на Bag-коллекции в одном запросе.
@EntityGraph → на больших данных легко получить декартово произведение в результирующем SQL и огромный набор дублирующихся строк. Используй DISTINCT или Set вместо List.
🐸 Библиотека собеса по Java
#spring