middle
Что такое проблема N+1 и как её решить?
Проблема N+1 – ситуация, когда для получения данных выполняется 1 запрос для основной коллекции и N дополнительных запросов для загрузки связанных сущностей каждого элемента.
Пример проблемы
Пример
// 1 запрос: SELECT * FROM users
List<User> users = userRepository.findAll();
for (User user : users) {
// N запросов: SELECT * FROM orders WHERE user_id = ?
System.out.println(user.getOrders().size());
}
// Итого: 1 + N запросов. При 1000 пользователей -- 1001 запрос!
Решения
1. JOIN FETCH (JPQL)
Пример
@Query("SELECT u FROM User u JOIN FETCH u.orders WHERE u.status = :status")
List<User> findByStatusWithOrders(@Param("status") String status);
// 1 запрос с JOIN
2. @EntityGraph
Пример
@EntityGraph(attributePaths = {"orders"})
List<User> findByStatus(String status);
3. Batch fetching
Пример
spring.jpa.properties.hibernate.default_batch_fetch_size=25
Вместо N запросов будет ceil(N/25) запросов с WHERE user_id IN (?, ?, ..., ?).
4. DTO-проекция (лучшая производительность)
Пример
@Query("SELECT new com.example.dto.UserOrderSummary(u.id, u.name, COUNT(o)) " +
"FROM User u LEFT JOIN u.orders o GROUP BY u.id, u.name")
List<UserOrderSummary> findUserOrderSummaries();
Как обнаружить
Пример
spring.jpa.show-sql=true
logging.level.org.hibernate.SQL=DEBUG
На собеседовании: назовите проблему, покажите пример и минимум 2-3 решения. Частая ошибка – использовать JOIN FETCH с пагинацией (Hibernate загрузит все данные в память). Решение –
@BatchSizeили подзапрос.