具有分页的Spring Data和Native Query

时间:2016-07-13 11:06:41

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

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

更新:20180306此问题现已在Spring 2.0.4中得到解决。对于仍对旧版本感兴趣或遇到困难的用户,请查看相关答案和评论以获取解决方法。

根据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,但它也在{{1}内寻找#pageable#sort个序列我没有提供。

我在我的查询结束时尝试添加queryString(这是注释),这使得验证通过但是,它在执行时失败,说查询需要一个额外的参数:3而不是2

有趣的是,如果我在运行时手动将#pageablecontainsPageableOrSortInQueryExpression更改为false,则查询工作正常,因此我不知道为什么要检查该字符串是在true,我不知道如何提供它。

非常感谢任何帮助。

更新01/30/2018 似乎spring-data项目的开发人员正在使用PR by Jens Schauder

来解决此问题

18 个答案:

答案 0 :(得分:32)

我提前道歉,这几乎总结了原始问题和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<Author>需要countQuery(在代码块中注释掉) 作为查询的返回类型,需要围绕“#pageable”注释的换行符以避免预期参数数量的运行时错误(变通方法的变通方法)。我希望这个bug很快就能解决......

答案 1 :(得分:20)

此代码与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<>

答案 2 :(得分:10)

仅仅是为了记录,在运行时使用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)。

答案 3 :(得分:6)

我有与@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);

答案 4 :(得分:5)

试试这个:

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

"/* */"的{​​{1}})

答案 5 :(得分:4)

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

答案 6 :(得分:1)

以下两种方法都适用于MySQL以分页本机查询。但他们并没有与H2合作。它会抱怨sql语法错误。

  • ORDER BY?#{#pageable}
  • ORDER BY a.id \ n#pageable \ n

答案 7 :(得分:1)

我可以成功地将分页集成到

spring-data-jpa-2.1.6

如下。

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

答案 8 :(得分:0)

我添加这个答案只是作为那些使用更新版本 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<Object[]>,这有时是必要的)。

答案 9 :(得分:0)

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

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

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

享受吧!

答案 10 :(得分: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);

答案 11 :(得分:0)

这在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

答案 12 :(得分:0)

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

答案 13 :(得分: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);

答案 14 :(得分: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);

答案 15 :(得分:0)

使用&#34; ORDER BY id DESC \ n-- #pageable \ n&#34; 而不是&#34; ORDER BY id \ n#pageable \ n&#34;用MS SQL SERVER为我工作

答案 16 :(得分: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);
}

答案 17 :(得分:-1)

用?#{#pageable}替换

/ #pageable /允许分页。 添加PageableDefault允许您设置页面元素的大小。