Wikipedia defines pagination as “The process of dividing a document into discrete pages, either electronic pages or printed pages”. For a programmer, pagination is the process of dividing a large amount of content to be presented on the UI in chunks. Enterprise Web-based applications, such as search engines, forums, and e-commerce stores use pagination to break up, organize, and present content for increased readability and for a rich user experience. Imagine scrolling down a single page to view 4,84,00,000 results that Google returns when you search for the word “Pagination”. Instead, Google uses pagination to limit the results that are presented to a user per page and provides navigation options to browse the results of the other pages. This is how it looks in Google.

Pagination in Google Search

Pagination is not limited only to Web applications. I have seen Swing-based GUI applications, and also network operations software, CRM, ERP, and inventory control software with pagination enabled. Pagination is a very common use case in software development.

Hibernate, which is one of the most widely adopted ORM framework for Java provides support for pagination. In this post we’ll take a look how we can use features in Hibernate to support pagination.

Pagination Using Query

The Query interface of Hibernate, which is an object-oriented representation of a Hibernate query, provides the setFirstRow() and setMaxResults() methods for pagination. The setFirstRow() method sets the first row to retrieve while the setMaxResults() method sets the maximum number of rows to retrieve from the result set that the Query generates. So if you want to list the first five records from the result set, you will call the methods as query.setFirstResult(0) and query.setMaxResults(5). Note that we passed 0 to setFirstResult() because records in a Query result set starts from the index 0, and we want to start from the first record.

Let’s now set up an application to paginate records stored in a database. For this application, we will use an in memory H2 database. As the first step, we need to add the following dependencies to the Maven POM.

The preceding code adds the H2 database and Hibernate dependencies to the project.

For the application, we will start with a Product entity.

Product.java

The Product class we wrote above is a simple entity decorated with JPA annotations.

Next, we will write a Hibernate utility class that will provide other components of the application a SessionFactory instance to create Hibernate sessions.

HibernateUtil.java

It’s now time to address the main concern of the application – Pagination. Let’s start by writing a ProductDao class.

ProductDao.java

In the saveProducts() method of the ProductDao class above, we saved 30 Product entities to the database. In the listPaginatedProductsUsingQuery() method, we created a Query instance and called the setFirstResult() and setmaxResults() methods passing the int values that the listPaginatedProductsUsingQuery() method receives as arguments. We executed the query and used an enhanced for loop to log the names of the retrieved products. We also wrote a deleteAllProducts() method to delete all Product records from the database.

We will next write the hibernate.cfg.xml configuration file of Hibernate. Ensure that this file is in your project classpath.

hibernate.cfg.xml

Our application is now ready for test. Let’s write this unit test.

For unit testing, we used JUnit in the test class above. We marked the setUp() and cleanUp() methods with the @Before and @After JUnit annotations. If you are new to JUnit, you can go through my series of posts on JUnit here. In the test method marked with @Test, we called the listPaginatedProductsUsingQuery() method on ProductDao passing 0 to specify the first record and 10 to specify the maximum records to retrieve.

The output on running the test is this.

The output above shows the log messages that list the names of the first ten products stored in the database.

Pagination Using Criteria

Pagination using Criteria is the same as using Query. The Criteria interface, similar to the Query interface, provides the setFirstResult() and setMaxResults() methods to support pagination.

In the ProductDao class, lets add a listPaginatedProductsUsingCriteria() method to perform pagination using Criteria.

In the preceding listPaginatedProductsUsingCriteria() method we wrote above, we called the setFirstRow() and setMaxRows() method on a Criteria instance to perform pagination.

The test code for the listPaginatedProductsUsingCriteria() method is this.

The output of the test is this.

As you can observe in the output above, the names of five products starting from the tenth record are logged.

Calculating the Total Number of Records

A common requirement of pagination is to calculate the total number of records to be displayed in the navigation options of the UI. This figure shows few navigation options that make use of the total number of records.

Navigation Options with Total Records

If you are using Criteria, one simple approach is to make two database calls for every page fetch: One for results and another for the total record count. This approach works fine for simple applications, but is not efficient for enterprise-grade applications receiving millions of requests per day. An efficient approach is to use the same Criteria to retrieve both the results and the result count with a single database call. It can be achieved using ScrollableResults – an interface for objects that allow navigating results by arbitrary increments.

To calculate the total result counts along with the results in a single database call, lets add a static getCriteria() method to ProductDao. This method takes a Hibernate Session, constructs a Criteria from it with a restriction and projections before returning the Criteria to the caller.

The code for the getCriteria() method is this.

In ProductDao, let’s add another method with the name listPaginatedProductsUsingScrollableResults(). This method, in addition to performing pagination, calculates the total record count in the result set.

In Line 6 of the listPaginatedProductsUsingScrollableResults() method above, we called the getCriteria() static method, and then called the scroll() method on the Criteria object. The scroll() method returns a ScrollableResults instance. In Line 7 we moved the ScrollableResults instance to the last record and in Line 8, we calculated the total record count. The rest of the code is all about setting the first result and maximum results for pagination and logging the product names of the paginated records. We finally returned the total record count to the caller in Line 24.

The test code for the listPaginatedProductsUsingScrollableResults() method is this.

The output of the test is this.

As you can observe in the output, the names of the first three products along with the total product count are logged.

Summary

Pagination can be handled on the client side, the server side, the database, or a mix of them. Hibernate pagination is done on the server side and, as you have seen, it’s pretty easy to understand and use. Some may argue that handling pagination on the server side results in low UI responsiveness as compared to client side pagination done using JavaScript, jQuery, or some third-party pagination plugins. However, client side pagination can lead to significant lag in the initial page load time for large data sets that are common in enterprise applications.

There is no right or wrong approach for handling pagination. Select the pagination approach that best suits your application requirements. When developing applications that use Hibernate as the ORM framework, it’s logical to make use of the built-in pagination capabilities of Hibernate instead of reinventing the wheel.

You will notice even though this is a website focused on the Spring Framework, Spring was not used in this post. Spring projects such as Spring Data JPA, or other projects like Grails use Hibernate Pagination under the covers. You as the developer do not interact with Hibernate Pagination directly. This is abstracted by the tools you are using. But often, it is important to understand what is going on under the covers.

6
Share