【问题标题】:Spring Data and Native Query with pagination带有分页的 Spring Data 和 Native Query
【发布时间】:2016-11-15 22:30:51
【问题描述】:

在一个 web 项目中,使用最新的 spring-data (1.10.2) 和 MySQL 5.6 数据库,我正在尝试使用带有分页的本机查询,但我在启动时遇到了 org.springframework.data.jpa.repository.query.InvalidJpaQueryMethodException

更新:20180306 此问题现已在Spring 2.0.4 中修复。对于那些仍然感兴趣或坚持使用旧版本的用户,请查看相关答案和 cmets 以了解解决方法。

根据Example 50 at Using @Query from spring-data documentation,可以指定查询本身和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 参数,所以hasPagingOrSortingParametertrue,但它也在queryString 中寻找#pageable#sort 序列,我没有提供。

我尝试在查询末尾添加#pageable(这是一条注释),这使验证通过,但随后执行失败,说查询需要一个额外的参数:3 而不是 2。

有趣的是,如果我在运行时手动将 containsPageableOrSortInQueryExpressionfalse 更改为 true,则查询工作正常,所以我不知道为什么它会检查该字符串是否位于我的 queryString 和我不知道如何提供。

任何帮助将不胜感激。

2018 年 1 月 30 日更新 spring-data 项目的开发人员似乎正在通过PR by Jens Schauder 解决此问题

【问题讨论】:

  • 不,我还没有找到解决方案。我在 Spring JIRA 中创建了一张票,但没有回复:jira.spring.io/browse/DATAJPA-928。最后,我不需要分页,所以我没有尝试进一步调查或用那张票更努力地推动。
  • 好的,谢谢。作为一种解决方法,您可以添加“\n#pageable\n”而不是“#pageable”,但我希望它会在未来得到修复。
  • 当前的修复让我的情况变得更糟。现在我的 PageAble 参数被完全忽略,查询无限执行。因此,如果您插入了该 \n#pageable\n 解决方法,请确保将其删除,否则您会遇到麻烦。

标签: spring spring-data spring-data-jpa


【解决方案1】:

我提前道歉,这几乎是对原始问题和Janar的评论的总结,但是......

我遇到了同样的问题:我发现 Example 50 of 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);

使用Page&lt;Author&gt;需要countQuery(在代码块中注释掉) 作为查询的返回类型,需要“#pageable”注释周围的换行符来避免预期参数数量的运行时错误(变通方法的变通方法)。我希望这个错误将很快得到修复...

【讨论】:

  • 在我的情况下,?#{#pageable} 正在工作,而不是 \n#pageable\n
  • 这行得通。对于参数,您仍然可以使用命名参数。例如:authorId。对于 postgresql,我将您的 \n#pageable\n 更改为 --#pageable\n。正如您所建议的那样, countQuery 是必需的,因为许多示例使用 List 而不是 Page 您的答案就是针对此问题的。我浏览了相同的文档,如果不是您的回答,我会放弃。
  • 如果您不编写 countQuery 并让框架处理它,有时不会为您正确编写按查询的计数,您可能会看到错误为无效字段列表。您始终可以通过将以下条目添加到属性文件 logging.level.org.hibernate.SQL=DEBUG logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE 来分析 SQL 语句,分别通过查询添加计数将解决问题。这可能取决于您使用的框架版本和数据库。
  • 就我而言,只有/*:pageable*/ 有效(使用 SQL Server、Hibernate 5.4.1.Final 和 Spring Boot 2.2.1)
【解决方案2】:

这是对使用 Spring Data JPA 2.0.4 之前版本的程序的 hack。

代码已与 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} 用于PageablecountQuery 代表Page&lt;User&gt;

【讨论】:

  • 我现在不知道。尝试 1. 启用 SQL 语句的日志记录 - 在 application.properties 中设置 spring.jpa.show-sql=false; 2. 在您的 nativeQuery 中设置?#{#pageable} 和 3. 从日志中分析结果 SQL。 @Lasneyx 针对 Spring Data JPA 的这种特性创建了问题 DATAJPA-928
【解决方案3】:

仅作记录,使用 H2 作为测试数据库,并在运行时使用 MySQL,这种方法有效(例如 newest object in group):

@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 动态地将 order 传递给查询怎么办?
【解决方案4】:

试试这个:

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*/”,导致语法错误
  • 我删除了订单,但生成了异常意外字符:'#'
【解决方案5】:

我的症状与@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 的数据类型
【解决方案6】:

我使用 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);
 }

【讨论】:

    【解决方案7】:

    我添加这个答案只是作为那些使用 Spring Boot 更新版本的用户的占位符。在 Spring Boot 2.4.3 上,我观察到没有任何解决方法是必要的,以下代码对我来说是直接开箱即用的:

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

    countQuery 定义不是必需的,实际上对 Page#getTotalElements() 的调用已经返回了正确的计数,正如 JPA 自己的内部计数查询所返回的那样。

    上面的代码非常强大,提供通过原生查询进行的分页,但它会将结果返回到实际的 Java 实体中(而不是丑陋而笨重的 List&lt;Object[]&gt;,有时这是必要的)。

    【讨论】:

    • 这对我有用,避免在选择查询中使用表别名,它不起作用!
    【解决方案8】:

    以下两种方法都适用于 MySQL 对本机查询进行分页。但它们不适用于 H2。会报sql语法错误。

    • 订购方式 ?#{#pageable}
    • 按 a.id 排序\n#pageable\n

    【讨论】:

      【解决方案9】:

      我可以成功地将 Pagination 集成到

      spring-data-jpa-2.1.6

      如下。

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

      【讨论】:

      • 原生查询中的pageable参数在哪里?
      【解决方案10】:

      使用“ORDER BY id DESC \n-- #pageable\n” 而不是“ORDER BY id \n#pageable\n”为我使用 MS SQL SERVER

      【讨论】:

        【解决方案11】:

        它的工作原理如下:

        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);
        }
        

        【讨论】:

          【解决方案12】:

          对我来说,下面是在 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);
          

          【讨论】:

            【解决方案13】:

            我正在使用下面的代码。工作

            @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);
            

            【讨论】:

            • 请你解释一下为什么这个答案比已经存在了几年的其他答案更好?
            【解决方案14】:

            从查询和计数查询中删除 \n#pageable\n 对我有用。 Springboot 版本:2.1.5.RELEASE 数据库:Mysql

            【讨论】:

              【解决方案15】:

              这在 Groovy 中对我有用(我正在使用 Postgres):

              @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/entities/search/namespaceAndNameAndRawStateContainsMostRecentVersion?namespace=&name=&state=published&page=0&size=3&sort=name,desc

              注意这个问题:Spring Pageable does not translate @Column name

              【讨论】:

                【解决方案16】:

                您可以将以下代码用于 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);
                

                【讨论】:

                  【解决方案17】:

                  您可以使用以下代码来实现它,

                  @Query(value = "SELECT * FROM users u WHERE  ORDER BY ?#{#pageable}", nativeQuery = true)
                  List<User> getUsers(String name, Pageable pageable);
                  

                  只需使用 ORDER BY ?#{#pageable} 并将页面请求传递给您的方法。

                  享受吧!

                  【讨论】:

                    【解决方案18】:

                    将 /#pageable/ 替换为 ?#{#pageable} 允许进行分页。 添加 PageableDefault 允许您设置页面元素的大小。

                    【讨论】:

                      猜你喜欢
                      • 2017-03-08
                      • 1970-01-01
                      • 2015-06-07
                      • 2020-05-20
                      • 2021-11-06
                      • 1970-01-01
                      • 2018-12-25
                      • 2018-11-25
                      • 2014-09-12
                      相关资源
                      最近更新 更多