Почему Hibernate игнорирует setMaxResults?

#oracle #hibernate #jpa #pagination #persistence

#Oracle #гибернация #jpa #разбивка на страницы #постоянство

Вопрос:

Я использую разбивку на страницы на стороне сервера для одной из моих таблиц, используя CriteriaQuery и TypedQuery, и устанавливаю следующие значения: typedQuery.setFirstResult(0); typedQuery.setMaxResults(100);

К сожалению, в сгенерированном SQL-запросе, который выполняется в Oracle DB, я никогда не вижу условие ROWNUM . Я добавил также ORDER BY в свой TypedQuery, но, тем не менее, запрос выполняет простой select без ограничения результатов в базе данных.

В результате я получаю следующее предупреждение HHH000104: firstResult / maxResults, указанное с помощью выборки коллекции; применяется в памяти! . Другими словами, Hibernate выполняет разбивку на страницы в памяти, поскольку она не выполняется в БД. Для этого предупреждения я прочитал следующую статью https://vladmihalcea.com/fix-hibernate-hhh000104-entity-fetch-pagination-warning-message / но прежде чем разделить мой запрос на два запроса (получить идентификатор, а затем получить данные для этого идентификатора), я подумал о предоставлении setMaxResults . Тем не менее, мне интересно, почему сгенерированный запрос не соответствует ожидаемому с ROWNUM .

Дополнительная информация:

  • DB: Oracle 18
  • Диалоговое окно: org.hibernate.dialect.Oracle12cDialect
  • Гибернация: 5.3.15
  • JDK: 11

Комментарии:

1. Попробуйте также установить setFirstResult()

2. @Conrad, я сделал. Это упоминается в описании проблемы.

Ответ №1:

Вы должны понимать, что первые / максимальные результаты применяются на уровне сущности, если вы выбираете сущность. Если вы извлекаете атрибуты коллекции join или используете граф объектов для атрибутов коллекции, вы изменяете количество строк, возвращаемых JDBC для каждого объекта, т.Е. Каждая строка для строки основного объекта дублируется для каждого элемента коллекции. Результатом этого является то, что Hibernate больше не может выполнять разбивку на страницы с помощью ROWNUM, поэтому вы не видите его в запросе. Если вы удалите соединение выборки, вы увидите использование ROWNUM .

Сказав это, это идеальный вариант использования для Blaze-Persistence.

Blaze-Persistence — это конструктор запросов поверх JPA, который поддерживает многие расширенные функции СУБД поверх модели JPA. Поддержка разбивки на страницы, с которой он поставляется, решает все проблемы, с которыми вы можете столкнуться.

Он также имеет интеграцию с данными Spring, поэтому вы можете использовать тот же код, что и сейчас, вам нужно только добавить зависимость и выполнить настройку: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-setup

В Blaze-Persistence есть много разных стратегий разбивки на страницы, которые вы можете настроить. Стратегия по умолчанию заключается в том, чтобы встроить запрос идентификаторов в основной запрос. Что-то вроде этого:

 select u 
from User u 
left join fetch u.notes 
where u.id IN (
  select u2.id
  from User u2
  order by ...
  limit ...
)
order by ...
  

Комментарии:

1. Действительно, как только я удалил выборку из своих критериев, я снова смог увидеть ROWNUM в своих запросах. Как ни странно, я получил, хотя в других запросах выборка (root. выборка(MyEntity_.field, JoinType.LEFT);), которая выполняет ЛЕВОЕ ВНЕШНЕЕ СОЕДИНЕНИЕ в моем сгенерированном запросе, а также добавляет число СТРОК. Вот где началась вся моя путаница. Спасибо за ваш ответ.

Ответ №2:

При объединении данных родительский элемент будет дублироваться n раз. Например:

 select p from Post p join p.comments
  

Если у post есть 20 комментариев под одним сообщением, то это одно сообщение будет возвращено 20 раз с 20 разными комментариями.
Ограничение строк в этом случае не имеет смысла, потому что фактическое количество возвращенных сообщений не будет равно размеру страницы. Другими словами, ограничение страницы до 20 записей вернет только одно сообщение.

Комментарии:

1. Я не думаю, что это связано с моим вопросом.. Я говорю о setMaxResults, который должен создать запрос с предложением where ROWNUM> 100 . Но это не так.

2. можете ли вы привести пример?

3. и кстати. это связано с предоставленным вами предупреждающим сообщением hibernate..