JPA Pagination – JPA分页

最后修改: 2014年 4月 8日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

This article illustrates how to implement pagination in the Java Persistence API.

本文说明了如何在Java Persistence API中实现pagination

It explains how to do paging with basic JQL and with the more type-safe Criteria-based API’s, discussing the advantages and known issues of each implementation.

它解释了如何用基本的JQL和更安全的基于类型的API进行分页,讨论了每种实现的优势和已知的问题。

2. Pagination With JQL and the setFirstResult(), setMaxResults() API

2.用JQL和setFirstResult()setMaxResults() API进行分页

The simplest way to implement pagination is to use the Java Query Language – create a query and configure it via setMaxResults and setFirstResult:

实现分页的最简单方法是使用Java查询语言–创建一个查询,并通过setMaxResultssetFirstResult配置它。

Query query = entityManager.createQuery("From Foo");
int pageNumber = 1;
int pageSize = 10;
query.setFirstResult((pageNumber-1) * pageSize); 
query.setMaxResults(pageSize);
List <Foo> fooList = query.getResultList();

The API is simple:

该API很简单。

  • setFirstResult(int): Sets the offset position in the result set to start pagination
  • setMaxResults(int): Sets the maximum number of entities that should be included in the page

2.1. The Total Count and the Last Page

2.1.总数和最后一页

For a more complete pagination solution, we’ll also need to get the total result count:

对于一个更完整的分页解决方案,我们还需要获得总结果数

Query queryTotal = entityManager.createQuery
    ("Select count(f.id) from Foo f");
long countResult = (long)queryTotal.getSingleResult();

Calculating the last page is also very useful:

计算最后一页也非常有用。

int pageSize = 10;
int pageNumber = (int) ((countResult / pageSize) + 1);

Notice that this approach to getting the total count of the result set does require an additional query (for the count).

请注意,这种获得结果集总计数的方法确实需要一个额外的查询(用于计数)。

3. Pagination With JQL Using the Id’s of Entities

3.使用实体的Id进行JQL分页

A simple alternative pagination strategy is to first retrieve the full ids and then – based on these – retrieve the full entities. This allows for better control of entity fetching – but it also means that it needs to load the entire table to retrieve the ids:

一个简单的替代分页策略是:首先检索完整的ID,然后–基于这些–检索完整的实体。这可以更好地控制实体的获取–但这也意味着需要加载整个表来获取ID。

Query queryForIds = entityManager.createQuery(
  "Select f.id from Foo f order by f.lastName");
List<Integer> fooIds = queryForIds.getResultList();
Query query = entityManager.createQuery(
  "Select f from Foo e where f.id in :ids");
query.setParameter("ids", fooIds.subList(0,10));
List<Foo> fooList = query.getResultList();

Finally, also note that it requires 2 distinct queries to retrieve the full results.

最后,还要注意的是,它需要2次不同的查询来检索全部结果。

4. Pagination With JPA Using Criteria API

4.用JPA的标准API进行分页

Next, let’s look at how we can leverage the JPA Criteria API to implement pagination:

接下来,让我们看看我们如何利用JPA Criteria API来实现分页。

int pageSize = 10;
CriteriaBuilder criteriaBuilder = entityManager
  .getCriteriaBuilder();
CriteriaQuery<Foo> criteriaQuery = criteriaBuilder
  .createQuery(Foo.class);
Root<Foo> from = criteriaQuery.from(Foo.class);
CriteriaQuery<Foo> select = criteriaQuery.select(from);
TypedQuery<Foo> typedQuery = entityManager.createQuery(select);
typedQuery.setFirstResult(0);
typedQuery.setMaxResults(pageSize);
List<Foo> fooList = typedQuery.getResultList();

This is useful when the aim is to create dynamic, failure-safe queries. In contrast to “hard-coded”, “string-based” JQL or HQL queries, JPA Criteria reduces run-time failures because the compiler dynamically checks for query errors.

当目的是创建动态的、无故障的查询时,这很有用。与 “硬编码”、”基于字符串 “的JQL或HQL查询相比,JPA Criteria减少了运行时的故障,因为编译器会动态地检查查询错误。

With JPA Criteria getting the total number of entities in simple enough:

使用JPA标准获得实体的总数就很简单。

CriteriaQuery<Long> countQuery = criteriaBuilder
  .createQuery(Long.class);
countQuery.select(criteriaBuilder.count(
  countQuery.from(Foo.class)));
Long count = entityManager.createQuery(countQuery)
  .getSingleResult();

The end result is a full pagination solution, using the JPA Criteria API:

最终的结果是一个完整的分页解决方案,使用JPA Criteria API。

int pageNumber = 1;
int pageSize = 10;
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();

CriteriaQuery<Long> countQuery = criteriaBuilder
  .createQuery(Long.class);
countQuery.select(criteriaBuilder
  .count(countQuery.from(Foo.class)));
Long count = entityManager.createQuery(countQuery)
  .getSingleResult();

CriteriaQuery<Foo> criteriaQuery = criteriaBuilder
  .createQuery(Foo.class);
Root<Foo> from = criteriaQuery.from(Foo.class);
CriteriaQuery<Foo> select = criteriaQuery.select(from);

TypedQuery<Foo> typedQuery = entityManager.createQuery(select);
while (pageNumber < count.intValue()) {
    typedQuery.setFirstResult(pageNumber - 1);
    typedQuery.setMaxResults(pageSize);
    System.out.println("Current page: " + typedQuery.getResultList());
    pageNumber += pageSize;
}

5. Conclusion

5.结论

This article has explored the basic pagination options available in JPA.

这篇文章已经探讨了JPA中可用的基本分页选项。

Some have drawbacks – mainly related to query performance, but these are usually offset by improved control and overall flexibility.

一些有缺点–主要与查询性能有关,但这些缺点通常被改进的控制和整体灵活性所抵消。

The implementation of this Spring JPA Tutorial can be found in the GitHub project – this is a Maven-based project, so it should be easy to import and run as it is.

这个Spring JPA教程的实现可以在GitHub项目中找到 – 这是一个基于Maven的项目,因此应该很容易导入并按原样运行。