简体   繁体   English

Spring 带分页的数据和本机查询

[英]Spring Data and Native Query with pagination

In a web project, using latest spring-data (1.10.2) with a MySQL 5.6 database, I'm trying to use a native query with pagination but I'm experiencing an org.springframework.data.jpa.repository.query.InvalidJpaQueryMethodException at startup. In a web project, using latest spring-data (1.10.2) with a MySQL 5.6 database, I'm trying to use a native query with pagination but I'm experiencing an org.springframework.data.jpa.repository.query.InvalidJpaQueryMethodException at启动。

UPDATE : 20180306 This issue is now fixed in Spring 2.0.4 For those still interested or stuck with older versions check the related answers and comments for workarounds.更新:20180306 这个问题现在在Spring 2.0.4中得到修复对于那些仍然感兴趣或坚持旧版本的人,请查看相关答案和评论以获取解决方法。

According to Example 50 at Using @Query from spring-data documentation this is possible specifying the query itself and a countQuery, like this:根据来自 spring-data 文档的使用 @Query 的示例 50,可以指定查询本身和 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);
}

Out of curiosity, In NativeJpaQuery class I can see that it contains the following code to check if it's a valid jpa query:出于好奇,在NativeJpaQuery class 中,我可以看到它包含以下代码来检查它是否是有效的 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);
   }
}

My query contains a Pageable parameter, so hasPagingOrSortingParameter is true , but it's also looking for a #pageable or #sort sequence inside the queryString , which I do not provide.我的查询包含一个Pageable参数,因此hasPagingOrSortingParametertrue ,但它也在queryString中寻找#pageable#sort序列,我没有提供。

I've tried adding #pageable (it's a comment) at the end of my query, which makes validation to pass but then, it fails at execution saying that the query expects one additional parameter: 3 instead of 2.我尝试在查询末尾添加#pageable (它是一条注释),这使得验证通过,但随后执行失败,说查询需要一个额外的参数:3 而不是 2。

Funny thing is that, if I manually change containsPageableOrSortInQueryExpression from false to true while running, the query works fine so I don't know why it's checking for that string to be at my queryString and I don't know how to provide it.有趣的是,如果我在运行时手动将containsPageableOrSortInQueryExpressionfalse更改为true ,则查询工作正常,所以我不知道为什么它会检查该字符串是否位于我的queryString中,而且我不知道如何提供它。

Any help would be much appreciated.任何帮助将非常感激。

Update 01/30/2018 It seems that developers at spring-data project are working on a fix for this issue with a PR by Jens Schauder 2018 年 1 月 30 日更新似乎 spring-data 项目的开发人员正在通过 Jens Schauder 的 PR解决此问题

My apologies in advance, this is pretty much summing up the original question and the comment from Janar , however...我提前道歉,这几乎是对原始问题和Janar评论的总结,但是......

I run into the same problem: I found the Example 50 of Spring Data as the solution for my need of having a native query with pagination but Spring was complaining on startup that I could not use pagination with native queries.我遇到了同样的问题:我发现Spring Data示例 50作为我需要使用分页的本机查询的解决方案,但 Spring 在启动时抱怨我无法对本机查询使用分页。

I just wanted to report that I managed to run successfully the native query I needed, using pagination, with the following code:我只是想报告,我使用以下代码成功运行了我需要的本机查询,使用分页:

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

The countQuery (that is commented out in the code block) is needed to use Page<Author> as the return type of the query, the newlines around the "#pageable" comment are needed to avoid the runtime error on the number of expected parameters (workaround of the workaround). countQuery(在代码块中注释掉)需要使用Page<Author>作为查询的返回类型,“#pageable”注释周围的换行符需要避免预期参数数量的运行时错误(解决方法的解决方法)。 I hope this bug will be fixed soon...希望这个bug能早点修复。。。

This is a hack for program using Spring Data JPA before Version 2.0.4 .这是使用Spring Data JPA 版本 2.0.4 之前的程序的黑客。

Code has worked with PostgreSQL and MySQL :代码已与 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} is for Pageable . ORDER BY ?#{#pageable}用于Pageable countQuery is for Page<User> . countQuery用于Page<User>

Just for the record, using H2 as testing database, and MySQL at runtime, this approach works (example is newest object in group ):只是为了记录,使用 H2 作为测试数据库,并在运行时使用 MySQL,这种方法有效(例如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). Spring Data JPA 1.10.5、H2 1.4.194、MySQL Community Server 5.7.11-log (innodb_version 5.7.11)。

Try this:试试这个:

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

( "/* */" for Oracle notation ) ( "/* */"Oracle notation )

I have exact same symptom like @Lasneyx.我有与@Lasneyx 完全相同的症状。 My workaround for Postgres native query我对 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);

I use oracle database and I did not get the result but an error with generated comma which d-man speak about above.我使用 oracle 数据库,但没有得到结果,但生成的逗号错误,d-man 在上面谈到了这个错误。

Then my solution was:然后我的解决方案是:

Pageable pageable = new PageRequest(current, rowCount);

As you can see without order by when create Pagable.正如您在创建 Pagable 时无序所见。

And the method in the DAO:以及 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);
 }

Both the following approaches work fine with MySQL for paginating native query.以下两种方法都适用于 MySQL 对本机查询进行分页。 They doesn't work with H2 though.但它们不适用于 H2。 It will complain the sql syntax error.它会抱怨 sql 语法错误。

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

I could successfully integrate Pagination in我可以成功地将 Pagination 集成到

spring-data-jpa-2.1.6 spring-data-jpa-2.1.6

as follows.如下。

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

使用 "ORDER BY id DESC \\n-- #pageable\\n " 而不是 "ORDER BY id \\n#pageable\\n" 对我的 MS SQL SERVER 有用

I am adding this answer just as a placeholder for those users who are using more recent versions of Spring Boot.我添加这个答案只是作为那些使用更新版本 Spring Boot 的用户的占位符。 On Spring Boot 2.4.3, I observed that none of the workaround were necessary, and the following code worked straight out of the box for me:在 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);
}

The countQuery definition was not necessary, and a call to Page#getTotalElements() in fact already was returning the correct count, as returned by JPA's own internal count query. countQuery定义不是必需的,实际上对Page#getTotalElements()的调用已经返回了正确的计数,正如 JPA 自己的内部计数查询所返回的那样。

The above code is extremely powerful, offering pagination made via a native query, yet which return results into actual Java entities (rather than the ugly and bulky List<Object[]> , which sometimes is necessary).上面的代码非常强大,提供通过本机查询进行的分页,但将结果返回到实际的 Java 实体中(而不是丑陋而笨重的List<Object[]> ,有时这是必要的)。

  • Create your custom repository:创建您的自定义存储库:
public interface ProductsCustomRepository extends JpaRepository<ProductResultEntity, Long> {
    @Query(
        value = "select tableA.id, tableB.bank_name from tableA join tableB on tableA.id = tableB.a_id where tableA.id = :id
        and (:fieldX is null or tableA.fieldX LIKE :fieldX)",
        countQuery = "select count(*) from tableA join tableB on tableA.id = tableB.a_id where tableA.id = :id
        and (:fieldX is null or tableA.fieldX LIKE :fieldX)",
        nativeQuery = true
    )
    Page<ProductResultEntity> search(@Param("id") Long aId,
        @Param("fieldX") String keyword, Pageable pageable
    );
}
  • Create View as query of:创建视图作为查询:
create view zzz as select * from tableA join tableB on tableA.id = tableB.a_id
  • Generate ProductResultEntity from that zzz view (and delete view zzz when have done)从该zzz视图生成 ProductResultEntity(并在完成后删除视图 zzz)
  • Test call function and enjoy it:测试调用 function 并享受它:
productsRepository.search("123", "%BANK%", PageRequest.of(0, 5, Sort.by(Sort.Direction.ASC, "id")));
  • Entity:实体:
@Entity
public class ProductResultEntity {
    private Long id;
    private String bank;

    @Id
    @Column(name = "id", nullable = false)
    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    @Column(name = "bank_name", nullable = false)
    public String getBank() {
        return bank;
    }

    public void setBank(String bank) {
        this.bank = bank;
    }
}

It does work as below:它的工作原理如下:

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

For me below worked in MS SQL对我来说,下面在 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);

I'm using the code below.我正在使用下面的代码。 working工作

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

Removing \\n#pageable\\n from both query and count query worked for me.从查询和计数查询中删除 \\n#pageable\\n 对我有用。 Springboot version : 2.1.5.RELEASE DB : Mysql Springboot 版本:2.1.5.RELEASE 数据库:Mysql

This worked for me (I am using Postgres) in Groovy:这在 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)

The key here was to use: /*#{#pageable}*/这里的关键是使用: /*#{#pageable}*/

It allows me to do sorting and pagination.它允许我进行排序和分页。 You can test it by using something like this: http://localhost:8080/api/v1/entities/search/namespaceAndNameAndRawStateContainsMostRecentVersion?namespace=&name=&state=published&page=0&size=3&sort=name,desc您可以使用以下内容对其进行测试: http://localhost:8080/api/v1/entities/search/namespaceAndNameAndRawStateContainsMostRecentVersion?namespace=&name=&state=published&page=0&size=3&sort=name,desc

Watch out for this issue: Spring Pageable does not translate @Column name注意这个问题: Spring Pageable 不会翻译@Column 名称

You can use below code for h2 and MySQl您可以将以下代码用于 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);

You can achieve it by using following code,您可以使用以下代码实现它,

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

Simply use ORDER BY ?#{#pageable} and pass page request to your method.只需使用ORDER BY ?#{#pageable}并将页面请求传递给您的方法。

Enjoy!享受!

@Query(value = "select " +
//"row_number() over (order by ba.order_num asc) as id, " +
"row_number() over () as id, " +
"count(ba.order_num),sum(ba.order_qty) as sumqty, " +
"ba.order_num, " +
"md.dpp_code,md.dpp_name, " +
"from biz_arrangement ba " +
"left join mst_dpp md on ba.dpp_code = md.dpp_code " +
"where 1 = 1 " +
"AND (:#{#flilter.customerCodeListCheck}='' OR ba.customer_code IN (:#{#flilter.customerCodeList})) " +
"AND (:#{#flilter.customerNameListCheck}='' OR ba.customer_name IN (:#{#flilter.customerNameList})) " +
"group by " +
"ba.order_num, " +
"md.dpp_code,md.dpp_name ",
countQuery = "select " +
"count ( " +
"distinct ( " +
"ba.order_num, " +
"md.dpp_code,md.dpp_name) " +
")" +
"from biz_arrangement ba " +
"left join mst_dpp md on ba.dpp_code = md.dpp_code " +
"where 1 = 1 " +
"AND (:#{#flilter.customerCodeListCheck}='' OR ba.customer_code IN (:#{#flilter.customerCodeList})) " +
"AND (:#{#flilter.customerNameListCheck}='' OR ba.customer_name IN (:#{#flilter.customerNameList})) ",
nativeQuery = true)
Page<Map<String, Object>> nativeQueryDynamicPageAndSort(@Param("flilter") Flilter flilter, Pageable pageable);

no need to add?#{#pageable}, the problem I got is when I use不需要加吗?#{#pageable},我遇到的问题是我使用的时候

row_number() over (order by ba.order_num asc) as id,

the input sort won't work when I change to当我更改为时,输入排序将不起作用

row_number() over () as id,

the dynamic input sort and pagination are both okay!动态输入排序和分页都可以!

This is a group by query with a row id.这是具有行 ID 的查询分组。

Replacing / #pageable / with ?#{#pageable} allow to do pagination.用 ?#{# pageable } 替换 / #pageable / 允许进行分页。 Adding PageableDefault allow you to set size of page Elements.添加 PageableDefault 允许您设置页面元素的大小。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM