Весняні дані та власний запит з пагінацією


86

У веб-проекті, використовуючи найновіші дані spring (1.10.2) з базою даних MySQL 5.6, я намагаюся використовувати власний запит з пагінацією, але org.springframework.data.jpa.repository.query.InvalidJpaQueryMethodExceptionпід час запуску я відчуваю .

ОНОВЛЕННЯ : 20180306 Цю проблему тепер вирішено навесні 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 . Врешті-решт, мені не була потрібна пагінація, тому я не намагався далі розслідувати або наполегливо натискати на цей квиток.
Ласнейкс

3
Добре, дякую. Як обхідний шлях ви могли додати "\ n # pageable \ n" замість "#pageable", але, сподіваюся, це буде виправлено в майбутньому.
Янар

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

Відповіді:


48

Заздалегідь прошу вибачення, це майже підсумовує оригінальне питання та коментар від Янара , проте ...

Я стикаюся з тією ж проблемою: я знайшов Приклад 50 даних Spring як рішення для моєї необхідності мати власний запит з пагінацією, але 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\nна --#pageable\npostgresql. CountQuery потрібен, як ви запропонували, оскільки багато прикладів використовують List <> замість Page <>, для чого вам було знайдено відповідь. Я пройшов ті самі документи, і якби не ваша відповідь, я б просто відмовився.
Abhishek Dujari

2
Якщо ви не пишете countQuery і дозволяєте фреймворку це обробляти, коли-небудь ви не пишете підрахунок за запитом правильно для вас, і ви можете побачити помилку як недійсний список полів. Ви завжди можете проаналізувати оператори SQL, додавши введені нижче записи до файлу властивостей logging.level.org.hibernate.SQL = DEBUG logging.level.org.hibernate.type.descriptor.sql.BasicBinder = TRACE Додавання підрахунку за запитом окремо вирішити проблему. Це може залежати від версій фреймворку та бази даних, яку ви використовуєте.
Налака

У моєму випадку /*: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}у вашому рідномуQuery і 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.
Anders Metnik

@Query (nativeQuery = true, value = "select a. * From rqst a LEFT OUTER JOIN table_b b ON a.id = b.id де 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)


з якоїсь причини сгенерований запит, який має кому після сторінки, яку можна передати, щось на зразок цього "ЗАМОВИТИ ПО / * # pageable * /", що спричиняє синтаксичну помилку
d-man


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 # сторінкою \ n


1

Я міг би успішно інтегрувати Pagination в

spring-data-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
Будь ласка, чи можете ви пояснити, чому ця відповідь краща за будь-яку з інших, які існують вже кілька років?
Dragonthoughts

0

Видалення \ n # сторінки, що піддається \ n, як із запиту, так і з запиту підрахунку, мені вдалося. Версія Springboot: 2.1.5.ВИПУСК БД: 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.