Как реализовать пагинацию при получении записей справочника
Что проверяют
Знаете ли вы штатные средства Spring Data и задаётесь ли вопросом, нужна ли пагинация вообще. Справочник часто маленький и почти не меняется. Следом обычно спрашивают, откуда брать общее количество и как обеспечить стабильный порядок.
Как отвечать
Сначала уточните размер. Валюты, страны, статусы это сотни строк: их выгоднее отдать целиком и закэшировать через @Cacheable с Caffeine. Пагинация нужна, когда в справочнике десятки тысяч записей (адреса, номенклатура) или есть поиск.
В Spring Data JPA:
Pageable: контроллер принимаетPageableиз параметровpage,size,sort, репозиторий объявляетPage<Item> findAll(Pageable pageable). По умолчанию страницы нумеруются с нуля, размер 20, а предел задаётspring.data.web.pageable.max-page-size(по умолчанию 2000). Его стоит уменьшить;PageпротивSlice:Pageвыполняет дополнительныйcount-запрос,Sliceзнает только, есть ли следующая порция. Для кнопки «показать ещё»Sliceдешевле;- keyset: начиная с Spring Data 3.1 есть прокрутка через
Window<T>иScrollPosition.keyset(): следующая порция выбирается по ключу последнего элемента безOFFSET; - стабильный порядок: сортировка всегда по уникальному ключу или с
idвторым полем. Иначе строки с одинаковым названием между запросами могут меняться местами.
Ответ API не стройте сериализацией PageImpl как есть: в свежих версиях Spring Data это сопровождается предупреждением, рекомендуют PagedModel или свой DTO с items, page, size, total. На чистом JDBC это LIMIT ? OFFSET ? в PreparedStatement с проверкой page и size.
Где спотыкаются
Пагинируют запрос с JOIN FETCH по коллекции. Hibernate предупреждает и применяет лимит в памяти, после загрузки всех строк. Решение: сначала страница идентификаторов, затем загрузка сущностей с коллекциями по этим id.
Не задают сортировку вовсе. База не обязана возвращать строки в одном и том же порядке, и записи повторяются на соседних страницах.
Не ограничивают size и считают count на каждый запрос к огромной таблице. Или режут в памяти весь справочник через subList.