Что такое жадная загрузка (eager loading) в Hibernate?

Ответ

Жадная загрузка (Eager Loading) — это стратегия загрузки связанных сущностей в Hibernate, при которой данные загружаются немедленно и вместе с родительской сущностью в рамках одного запроса к базе данных (или с помощью JOIN). Это поведение по умолчанию для коллекций, аннотированных @ElementCollection, и для связей @ManyToOne и @OneToOne.

Как работает: При загрузке сущности Hibernate также загружает все её связи, помеченные как FetchType.EAGER, используя JOIN-запрос или дополнительные SELECT-запросы.

Пример:

@Entity
public class Author {
    @Id
    @GeneratedValue
    private Long id;
    private String name;

    // ЖАДНАЯ загрузка (по умолчанию для @OneToMany)
    @OneToMany(fetch = FetchType.EAGER, mappedBy = "author")
    private List<Book> books = new ArrayList<>();
    // Геттеры и сеттеры
}

@Entity
public class Book {
    @Id
    @GeneratedValue
    private Long id;
    private String title;

    @ManyToOne(fetch = FetchType.EAGER) // ЖАДНАЯ загрузка (по умолчанию)
    private Author author;
    // Геттеры и сеттеры
}

Что происходит при запросе:

// Hibernate выполнит запрос с JOIN (или несколько запросов), чтобы сразу получить Author и все его Books
Author author = entityManager.find(Author.class, 1L);
// Коллекция books уже загружена в память и доступна
List<Book> authorBooks = author.getBooks();
Плюсы и минусы: Плюсы Минусы
Связанные данные доступны сразу, нет задержек (N+1 проблемы на этом уровне). Производительность: Загрузка большого объема ненужных данных, если связи не используются.
Проще в понимании (меньше ленивых исключений LazyInitializationException). Потребление памяти: Все загруженные объекты хранятся в контексте persistence.
Риск Cartesian Product: При множественных EAGER-связях один запрос может вернуть огромное количество дублирующихся строк.

Рекомендация: Используйте жадную загрузку только для связей, которые нужны практически всегда. В большинстве случаев предпочтительна ленивая загрузка (FetchType.LAZY) с последующей явной загрузкой через JOIN FETCH в JPQL или @EntityGraph при необходимости.

Ответ 18+ 🔞

Э, слушай, смотри, вот эта вся хуйня с жадной загрузкой в Hibernate. Представь, ты заходишь в магазин за хлебом, а тебе продавец, такой ебаный максималист, сразу впихивает в руки и хлеб, и батон, и булку, и ещё мешок муки нахуй, потому что «а вдруг пригодится». Вот это и есть Eager Loading, ёпта.

По умолчанию, эта падла Hibernate так и делает для некоторых связей — @OneToMany, например. Ты просишь одного автора, а он тебе в нагрузку сразу всех его книг притаскивает, одним махом. JOIN'ом или кучей SELECT'ов, но сразу, блядь, всё.

Смотри, как это выглядит в коде, тут всё чётко:

@Entity
public class Author {
    @Id
    @GeneratedValue
    private Long id;
    private String name;

    // Вот этот самый ебучий жадный флаг. FetchType.EAGER. Он тут по дефолту.
    @OneToMany(fetch = FetchType.EAGER, mappedBy = "author")
    private List<Book> books = new ArrayList<>();
    // Геттеры и сеттеры
}

И что потом происходит? А вот что:

// Ты думаешь: "Найди мне автора с id=1"
Author author = entityManager.find(Author.class, 1L);
// А Hibernate уже про себя думает: "Ага, автор... Ну нахуй, давай-ка я ему и все книги этого долбоёба тоже прицеплю!"
// И бабахает запрос с JOIN. И книги уже тут, в памяти.
List<Book> authorBooks = author.getBooks(); // Всё готово, бери и пользуйся, небось.

Ну и что мы имеем в сухом остатке? Плюсы и минусы, блядь.

Что хорошего (типа плюсы) Что пиздецового (минусы, их больше)
Данные под рукой сразу. Не будешь потом, как лох, ловить LazyInitializationException где-нибудь в JSP. Производительность ебнулась. Загрузил кучу данных, которые, может, вообще не нужны. Зачем мне 100500 книг автора, если я только имя его посмотреть хотел?
Проще для понимания, особенно если мозги на ленивую загрузку не заточены. Память жрёт, как не в себя. Все эти объекты в persistence context висят, как груз.
Риск получить декартово произведение, ёб твою мать! Если у сущности несколько EAGER-связей, запрос может превратиться в адскую телегу с триллионом строк, все поля повторятся до охуения.

Так что, вывод, блядь, простой: используй эту жадность только тогда, когда связь действительно, блядь, практически всегда нужна. А так — ставь везде FetchType.LAZY, а если понадобится — явно дохуячивай данные через JOIN FETCH в JPQL или через @EntityGraph. Не будь, как тот Герасим из рассказа, который сразу всё решил радикально, а потом совесть ебала.