JPA графы объектов и нумерация страниц

В моем текущем проекте у нас есть несколько поисковых страниц в системе, где мы получаем много данных из базы данных для отображения в большом элементе таблицы в пользовательском интерфейсе. Мы используем JPA для доступа к данным (наш провайдер - Hibernate). Данные для большинства страниц собираются из нескольких таблиц базы данных (во многих случаях около 10), включая некоторые совокупные данные из отношений OneToMany (например, "количество связанных объектов типа X"). Для повышения производительности мы используем нумерацию результатов набора с TypedQuery.setFirstResult() а также TypedQuery.setMaxResults() загружать дополнительные строки из базы данных, когда пользователь прокручивает таблицу. Поскольку поиски очень динамичны, мы используем JPA CriteriaQuery API для построения запросов. Однако в настоящее время мы несколько страдаем от проблемы N+1 SELECT. На самом деле это довольно плохо в некоторых случаях, так как мы можем перебирать 3 уровня вложенных отношений OneToMany, где на каждом уровне данные загружаются лениво. Мы не можем объявить эти коллекции как загруженные в сопоставлениях сущностей, так как мы заинтересованы в них только на некоторых наших страницах. Т.е. мы можем получать данные из одной и той же таблицы на нескольких разных страницах, но мы показываем разные данные из таблицы и из разных связанных таблиц на разных страницах.

Чтобы облегчить это, мы начали экспериментировать с графами сущностей JPA, и они, кажется, очень помогают с проблемой N+1 SELECT. Однако, когда вы используете графы сущностей, Hibernate, очевидно, применяет разбиение на страницы в памяти. Я могу несколько понять, почему это происходит, но такое поведение во многих случаях сводит на нет многие (если не все) преимущества графов сущностей. Когда мы не использовали графы сущностей, мы могли загружать данные без применения каких-либо ограничений WHERE (т. Е. Рассматривая всю таблицу как набор результатов), независимо от того, сколько миллионов строк было в таблице, поскольку было очень ограниченное количество строк. на самом деле взято из-за нумерации страниц. Теперь, когда разбиение на страницы выполняется в памяти, Hibernate в основном выбирает всю таблицу базы данных (плюс все отношения, определенные в графе сущностей), а затем применяет разбиение на страницы в памяти, отбрасывая оставшиеся строки. Нехорошо.

Таким образом, вопрос в том, существует ли эффективный способ применения как пагинации, так и графов сущностей с помощью JPA (Hibernate)? Если JPA не предлагает решения для этого, также допустимы специфичные для Hibernate расширения. Если это также невозможно, каковы другие альтернативы? Использование базы данных представлений? Представления были бы немного громоздкими, так как мы поддерживаем несколько поставщиков баз данных. Создание всех необходимых представлений для разных поставщиков значительно увеличило бы усилия по разработке.

Еще одна идея, которая у меня возникла, состояла в том, чтобы применить графы сущностей и нумерацию страниц, как мы это делаем в настоящее время, и просто не запускать какие-либо запросы, если они будут возвращать слишком много строк. Мне уже нужно выполнять COUNT-запросы, чтобы ленивая загрузка строк работала должным образом в пользовательском интерфейсе.

1 ответ

Решение

Я не уверен, что полностью понимаю вашу проблему, но мы столкнулись с чем-то похожим: у нас есть постраничные списки сущностей, которые могут содержать данные из нескольких соединенных сущностей. Эти списки могут быть отсортированы и отфильтрованы (некоторые из этих сортировок / фильтров должны быть применены в памяти из-за отсутствия возможностей в dbms, но это только примечание), и подкачка должна применяться позже.

Хранение всех этих данных в памяти не работает должным образом, поэтому мы выбрали следующий подход (могут быть более качественные / более стандартные):

  1. Используйте запрос для загрузки первичных ключей (в нашем случае простых длин) основных сущностей. Объединяйте только то, что нужно для сортировки и фильтрации, чтобы сделать запрос максимально простым.

    В нашем случае запрос фактически загружает больше данных для применения сортировок и фильтров в памяти, где это необходимо, но эти данные высвобождаются как можно скорее, и сохраняются только первичные ключи.
  2. При отображении конкретной страницы мы извлекаем соответствующие первичные ключи для страницы и используем второй запрос для загрузки всего, что должно отображаться на этой странице. Этот второй запрос может содержать больше объединений и, следовательно, быть более сложным и медленным, чем тот, что на шаге 1, но, поскольку мы загружаем данные только для этой страницы, фактическая нагрузка на систему довольно низкая.
Другие вопросы по тегам