Как мне реализовать Hibernate Pagination с помощью курсора (чтобы результаты оставались согласованными, несмотря на добавление новых данных в таблицу для разбивки страниц)? - PullRequest
4 голосов
/ 04 мая 2010

Есть ли способ поддерживать курсор базы данных, используя Hibernate между веб-запросами? По сути, я пытаюсь реализовать разбиение на страницы, но данные, которые выстраиваются, постоянно меняются (то есть новые записи добавляются в базу данных). Мы пытаемся настроить его так, чтобы, когда вы выполняете первоначальный поиск (возвращающий максимум 5000 результатов) и просматриваете результаты, одни и те же записи всегда появляются на одной странице (т. Е. Мы не выполняем запрос непрерывно). каждый раз нажимаются кнопки следующей и предыдущей страницы). В настоящее время мы реализуем это, просто выбирая 5000 (не более) первичных ключей из таблицы, которую мы разбиваем на страницы, сохраняя эти ключи в памяти, а затем просто используя 20 первичных ключей за раз, чтобы получить их данные из базы данных. , Однако мы хотим избавиться от необходимости хранить эти ключи в памяти и предпочли бы курсор в базе данных, к которому мы просто продолжаем возвращаться и перемещаться назад и вперед по курсору для генерации страниц.

Я попытался сделать это с помощью ScrollableResults Hibernate, но обнаружил, что не могу вызывать такие методы, как next () и previous (), что вызовет исключение, если вы находитесь в другом веб-запросе / сеансе Hibernate (что неудивительно). Есть ли способ повторно присоединить объект ScrollableResults к сеансу, во многом так же, как вы бы подключили отдельный объект базы данных, чтобы сделать его постоянным? Существуют ли другие подходы для реализации этой подкачки данных с согласованными результатами подкачки без кэширования первичных ключей?

Ответы [ 2 ]

2 голосов
/ 04 мая 2010

По сути, вы один для этого. Что вы хотите сделать, так это взглянуть на фильтр OpenSessionInView и создать свой собственный, чтобы вместо создания нового HibernateSession для каждого запроса вы извлекали один из кэша, который связан с веб-сеансом пользователя.

Если у вас нет фреймворка, такого как Spring WebFlow, который дает вам некоторую структуру диалога, вам также понадобится построить его. Поскольку вам, вероятно, нужен какой-то способ управления жизненным циклом этого сеанса Hibernate, кроме «Когда срок веб-сеанса истекает». Вы также, скорее всего, не хотите, чтобы два пользовательских потока из одного и того же веб-сеанса, но разных вкладок браузера, совместно использовали сеанс гибернации. (Веселость может последовать.)

0 голосов
/ 26 марта 2019

Никогда не используйте смещение, потому что смещение также считывает все данные до смещения, что очень неэффективно.

Вам необходимо упорядочить по индексируемому уникальному свойству, вернуть значение последнего свойства элемента в вызове API и использовать предложение WHERE, чтобы начать с того места, где вы оставили. Значение свойства этого последнего элемента будет вашей позицией курсора. Например, простой разбитый на страницы запрос, использующий первичный ключ id в качестве курсора, будет выглядеть так:

List<MyEntity> entities = entityManager
    .createQuery("""
        FROM
            MyEntity e
        WHERE
            e.id > :cursorPosition
        ORDER BY
            e.id ASC
    """, MyEntity.class)
    .setParameter("cursorPosition", cursorPosition)
    .setMaxResults(pageSize)
    .getResultList()

При первом вызове API значение cursorPosition может быть равно 0. Второй - вы получите от клиента курсор, который клиент получил от первого вызова. Посмотрите, как запрос Google Maps разбивает на страницы , работает с атрибутом nextPageToken.

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

Я считаю, что вы можете сделать это несколькими способами. Один из способов - объединить все параметры и cursorPosition в строку, закодировать ее в строку, удобную для URL-адреса, например Base64, и при получении обратно декодировать ее и разделить строку на исходные параметры:

 String nextPageToken = Base64.getUrlEncoder()
     .encodeToString("indexProperty=id&cursorPos=123&ageBiggerThan=65".getBytes())

Ваш вызов API вернет JSON, как это:

{
    "items": [ ... ],
    "nextPageToken": "aW5kZXhQcm9wZXJ0eT1pZCZjdXJzb3JQb3M9MTIzJmFnZUJpZ2dlclRoYW49NjU="
}

И клиент следующий звонок:

GET https://www.example.com/api/myservice/v1/myentity?pageToken=aW5kZXhQcm9wZXJ0eT1pZCZjdXJzb3JQb3M9MTIzJmFnZUJpZ2dlclRoYW49NjU=

Часть объединения и разбиения строки курсора может быть утомительной, я действительно не знаю, есть ли библиотека, которая обрабатывает эту работу по созданию токенов и их синтаксическому анализу, я на самом деле в этом вопросе, потому что я искал Это. Но я думаю, что GSON или Джексон могут сэкономить вам строки кода на этом.

...