Данные Spring и собственный запрос с разбивкой на страницы


86

В веб-проекте, использующем последние весенние данные (1.10.2) с базой данных MySQL 5.6, я пытаюсь использовать собственный запрос с разбивкой на страницы, но org.springframework.data.jpa.repository.query.InvalidJpaQueryMethodExceptionпри запуске у меня возникает ошибка.

ОБНОВЛЕНИЕ : 20180306 Эта проблема теперь исправлена ​​в Spring 2.0.4. Для тех, кто все еще заинтересован или застрял в более старых версиях, проверьте соответствующие ответы и комментарии на предмет обходных путей.

Согласно примеру 50 при использовании @Query из документации spring -data это возможно, указав сам запрос и countQuery, например:

public interface UserRepository extends JpaRepository<User, Long> {
  @Query(value = "SELECT * FROM USERS WHERE LASTNAME = ?1",
    countQuery = "SELECT count(*) FROM USERS WHERE LASTNAME = ?1",
    nativeQuery = true)
  Page<User> findByLastname(String lastname, Pageable pageable);
}

Из любопытства в NativeJpaQueryклассе я вижу, что он содержит следующий код, чтобы проверить, является ли он действительным запросом jpa:

public NativeJpaQuery(JpaQueryMethod method, EntityManager em, String queryString, EvaluationContextProvider evaluationContextProvider, SpelExpressionParser parser) {
   super(method, em, queryString, evaluationContextProvider, parser);
   JpaParameters parameters = method.getParameters();
   boolean hasPagingOrSortingParameter = parameters.hasPageableParameter() || parameters.hasSortParameter();
   boolean containsPageableOrSortInQueryExpression = queryString.contains("#pageable") || queryString.contains("#sort");
   if(hasPagingOrSortingParameter && !containsPageableOrSortInQueryExpression) {
       throw new InvalidJpaQueryMethodException("Cannot use native queries with dynamic sorting and/or pagination in method " + method);
   }
}

Мой запрос содержит Pageableпараметр, как и hasPagingOrSortingParameterесть true, но он также ищет последовательность #pageableили #sortвнутри queryString, которую я не предоставляю.

Я попытался добавить #pageable(это комментарий) в конец моего запроса, что заставляет пройти проверку, но затем он не выполняется, говоря, что запрос ожидает один дополнительный параметр: 3 вместо 2.

Забавно то, что если я вручную переключаюсь containsPageableOrSortInQueryExpressionс falseна trueво время выполнения, запрос работает нормально, поэтому я не знаю, почему он проверяет, находится ли эта строка у меня, queryStringи я не знаю, как ее предоставить.

Любая помощь приветствуется.

Обновление 30.01.2018 Похоже, что разработчики проекта spring-data работают над исправлением этой проблемы с PR Йенсом Шаудером.


Нет, решения пока не нашел. Я создал заявку в Spring JIRA, но ответа нет: jira.spring.io/browse/DATAJPA-928 . В конце концов, мне не потребовалось разбиение на страницы, поэтому я не пытался продолжить расследование или усерднее работать с этим билетом.
Lasneyx

3
Хорошо спасибо. В качестве обходного пути вы могли бы добавить "\ n # pageable \ n" вместо "#pageable", но я надеюсь, что это будет исправлено в будущем.
Janar

1
это текущее исправление сделало мне хуже. теперь мой параметр PageAble полностью игнорируется, и запрос выполняется без ограничений. поэтому, если вы вставили это обходное решение \ n # pageable \ n, обязательно удалите его, иначе у вас возникнут проблемы.
Рюдигер Шульц

Ответы:


48

Приношу свои извинения заранее, это в значительной степени резюмирует исходный вопрос и комментарий Джанара , однако ...

Я столкнулся с той же проблемой: я нашел Пример 50 Spring Data в качестве решения моей потребности иметь собственный запрос с разбивкой на страницы, но Spring при запуске жаловался, что я не могу использовать разбиение на страницы с собственными запросами.

Я просто хотел сообщить, что мне удалось успешно выполнить нужный мне собственный запрос, используя разбиение на страницы, со следующим кодом:

    @Query(value="SELECT a.* "
            + "FROM author a left outer join mappable_natural_person p on a.id = p.provenance_id "
            + "WHERE p.update_time is null OR (p.provenance_name='biblio_db' and a.update_time>p.update_time)"
            + "ORDER BY a.id \n#pageable\n", 
        /*countQuery="SELECT count(a.*) "
            + "FROM author a left outer join mappable_natural_person p on a.id = p.provenance_id "
            + "WHERE p.update_time is null OR (p.provenance_name='biblio_db' and a.update_time>p.update_time) \n#pageable\n",*/
        nativeQuery=true)
public List<Author> findAuthorsUpdatedAndNew(Pageable pageable);

CountQuery (закомментированный в блоке кода) необходим для использования Page<Author> в качестве возвращаемого типа запроса, новые строки вокруг комментария «#pageable» необходимы, чтобы избежать ошибки времени выполнения в количестве ожидаемых параметров (обходной путь обходной путь). Надеюсь, этот баг скоро исправят ...


1
В моем случае ?#{#pageable}вместо этого работает \n#pageable\n.
Дмитрий Столбов

6
Это работает. В качестве аргументов вы по-прежнему можете использовать именованные параметры. пример :authorId. Я изменил свой \n#pageable\nTo --#pageable\nдля PostgreSQL. Как вы предложили, countQuery необходим, так как во многих примерах используется List <> вместо Page <>, ваш ответ был правильным. Я просмотрел те же документы, и если бы не ваш ответ, я бы просто сдался.
Abhishek Dujari

2
Если вы не пишете countQuery и не позволяете фреймворку обрабатывать это, иногда неправильно записывает счетчик по запросу, и вы можете увидеть ошибку как недопустимый список полей. Вы всегда можете проанализировать операторы SQL, добавив следующие записи в файл свойств logging.level.org.hibernate.SQL = DEBUG logging.level.org.hibernate.type.descriptor.sql.BasicBinder = TRACE Добавление счетчика по запросу отдельно будет исправить проблему. Это может зависеть от версий фреймворка и используемой базы данных.
Nalaka

В моем случае /*:pageable*/работает только (с SQL Server, Hibernate 5.4.1.Final и Spring Boot 2.2.1)
marioosh

35

Это хак для программы, использующей Spring Data JPA до версии 2.0.4 .

Код работал с PostgreSQL и MySQL:

public interface UserRepository extends JpaRepository<User, Long> {

@Query(value = "SELECT * FROM USERS WHERE LASTNAME = ?1 ORDER BY ?#{#pageable}",
       countQuery = "SELECT count(*) FROM USERS WHERE LASTNAME = ?1",
       nativeQuery = true)
   Page<User> findByLastname(String lastname, Pageable pageable);   
}

ORDER BY ?#{#pageable}для Pageable. countQueryдля Page<User>.


1
Я не знаю. Попробуйте 1. включить ведение журнала операторов SQL - набор spring.jpa.show-sql=falseв application.properties; 2. задать ?#{#pageable}в вашем nativeQuery и 3. проанализировать результат SQL из журнала. @Lasneyx создал выпуск DATAJPA-928 для этой особенности Spring Data JPA.
Дмитрий Столбов

12

Просто для записи, используя H2 в качестве тестовой базы данных и MySQL во время выполнения, этот подход работает (пример - новейший объект в группе ):

@Query(value = "SELECT t.* FROM t LEFT JOIN t AS t_newer " +
        "ON t.object_id = t_newer.object_id AND t.id < t_newer.id AND o_newer.user_id IN (:user_ids) " +
        "WHERE t_newer.id IS NULL AND t.user_id IN (:user_ids) " +
        "ORDER BY t.id DESC \n-- #pageable\n",
        countQuery = "SELECT COUNT(1) FROM t WHERE t.user_id IN (:user_ids) GROUP BY t.object_id, t.user_id",
        nativeQuery = true)
Page<T> findByUserIdInGroupByObjectId(@Param("user_ids") Set<Integer> userIds, Pageable pageable);

Spring Data JPA 1.10.5, H2 1.4.194, MySQL Community Server 5.7.11-log (innodb_version 5.7.11).


Работал на PostgreSQL. Спасибо!
Максим Черников

что, если нам нужно передать запрос по asc или desc динамически?
Кульбхушан Сингх

8

У меня такой же симптом, как у @Lasneyx. Мой способ обхода нативного запроса Postgres

@Query(value = "select * from users where user_type in (:userTypes) and user_context='abc'--#pageable\n", nativeQuery = true)
List<User> getUsersByTypes(@Param("userTypes") List<String> userTypes, Pageable pageable);

У меня тоже работал с DB2.
Андерс Метник

@Query (nativeQuery = true, value = "select a. * From rqst a LEFT OUTER JOIN table_b b ON a.id = b.id where a.code = 'abc' ORDER BY - # pageable \ n") не является работает: не удалось определить тип данных параметра $ 2
Разработчик

7

Попробуй это:

public interface UserRepository extends JpaRepository<User, Long> {
  @Query(value = "SELECT * FROM USERS WHERE LASTNAME = ?1 ORDER BY /*#pageable*/",
    countQuery = "SELECT count(*) FROM USERS WHERE LASTNAME = ?1",
    nativeQuery = true)
  Page<User> findByLastname(String lastname, Pageable pageable);
}

( "/* */"для Oracle notation)


по какой-то причине жанровый запрос, имеющий запятую после страницы, что-то вроде этого «ORDER BY / * # pageable * /», вызывающий синтаксическую ошибку
d-man

Я удаляю порядок, но сгенерировал исключение. Неожиданный символ: '#'
hicham abdedaime

5

Я использую базу данных oracle, и я не получил результата, а получил ошибку с сгенерированной запятой, о которой d-man говорит выше.

Тогда мое решение было:

Pageable pageable = new PageRequest(current, rowCount);

Как вы можете видеть без порядка при создании Pagable.

И метод в DAO:

public interface UserRepository extends JpaRepository<User, Long> {
  @Query(value = "SELECT * FROM USERS WHERE LASTNAME = ?1 /*#pageable*/ ORDER BY LASTNAME",
    countQuery = "SELECT count(*) FROM USERS WHERE LASTNAME = ?1",
    nativeQuery = true)
  Page<User> findByLastname(String lastname, Pageable pageable);
 }

1

Оба следующих подхода отлично работают с MySQL для разбивки на страницы собственных запросов. Однако они не работают с H2. Он будет жаловаться на синтаксическую ошибку sql.

  • ЗАКАЗАТЬ? # {# Pageable}
  • ЗАКАЗАТЬ ПО a.id \ n # pageable \ n


1

Я смог успешно интегрировать пагинацию в

Весна-данные-JPA-2.1.6

следующим образом.

@Query(
 value = “SELECT * FROM Users”, 
 countQuery = “SELECT count(*) FROM Users”, 
 nativeQuery = true)
Page<User> findAllUsersWithPagination(Pageable pageable);

0

Это работает, как показано ниже:

public interface UserRepository extends JpaRepository<User, Long> {
    @Query(value = "select * from (select (@rowid\\:=@rowid+1) as RN, u.* from USERS u, (SELECT @rowid\\:=0) as init where  LASTNAME = ?1) as total"+
        "where RN between ?#{#pageable.offset-1} and ?#{#pageable.offset + #pageable.pageSize}",
    countQuery = "SELECT count(*) FROM USERS WHERE LASTNAME = ?1",
    nativeQuery = true)
    Page<User> findByLastname(String lastname, Pageable pageable);
}

0

Для меня ниже работал в MS SQL

 @Query(value="SELECT * FROM ABC r where r.type in :type  ORDER BY RAND() \n-- #pageable\n ",nativeQuery = true)
List<ABC> findByBinUseFAndRgtnType(@Param("type") List<Byte>type,Pageable pageable);

0

Я использую приведенный ниже код. за работой

@Query(value = "select * from user usr" +
  "left join apl apl on usr.user_id = apl.id" +
  "left join lang on lang.role_id = usr.role_id" +
  "where apl.scr_name like %:scrname% and apl.uname like %:uname and usr.role_id in :roleIds ORDER BY ?#{#pageable}",
  countQuery = "select count(*) from user usr" +
      "left join apl apl on usr.user_id = apl.id" +
      "left join lang on lang.role_id = usr.role_id" +
      "where apl.scr_name like %:scrname% and apl.uname like %:uname and usr.role_id in :roleIds",
  nativeQuery = true)
Page<AplUserEntity> searchUser(@Param("scrname") String scrname,@Param("uname") String  uname,@Param("roleIds") List<Long> roleIds,Pageable pageable);

1
Не могли бы вы объяснить, почему этот ответ лучше, чем любой другой, который существует уже несколько лет?
Драконьи мысли

0

Удаление \ n # pageable \ n как из запроса, так и из запроса подсчета сработало для меня. Версия Springboot: 2.1.5.RELEASE DB: Mysql


0

Это сработало для меня (я использую Postgres) в Groovy:

@RestResource(path="namespaceAndNameAndRawStateContainsMostRecentVersion", rel="namespaceAndNameAndRawStateContainsMostRecentVersion")
    @Query(nativeQuery=true,
            countQuery="""
            SELECT COUNT(1) 
            FROM 
            (
                SELECT
                ROW_NUMBER() OVER (
                    PARTITION BY name, provider_id, state
                    ORDER BY version DESC) version_partition,
                *
                FROM mydb.mytable
                WHERE
                (name ILIKE ('%' || :name || '%') OR (:name = '')) AND
                (namespace ILIKE ('%' || :namespace || '%') OR (:namespace = '')) AND
                (state = :state OR (:state = ''))
            ) t
            WHERE version_partition = 1
            """,
            value="""
            SELECT id, version, state, name, internal_name, namespace, provider_id, config, create_date, update_date 
            FROM 
            (
                SELECT 
                ROW_NUMBER() OVER (
                    PARTITION BY name, provider_id, state
                    ORDER BY version DESC) version_partition,
                *
                FROM mydb.mytable
                WHERE 
                (name ILIKE ('%' || :name || '%') OR (:name = '')) AND
                (namespace ILIKE ('%' || :namespace || '%') OR (:namespace = '')) AND
                (state = :state OR (:state = ''))       
            ) t            
            WHERE version_partition = 1             
            /*#{#pageable}*/
            """)
    public Page<Entity> findByNamespaceContainsAndNameContainsAndRawStateContainsMostRecentVersion(@Param("namespace")String namespace, @Param("name")String name, @Param("state")String state, Pageable pageable)

Ключевым моментом здесь было использование: /*#{#pageable}*/

Это позволяет мне выполнять сортировку и разбиение на страницы. Вы можете проверить это, используя что-то вроде этого: http: // localhost: 8080 / api / v1 / entity / search / namespaceAndNameAndRawStateContainsMostRecentVersion? Namespace = & name = & state = published & page = 0 & size = 3 & sort = name, desc

Остерегайтесь этой проблемы: Spring Pageable не переводит имя @Column


0

Вы можете использовать приведенный ниже код для h2 и MySQl

    @Query(value = "SELECT req.CREATED_AT createdAt, req.CREATED_BY createdBy,req.APP_ID appId,req.NOTE_ID noteId,req.MODEL model FROM SUMBITED_REQUESTS  req inner join NOTE note where req.NOTE_ID=note.ID and note.CREATED_BY= :userId "
        ,
         countQuery = "SELECT count(*) FROM SUMBITED_REQUESTS req inner join NOTE note WHERE req.NOTE_ID=note.ID and note.CREATED_BY=:userId",
        nativeQuery = true)
Page<UserRequestsDataMapper> getAllRequestForCreator(@Param("userId") String userId,Pageable pageable);

-1

Замена / #pageable / на? # {# Pageable} позволяет выполнять нумерацию страниц. Добавление PageableDefault позволяет вам установить размер элементов страницы.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.