← Назад к вопросам
Что такое жадная загрузка?
2.0 Middle🔥 151 комментариев
#SOLID и паттерны проектирования#Spring Boot и Spring Data
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Жадная загрузка (Eager Loading) в Java/ORM
Жадная загрузка (Eager Loading) — это стратегия загрузки связанных данных, при которой все связанные объекты загружаются сразу же вместе с основным объектом, в одном или нескольких SQL запросах. Противоположность ленивой загрузке (Lazy Loading).
Концепция
// Пример: Автор и его Книги
Author author = authorRepository.findById(1);
// При жадной загрузке:
// 1. Загружается сам Author
// 2. СРАЗУ загружаются все его Books
author.getBooks(); // данные уже в памяти, нет доп. SQL запроса
Жадная загрузка в Hibernate/JPA
1. Аннотация @Eager на сущности:
@Entity
public class Author {
@Id
private Long id;
private String name;
@OneToMany(mappedBy = "author", fetch = FetchType.EAGER)
private List<Book> books; // будет загружена всегда
// геттеры/сеттеры
}
@Entity
public class Book {
@Id
private Long id;
private String title;
@ManyToOne(fetch = FetchType.EAGER)
private Author author; // будет загружена всегда
}
2. Explicit Join Fetch в запросе:
// Используя JPQL
public interface AuthorRepository extends JpaRepository<Author, Long> {
@Query("SELECT a FROM Author a JOIN FETCH a.books WHERE a.id = :id")
Author findByIdWithBooks(@Param("id") Long id);
}
// Использование
Author author = authorRepository.findByIdWithBooks(1L);
author.getBooks(); // уже загружено
3. QueryDSL пример:
public Author findWithBooks(Long id) {
return queryFactory
.selectFrom(author)
.leftJoin(author.books).fetchJoin()
.where(author.id.eq(id))
.fetchOne();
}
Жадная загрузка в Spring Data
public interface AuthorRepository extends JpaRepository<Author, Long> {
// Загрузить автора со всеми связанными данными
@EntityGraph(attributePaths = "books")
Optional<Author> findById(Long id);
// Или
@EntityGraph(attributePaths = {"books", "awards", "publisher"})
List<Author> findAll();
}
Жадная загрузка в MyBatis
@Select("""
SELECT
a.id, a.name,
b.id as book_id, b.title
FROM authors a
LEFT JOIN books b ON a.id = b.author_id
WHERE a.id = #{id}
""")
@Results({
@Result(property = "id", column = "id"),
@Result(property = "name", column = "name"),
@Result(property = "books", column = "id",
many = @Many(select = "getBooks"))
})
Author findWithBooks(Long id);
Практический пример: E-commerce
@Entity
public class Order {
@Id
private Long id;
private String orderNumber;
@ManyToOne(fetch = FetchType.EAGER)
private Customer customer; // жадная загрузка
@OneToMany(mappedBy = "order", fetch = FetchType.EAGER)
private List<OrderItem> items; // жадная загрузка
@ManyToOne(fetch = FetchType.EAGER)
private Warehouse warehouse; // жадная загрузка
}
@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
// Явно указываем, какие отношения загружать
@EntityGraph(attributePaths = {"customer", "items", "warehouse"})
Optional<Order> findById(Long id);
}
// Использование
Order order = orderRepository.findById(1L).orElse(null);
if (order != null) {
System.out.println(order.getCustomer().getName()); // нет доп. запроса
order.getItems().forEach(item -> System.out.println(item.getProduct()));
}
Жадная vs Ленивая загрузка
Жадная загрузка (EAGER):
Производится запрос:
SELECT a.*, b.* FROM authors a LEFT JOIN books b ON a.id = b.author_id
Результат:
- Один (или мало) запросов
- Все данные в памяти сразу
- Может загрузить МНОГО ненужных данных
Ленивая загрузка (LAZY):
Производится запрос:
SELECT * FROM authors WHERE id = 1
Затем при доступе к books:
SELECT * FROM books WHERE author_id = 1
Результат:
- Много запросов (N+1 problem)
- Данные загружаются по требованию
- Экономит память
Проблема N+1
// Плохо: ленивая загрузка создает N+1 запрос
List<Author> authors = authorRepository.findAll(); // Запрос 1
for (Author author : authors) { // Каждый вызов book.getAuthor() = новый запрос
System.out.println(author.getName());
author.getBooks().forEach(book -> System.out.println(book.getTitle()));
}
// Итого: 1 + N запросов!
// Хорошо: жадная загрузка решает N+1
@EntityGraph(attributePaths = "books")
List<Author> findAll(); // Один запрос с JOIN
Когда использовать жадную загрузку
- Связанные данные всегда нужны (например, Order → Customer)
- Выбирается одна сущность или небольшое количество
- Нужна максимальная производительность на чтение
- Хотим избежать N+1 problem
Когда избегать жадной загрузки
- Выбирается большой список сущностей
- Связанные данные не всегда нужны
- Может возникнуть декартово произведение (multiple @OneToMany)
- Нужна максимальная производительность памяти
Картезианское произведение (осторожно!)
@Entity
public class Author {
@OneToMany(fetch = FetchType.EAGER)
private List<Book> books; // много-к-многим
@OneToMany(fetch = FetchType.EAGER)
private List<Award> awards; // еще много-к-многим
}
// Результат запроса:
// books.size() * awards.size() дубликатов!
// Очень неэффективно
Итого
Жадная загрузка загружает все связанные данные сразу. Решает проблему N+1, но может загрузить много ненужных данных. Лучше использовать явно через @EntityGraph или JOIN FETCH, а не полагаться на FetchType.EAGER на аннотациях отношений.