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.

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.

According to Example 50 at Using @Query from spring-data documentation this is possible specifying the query itself and a countQuery, like this:

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:

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.

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.

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.

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

172650 次浏览

My apologies in advance, this is pretty much summing up the original question and the comment from Janar, however...

我遇到了同样的问题: 我发现 Spring 数据的示例50可以解决我需要一个带有分页的本地查询的问题,但是 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”注释周围换行,以避免在预期参数数量上出现运行时错误(解决方案的工作区)。我希望这个错误将很快被修复..。

我有完全相同的症状像@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);

这是一个使用 版本2.0.4之前的 Spring Data JPA编写的程序。

代码可以与 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<User>

以下两种方法都可以很好地使用 MySQL 对本机查询进行分页。但是它们对 H2不起作用。它将报告 sql 语法错误。

  • 订购人
  • 可分页的订单

试试这个:

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)

仅供记录,使用 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,H21.4.194,MySQL Community Server 5.7.11-log (injdb _ version 5.7.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);
}

使用“ ORDER BY id DESC n —— # pageable n” 而不是“ ORDER BY id n # pageable n”对我来说在 MS SQL SERVER 中工作

我使用 Oracle 数据库,但是没有得到结果,而是使用 d-man 上面提到的生成的逗号得到了一个错误。

然后我的解决办法是:

Pageable pageable = new PageRequest(current, rowCount);

正如你可以看到没有顺序时,创建分页。

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

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

对于我来说,以下工作在 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);

我正在使用下面的代码

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

从查询和计数查询中删除 n # 可分页 n 对我来说都很有效。 Springboot 版本: 2.1.5. 发布 DB: Mysql

这在 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}*/

它允许我进行排序和分页。 你可以用这样的东西来测试它: < a href = “ http://localhost: 8080/api/v1/tity/search/namepaceAndNameAndRawStateContainsMostRecentVersion? nampace = & amp; name = & amp; state = publications & amp; page = 0 & amp; size = 3 & amp; sort = name,desc”rel = “ nofollow noReferrer”> http://localhost:8080/api/v1/entities/search/namespaceandnameandrawstatecontainsmostrecentversion?namespace=&name=&state=published&page=0&size=3&sort=name,desc

留意这个问题: SpringPageable 不翻译@Column 名称

您可以使用下面的 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);

我可以成功地将 Pagination 集成到

Spring-data-jpa-2.1.6

如下所示。

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

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

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

Simply use 订购人 and pass page request to your method.

好好享受吧!

我添加这个答案只是作为那些使用最新版本的 SpringBoot 的用户的占位符。在 SpringBoot2.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[]>,有时这是必要的)。

  • 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 zzz as select * from tableA join tableB on tableA.id = tableB.a_id
  • 从该 视图生成 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;
}
}
@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);

不需要添加吗, 我的问题是当我使用

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

输入排序不起作用 当我变成

row_number() over () as id,

动态输入排序和分页都没问题!

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