Ответ
FetchType.EAGER предписывает Hibernate загружать связанную сущность или коллекцию немедленно, вместе с владельцем. Хотя это кажется простым, оно часто приводит к проблемам с производительностью.
Основные проблемы:
-
Проблема N+1 SELECT (для коллекций
@OneToMany,@ManyToMany): Hibernate сначала выполняет 1 запрос для загрузки корневых сущностей, а затем по отдельному запросу (SELECT) для загрузки связанной коллекции каждой из них. Для 100 сущностей это 101 запрос.@Entity public class Author { @OneToMany(fetch = FetchType.EAGER, mappedBy = "author") private List<Book> books; // Загрузится отдельным запросом для каждого автора! } -
Избыточная загрузка данных (Cartesian Product): При использовании
JOINв одном запросе для загрузки@ManyToOne/@OneToOneсвязей сEAGERможет возникнуть декартово произведение, когда строк в результате значительно больше, чем реальных сущностей, что увеличивает нагрузку на сеть и память. -
Нарушение принципа "ленивой" загрузки по умолчанию: Для
@ManyToOneи@OneToOneстратегия по умолчанию —EAGER. Это решение JPA, и оно часто неоптимально. Рекомендуется всегда явно указыватьFetchType.LAZYдля всех ассоциаций, а загружать данные жадным образом только когда это необходимо для конкретного бизнес-сценария.
Решение: Используйте LAZY + явные запросы.
// 1. Использование JPQL с FETCH JOIN (рекомендуется)
@Query("SELECT DISTINCT a FROM Author a JOIN FETCH a.books WHERE a.id = :id")
Author findAuthorWithBooks(@Param("id") Long id);
// 2. Использование EntityGraph
@EntityGraph(attributePaths = {"books"})
@Query("SELECT a FROM Author a WHERE a.id = :id")
Author findAuthorWithBooks(@Param("id") Long id);
Вывод: EAGER — это заявка на данные. Используйте его осознанно, только если связь действительно нужна в 95+% случаев использования сущности. В остальных случаях предпочитайте LAZY с контролируемой жадной загрузкой через JOIN FETCH или EntityGraph.