如何对 JPA 查询进行分页
Posted
技术标签:
【中文标题】如何对 JPA 查询进行分页【英文标题】:How to paginate a JPA Query 【发布时间】:2013-04-18 16:56:40 【问题描述】:我有一个提交表,其中包含 ID
、Name
、Code
等列以及其他属性。我的要求是根据提到的属性搜索记录并返回一个分页集。
这是我正在寻找的伪代码:
searchSubmission(searchFilter sf,pageIndex,noOfRecords)
query = 'from submisssion where code=sf.code or id=sf.id order by id start_from (pageIndex*noOfRecords) limit noOfRecords'
return result();
似乎有很多选项,例如CriteriaBuilder
、NamedQuery
等。在这种情况下,哪个选项最有效?
【问题讨论】:
【参考方案1】:对于所有 JPA 查询对象(本地 SQL 查询除外),您将通过 setMaxResults(int) 和 setFirstResult(int) 方法使用分页。例如:
return em.createNamedQuery("yourqueryname", YourEntity.class)
.setMaxResults(noOfRecords)
.setFirstResult(pageIndex * noOfRecords)
.getResultList();
JPA 将为您执行分页。
命名查询只是预定义的,可以缓存,而其他类型则是动态创建的。 所以选择使用 JPQL,如:
Query query = em.createQuery("SELECT s FROM Submission s WHERE s.code = :code or s.id = :id ORDER BY s.id", Submission.class);
或 CriteriaBuilder api 形成类似的查询:
CriteriaBuilder qb = em.getCriteriaBuilder();
CriteriaQuery<Submission> cq = qb.createQuery(Submission.class);
Root<Submission> root = cq.from(Submission.class);
cq.where( qb.or(
qb.equal(root.get("code"), qb.parameter(String.class, "code")),
qb.equal(root.get("id"), qb.parameter(Integer.class, "id"))
));
Query query = em.createQuery(cq);
不要忘记使用 query.setParameter("id", sf.id) 设置参数值。
【讨论】:
【参考方案2】:你可以在Spring
的repository方法中使用Pageable
@Repository
public interface StateRepository extends JpaRepository<State, Serializable>
@Query("select state from State state where state.stateId.stateCode = ?1")
public State findStateByCode(String code, Pageable pageable);
而在服务层,可以创建Pageable
对象:
@Autowire
StateRepository stateRepository;
public State findStateServiceByCode(String code, int page, int size)
Pageable pageable = new PageRequest(page, size);
Page<Order> statePage = stateRepository.findStateByCode(code, pageable);
return statePage.getContent();
【讨论】:
构造函数 new PageRequest(page, size) 已弃用。使用 Pageable pageable = PageRequest.of(page, size) 代替 如果我没记错的话,这是特定于 Spring 的。你能在你的答案中指定它吗?【参考方案3】:您可以将 JPA 分页用于实体查询和原生 SQL。
为了限制底层查询ResultSet
的大小,JPA Query
接口提供了setMaxResults
方法。
浏览下一页需要将结果集定位到最后一页结束的位置。为此,JPA Query
接口提供了setFirstResult
方法。
在 JPQL 查询中使用分页如下所示:
List<Post> posts = entityManager.createQuery("""
select p
from Post p
order by p.createdOn
""")
.setFirstResult(10)
.setMaxResults(10)
.getResultList();
Criteria API 是一样的,因为您需要从CriteriaQuery
创建一个Query
:
CriteriaBuilder qb = em.getCriteriaBuilder();
CriteriaQuery<Post> cq = qb.createQuery(Post.class);
Root<Post> root = cq.from(Post.class);
cq.orderBy(qb.asc(root.get("createdOn")));
List<Post> posts = em.createQuery(cq)
.setFirstResult(10)
.setMaxResults(10)
.getResultList();
【讨论】:
虽然 setMaxResults 和 setFirstResult api 确实存在并且可用于 Native SQL 查询,但我会犹豫使用它们。它们的工作方式取决于提供者,但由于它们只是通过 Native SQL 语句,因此唯一可以进行的过滤是对完整返回的结果进行过滤。同样,依赖于实现,但是 setFirstResult/setMaxResults 允许提供者在 JPQL 或条件查询时将 DB 特定的分页(如 rownum 过滤)注入到生成的 SQL 中。您最好对本机 SQL 查询执行相同操作以避免网络/数据库命中。 @Chris HIbernate 只是在将 SQL 发送到数据库之前对其进行修改。没有额外的网络/数据库命中。 它是否修改了它发送到数据库的本机 SQL 字符串?对于 JPQL/CriteriaQuery/HB 查询,当然,它可以修改它生成的 SQL,但我很惊讶它对于 Native SQL 查询会这样做,因为担心它会弄乱 SQL。如前所述,这是特定于 JPA 提供程序的 - 其他人不会触及您的 Native SQL 查询,因此依赖于其他方式进行分页,这比您在 SQL 本身中可以和应该做的要少。 您可能想知道它是如何工作的,但作为一名*** Hibernate 提交者,我可以为您确认这一点。这是GitHub 的一个示例。至于 JPA 的可移植性,那是神话。人们并没有真正改变 JPA 提供者,而且绝大多数人都使用 Hibernate,所以这不是现实生活中的问题。 感谢您的信息,但令我惊讶的是他们这样做,而不是他们如何做到的。我会避免进入 JPA 供应商辩论,因为这个问题/线程不在 Hibernate 或您对 JPA 的看法上,我怀疑有人关心我自己的。以上是关于如何对 JPA 查询进行分页的主要内容,如果未能解决你的问题,请参考以下文章