Spring Data和本地查询与分页

120

在一个web项目中,我正在使用最新的spring-data(1.10.2)与MySQL 5.6数据库,并尝试使用本地查询进行分页,但是在启动时遇到了org.springframework.data.jpa.repository.query.InvalidJpaQueryMethodException异常。

更新:20180306 此问题现在在Spring 2.0.4中已修复。对于那些仍然感兴趣或被困在旧版本中的人,请查看相关答案和评论以获取解决方法。

根据spring-data文档中的示例50-使用@Query,可以通过指定查询本身和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,但它还在查询字符串中查找一个 #pageable#sort 序列,而我没有提供它。

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

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

任何帮助将不胜感激。

更新于 2018 年 01 月 30 日 看起来,spring-data 项目的开发人员正在使用 Jens Schauder 的PR 修复此问题。


不,我还没有找到解决方案。我在Spring JIRA上创建了一个工单,但是没有得到回应:https://jira.spring.io/browse/DATAJPA-928。最终,我并不需要分页,所以我没有进一步调查或者在那个工单上继续推动。 - Lasneyx
3
好的,谢谢。作为一种解决方法,你可以用"\n#pageable\n"代替"#pageable",但我希望这个问题在将来会得到修复。 - Janar
1
当前的修复对我来说使情况变得更糟。现在我的PageAble参数完全被忽略了,查询被无限执行。因此,如果您插入了那个\n#pageable\n解决方法,请确保将其删除,否则您会遇到麻烦。 - Rüdiger Schulz
对我来说,我使用了@Param("lastname")注解,问题出在WHERE LASTNAME = \":lastname\",这里的双引号是不必要的。因此,问题是我简单地忘记从SQL参数中删除双引号。 - lechat
正如其他人所指出的那样,在较新版本的JPA中不再需要添加#pageable,但请确保您的Pageable参数已指定了Sort,否则它将被忽略。 - undefined
20个回答

1
  • 创建您的自定义代码仓库:
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 zzz as select * from tableA join tableB on tableA.id = tableB.a_id
  • zzz视图生成ProductResultEntity(完成后删除zzz视图)
  • 测试调用函数并享受它:
productsRepository.search("123", "%BANK%", PageRequest.of(0, 5, Sort.by(Sort.Direction.ASC, "id")));
  • 实体:
@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;
    }
}

使用Inteliji中的工具生成的视图实体:持久性,或者您可以手动编写它。 - nobjta_9x_tq

1

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


0

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


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

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

2
请问您能否解释一下为什么这个答案比其他已经存在了几年的答案更好? - Dragonthoughts

-1

它的工作方式如下:

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

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

不需要添加 ?#{#pageable}, 我遇到的问题是当我使用时

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

当我更改输入排序时,它无法工作。

row_number() over () as id,

动态输入排序和分页都可以!

这是一个带有行 ID 的分组查询。


-1

你可以通过使用以下代码来实现它:

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

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

享受吧!


-2

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接