Добавить в корзинуПозвонить
Найти в Дзене
Bereshpolov

JPA EntityGraphs: решение проблемы запроса N+1

Говорят, что проблема запроса N+1 возникает, когда ORM, например, Hibernate, выполняет 1 запрос для получения родительского объекта и N запросов для получения дочерних объектов. По мере увеличения количества объектов в базе данных запросы, выполняемые отдельно, могут легко повлиять на производительность приложения. В этой статье будет продемонстрировано, как возникают запросы N+1, и их решение на примере Spring Boot. Рассмотрим сервис управления контентом, в котором хранится список статей для каждой публикации. Публикация может иметь категорию и список связанных с ней статей. Ниже приведена упрощенная сущность для публикации и статьи. Публикация имеет отношение «один ко многим» с сущностью «Статья». Предположим, что в настоящее время в базе данных имеются следующие данные: публикация_1 содержит две статьи, а публикация_2 — одну статью. Теперь, когда мы пытаемся получить все публикации, относящиеся к определенной категории, скажем, технологии, используя findByCategory('technology'), сна
Оглавление

Говорят, что проблема запроса N+1 возникает, когда ORM, например, Hibernate, выполняет 1 запрос для получения родительского объекта и N запросов для получения дочерних объектов. По мере увеличения количества объектов в базе данных запросы, выполняемые отдельно, могут легко повлиять на производительность приложения. В этой статье будет продемонстрировано, как возникают запросы N+1, и их решение на примере Spring Boot.

Рассмотрим сервис управления контентом, в котором хранится список статей для каждой публикации. Публикация может иметь категорию и список связанных с ней статей. Ниже приведена упрощенная сущность для публикации и статьи.

-2
*упрощенные сущности для объяснения запросов N+1.
*упрощенные сущности для объяснения запросов N+1.

Публикация имеет отношение «один ко многим» с сущностью «Статья». Предположим, что в настоящее время в базе данных имеются следующие данные: публикация_1 содержит две статьи, а публикация_2 — одну статью.

Теперь, когда мы пытаемся получить все публикации, относящиеся к определенной категории, скажем, технологии, используя findByCategory('technology'), сначала выполняется запрос SELECT для извлечения записей из таблицы публикации.

-4

Этот запрос возвращает две записи с идентификаторами «publication_1» и «publication_2». Теперь для каждой из этих публикаций вам необходимо получить соответствующие статьи из таблицы статей, и JPA внутренне сгенерирует еще два запроса SELECT:

-5

Как вы можете видеть здесь, после первого запроса на получение данных из таблицы публикаций были сгенерированы два дополнительных запроса (N=2) для получения связанных статей из дочерней таблицы. Следовательно, для получения данных генерируется N+1 запросов, где N — количество объектов в родительской таблице.

JPA EntityGraphs

EntityGraphs предоставляет возможность формулировать более эффективные запросы, определяя, какие объекты необходимо извлечь из базы данных с помощью SQL JOINS.

Существует два типа entityGraphs: Fetch и Load, которые определяют, следует ли лениво или с нетерпением извлекать сущности, не указанные в атрибутивных узлах objectGraphs. Атрибуты, указанные атрибутивными узлами объектаentityGraph, всегда извлекаются с готовностью.

FETCH TYPE: Атрибуты, указанные в атрибутивных узлах объекта entityGraph, обрабатываются как FetchType.EAGER, а остальные атрибуты обрабатываются как FetchType.Lazy.

LOAD TYPE: Атрибуты, заданные атрибутивными узлами объекта entityGraph, обрабатываются как FetchType.EAGER, а остальные атрибуты обрабатываются в соответствии с указанными или заданными по умолчанию типами выборки.

EntityGraph можно определить двумя способами:

1. Использование аннотации NamedEntityGraph

Чтобы использовать NamedEntityGraph, сначала аннотируйте класс сущности Publication аннотацией @NamedEntityGraph JPA, а затем прикрепите аннотацию @EntityGraph к методу репозитория с именем графа.

-6
-7

2. Без аннотации NamedEntityGraph

Вы также можете определить специальный EntityGraph, используя attributePaths, без использования аннотации NamedEntityGraph для сущности. AttributePaths должен включать имена объектов, которые необходимо быстро получить.

-8

Специальные EntityGraph более динамичны и используются для одного или конкретного варианта использования. NamedEntityGraphs более полезен по сравнению со специальными, когда к одной и той же сущности используется несколько запросов. Атрибуты, которые необходимо получить через JOIN для каждого запроса, можно указать только один раз в NamedEntityGraph для всех запросов.
Запрос, созданный JPA после использования EntityGraphs:

-9

Таким образом, количество запросов N+1 сократилось до одного запроса на получение данных из обеих таблиц с использованием JOIN. EntityGraphs предоставляет механизм, с помощью которого сущности могут быть оперативно извлечены из базы данных с помощью одного оператора выбора, что помогает повысить производительность приложения. Вы также можете использовать подграфы для определения сущностей дочернего класса, которые необходимо быстро получить вместе с родительским классом.