#ВопросЭксперту: почему в Spring Data JDBC отсутствует пагинация для native query?



Отвечает эксперт сообщества Spring АйО – Михаил Поливаха.



–––



Вопрос действительно интересный, и хотя такая возможность могла бы быть полезной, здесь есть свои нюансы и подводные камни. Давайте разберёмся, какие именно.



Начнём с того, что native query — это запросы, написанные на чистом SQL. В Hibernate их часто используют, когда возможностей HQL/JPQL недостаточно или когда нужны специфичные фичи для конкретной СУБД. Однако ни один ORM-фреймворк (будь то Spring Data JDBC, Hibernate или любой другой) не может на 100% знать структуру вашего нативного запроса — он может лишь предполагать. В таком запросе может быть что угодно, и ORM-фреймворк не всегда сможет корректно с ним работать.



Теперь представьте ситуацию: у нас есть Spring Data JPA native query для PostgreSQL и следующий набор тестов:





@Query(

value = "SELECT * FROM users FOR UPDATE SKIP LOCKED;",

nativeQuery = true

)

List<User> findBySomething(String name, Pageable pageable);



@Test

void testWithoutSort() {

userRepository.findBySomething("another", PageRequest.of(1, 10));

}



@Test

void testWithSort() {

userRepository.findBySomething("another", PageRequest.of(1, 10, Sort.by("name")));

}





Первый тест пройдёт успешно, а второй — нет. Почему?



Во втором случае передаётся параметр сортировки через Pageable. Но у jakarta.persistence.Query нет API для установки сортировки, хотя её всё же необходимо учесть. Другими словами, JPA-ровайдер не может нам тут помочь, т.к. он не предоставляет возможности прогнать параметры сортировки через свой API.



В итоге вставлять сортировку в native запрос приходится Spring Data JPA, и ей в буквальном смысле надо угадывать, куда же в запросе поставить ORDER BY. Да, Spring Data JPA может модифицировать ваши native query (существует даже API, позволяющий делать это динамически). Как вы можете догадаться, в данном случае Spring Data JPA не угадает с позиционированием.



Вот пример SQL, который будет сгенерирован в упавшем тесте





SELECT

*

FROM

users

OFFSET

? ROWS

FETCH NEXT ? ROWS ONLY FOR UPDATE SKIP LOCKED;

ORDER BY

FOR.name ASC -- НЕПРАВИЛЬНО! ORDER BY ЗДЕСЬ БЫТЬ НЕ ДОЛЖНО!





Проблема заключается в том, что Spring Data JPA вставляет ORDER BY в неподходящее место, что и приводит к ошибке.



С нативными запросами ORM-фреймворки, такие как Spring Data JPA и Hibernate, вынуждены догадываться, куда вставить OFFSET, LIMIT и ORDER BY. Чаще всего они угадывают верно, так как эти конструкции обычно находятся в конце запроса, но бывают исключения.



Таким образом, поддержка пагинации для нативных запросов — действительно полезная функция, но нужно понимать, что её реализация может привести к множеству багов, связанных с неверным определением позиции для OFFSET, LIMIT и ORDER BY.



Тем не менее, мне кажется, что эта фича стоит того, чтобы её внедрить! Делитесь своим мнением в комментариях, буду рад обсудить!



🖥 Исходный код поста на GitHub.