带有分页的 Spring Data 和 Native Query
Posted
技术标签:
【中文标题】带有分页的 Spring Data 和 Native Query【英文标题】:Spring Data and Native Query with pagination 【发布时间】: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
参数,所以hasPagingOrSortingParameter
是true
,但它也在queryString
中寻找#pageable
或#sort
序列,我没有提供。
我尝试在查询末尾添加#pageable
(这是一条注释),这使验证通过,但随后执行失败,说查询需要一个额外的参数:3 而不是 2。
有趣的是,如果我在运行时手动将 containsPageableOrSortInQueryExpression
从 false
更改为 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 解决方法,请确保将其删除,否则您会遇到麻烦。 【参考方案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<Author>
需要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
用于Pageable
。
countQuery
代表Page<User>
。
【讨论】:
我现在不知道。尝试 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<Object[]>
,有时这是必要的)。
【讨论】:
这对我有用,避免在选择查询中使用表别名,它不起作用!【参考方案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 允许您设置页面元素的大小。
【讨论】:
以上是关于带有分页的 Spring Data 和 Native Query的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Spring Data JPA 中使用带有分页的投影接口?
带有分页的 Spring-Data FETCH JOIN 不起作用