Hibernate Pagination – 休眠分页

最后修改: 2014年 4月 10日

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

1. Overview

1.概述

This article is a quick introduction to Pagination in Hibernate. We will look at the standard HQL as well as the ScrollableResults API, and finally, at pagination with Hibernate Criteria.

这篇文章是对Hibernate中分页的快速介绍。我们将看看标准的HQL以及ScrollableResults API,最后是用Hibernate Criteria进行分页。

2. Pagination With HQL and setFirstResult, setMaxResults API

2.用HQL和setFirstResult, setMaxResults API进行分页

The simplest and most common way to do pagination in Hibernate is using HQL:

在Hibernate中进行分页的最简单和最常见的方法是使用HQL

Session session = sessionFactory.openSession();
Query query = sess.createQuery("From Foo");
query.setFirstResult(0);
query.setMaxResults(10);
List<Foo> fooList = fooList = query.list();

This example is using a basic Foo entity and is very much similar to the JPA with JQL implementation – the only difference being the query language.

这个例子使用的是一个基本的Foo实体,与JPA与JQL的实现非常相似–唯一的区别是查询语言。

If we turn on logging for Hibernate, we’ll see the following SQL being run:

如果我们打开Hibernate的日志记录,我们将看到以下SQL的运行。

Hibernate: 
    select
        foo0_.id as id1_1_,
        foo0_.name as name2_1_ 
    from
        Foo foo0_ limit ?

2.1. The Total Count and the Last Page

2.1.总数和最后一页

A pagination solution is not complete without knowing the total number of entities:

如果不知道实体的总数,分页解决方案就不完整。

String countQ = "Select count (f.id) from Foo f";
Query countQuery = session.createQuery(countQ);
Long countResults = (Long) countQuery.uniqueResult();

And lastly, from the total number and a given page size, we can calculate the last page:

最后,从总数和一个给定的页面大小,我们可以计算出最后一页

int pageSize = 10;
int lastPageNumber = (int) (Math.ceil(countResults / pageSize));

At this point we can look at a complete example for pagination, where we are calculating the last page and then retrieving it:

在这一点上,我们可以看看一个完整的分页例子,我们正在计算最后一页,然后检索它。

@Test
public void givenEntitiesExist_whenRetrievingLastPage_thenCorrectSize() {
    int pageSize = 10;
    String countQ = "Select count (f.id) from Foo f";
    Query countQuery = session.createQuery(countQ);
    Long countResults = (Long) countQuery.uniqueResult();
    int lastPageNumber = (int) (Math.ceil(countResults / pageSize));

    Query selectQuery = session.createQuery("From Foo");
    selectQuery.setFirstResult((lastPageNumber - 1) * pageSize);
    selectQuery.setMaxResults(pageSize);
    List<Foo> lastPage = selectQuery.list();

    assertThat(lastPage, hasSize(lessThan(pageSize + 1)));
}

3. Pagination With Hibernate Using HQL and the ScrollableResults API

3.使用HQL和ScrollableResults API的Hibernate分页

Using ScrollableResults to implement pagination has the potential to reduce database calls. This approach streams the result set as the program scrolls through it, therefore eliminating the need to repeat the query to fill each page:

使用ScrollableResults来实现分页有可能减少数据库调用。这种方法在程序滚动时流转结果集,因此不需要重复查询以填充每一页。

String hql = "FROM Foo f order by f.name";
Query query = session.createQuery(hql);
int pageSize = 10;

ScrollableResults resultScroll = query.scroll(ScrollMode.FORWARD_ONLY);
resultScroll.first();
resultScroll.scroll(0);
List<Foo> fooPage = Lists.newArrayList();
int i = 0;
while (pageSize > i++) {
    fooPage.add((Foo) resultScroll.get(0));
    if (!resultScroll.next())
        break;
}

This method is not only time-efficient (only one database call), but it allows the user to get access to the total count of the result set without an additional query:

这种方法不仅省时(只有一次数据库调用),而且允许用户在不进行额外查询的情况下获得对结果集的总计数的访问

resultScroll.last();
int totalResults = resultScroll.getRowNumber() + 1;

On the other hand, keep in mind that, although scrolling is quite efficient, a large window may take up a decent amount of memory.

另一方面,请记住,尽管滚动是相当有效的,但一个大的窗口可能会占用相当多的内存

4. Pagination With Hibernate Using the Criteria API

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

Finally, let’s look at a more flexible solution – using criteria:

最后,让我们看看一个更灵活的解决方案–使用标准。

Criteria criteria = session.createCriteria(Foo.class);
criteria.setFirstResult(0);
criteria.setMaxResults(pageSize);
List<Foo> firstPage = criteria.list();

Hibernate Criteria query API makes it very simple to also get the total count – by using a Projection object:

Hibernate Criteria查询API使得获取总计数非常简单–通过使用Projection对象。

Criteria criteriaCount = session.createCriteria(Foo.class);
criteriaCount.setProjection(Projections.rowCount());
Long count = (Long) criteriaCount.uniqueResult();

As you can see, using this API will result in minimally more verbose code than plain HQL, but the API is fully type safe and a lot more flexible.

正如你所看到的,使用这个API将导致比普通的HQL更少的冗长的代码,但API是完全类型安全的,而且更灵活

5. Conclusion

5.结论

This article is a quick introduction to the various ways of doing pagination in Hibernate.

本文是对Hibernate中各种分页方式的快速介绍。

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

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