Может ли возникнуть проблема N+1 при стратегии загрузки EAGER в Hibernate?

«Может ли возникнуть проблема N+1 при стратегии загрузки EAGER в Hibernate?» — вопрос из категории Hibernate, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Да, может. Проблема N+1 запроса связана не с типом загрузки (LAZY или EAGER), а с способом выполнения загрузки связанных коллекций.

Как возникает проблема при FetchType.EAGER:

  1. Hibernate выполняет один запрос для получения всех сущностей Parent.
  2. Для каждого загруженного Parent он немедленно (из-за EAGER) выполняет отдельный SELECT для загрузки его коллекции children.

Пример сущности и проблемного запроса:

@Entity
public class Parent {
    @Id private Long id;
    @OneToMany(fetch = FetchType.EAGER) // EAGER-загрузка
    private List<Child> children;
}
// Запрос, вызывающий N+1:
List<Parent> parents = em.createQuery("SELECT p FROM Parent p", Parent.class)
                         .getResultList();

Это приведет к выполнению: 1 запрос (для всех Parent) + N запросов (для children каждого Parent).

Решение: Использовать JOIN FETCH в JPQL-запросе, который загружает данные за один SQL-запрос с JOIN.

List<Parent> parents = em.createQuery(
        "SELECT DISTINCT p FROM Parent p JOIN FETCH p.children", Parent.class)
        .getResultList();

Вывод: Стратегия EAGER не решает проблему N+1, а лишь меняет момент ее возникновения (с отложенного до немедленного). Для эффективной загрузки всегда нужно явно указывать JOIN FETCH или использовать @EntityGraph.