http://www.developer.com/

Back to article

A Pagination Technique Using Spring


July 21, 2009

You can implement the pagination of result sets in Java-based web applications in many ways. You can do it with custom algorithms or third-party packages, on the server side or client side, and so on. The Spring Framework provides a convenient method for server-side implementation out of the box.

This article demonstrates how to use the Spring Framework to implement a pagination mechanism for server responses. While it focuses on web applications that use Java and Spring, it will touch on related pagination techniques as well.

The implementation discussed here may not be suitable for all scenarios. You have to account for considerations such as performance and the needs of the particular application during the design process. To help with design decisions, this article also provides the cons and pros of the pagination implementation.

UI and Performance Considerations

In enterprise Java web applications with n-tier design, the data flow typically goes from the database layer through the web application layer to the client layer. The processing of the business logic usually resides at the application server layer, and the client gets the end result rendered in some form of UI.

Presenting medium-sized to large data sets in tabular form usually requires a pagination mechanism that enables the client to see some portion of the final result and navigate back and forth through the result set. This is a very common feature in modern web applications.

You must answer a few very important questions—which will drive the design logic—during pagination design. The maximum result set per query, performance requirements, and the need for real-time data all need to be determined upfront. If the application will always return small to medium-sized results (for example, 10–5000 per query), you can implement server-side (or even client-side) solutions. If your result set is static but it can exceed 1000–2000 rows, you could consider server memory caching, but only within server memory limits. If your result set is dynamic and is constantly changing in real time, then even small responses need to come directly from the database layer.

With the client-side implantation, you transfer the entire result set from the database to the application server and then to the client. The client then shows only a portion of the results to eliminate user scrolling or to fit them into the display area. This mechanism requires no special logic on the database or application server layers. Rather, it implements pagination logic on the client using specific client technology, such as Flex, JavaScript, Applets, and so on. (For a complete example of one client-side implementation, see my other article "Implementing Search Result Pagination in a Web Application".)

With application server pagination, the entire result set is transferred from the database to the application server, but only a portion is then transferred to the client. Specific server-side pagination logic controls (usually with caching) what is sent to the client based on the requests. Usually you use this method with third-party packages or libraries, and it is server technology specific. For example, it is supported by the Spring Framework, which this article describes.

With the database layer implementation, only a portion of the entire result set is transferred from the database to the application server, and then from the app server to the client. This method depends on a specific database vendor implementation and support of that implementation on the application server layer. For example, these implementations can include SQL syntax that can "limit" a result set, scrollable result sets (supported in some JDBC drivers), cursor-based stored procedures or functions, and so on. The database layer implementation depends on the database and can be either easy or cumbersome to implement, because each database vendor provides a different mechanism for data retrieval. But most modern ORM databases provide this functionality.

The tables in the next section can help you decide on the best approach.

Methods of Pagination: Back End, Middle Tier, and Front End

The pagination algorithm can reside on any of the n-tier application layers, but the response size ultimately determines where it can be implemented.

This table shows the pros and cons of the location of the pagination logic.

Java Spring implementation

Assuming your application returns small- to medium-sized result sets every time, then implementing the Spring Framework on the application server layer is a good approach. You can easily combine this approach with memory-caching to save database resources and reduce network traffic.

The logic to do Spring pagination will reside mainly on the server, but requires a small UI control on the client. The client-side control handles the look and feel for pagination, and exposes links that let the server know which portion of the total result set to return next (a.k.a., page navigation).

Here is an example of the look-and-feel CSS (download project for full source)

The Client-Side Look and Feel

.pagingItem {
font-weight: normal;
text-decoration: none;
color: #747474;
margin: 0 2px;
padding: 0 2px;
background-color: #eeeeee;
border: 1px solid #bababa;
font-size: 0.9em;
line-height: 1.5em;
}
.pagingItemCurrent {
padding: 0 2px;
margin: 0 2px;
font-weight: normal;
color: #FFFFFF;
background-color: #bfbfbf;
border: 1px solid #bfbfbf;
font-size: 0.9em;
}

This will render something like this:

But you can easily change the CSS to any preferred look:

The Controller

The Spring Framework comes with a bean called org.springframework.beans.support.PagedListHolder,. This class will do all the heavy lifting for the pagination logic implementation. This bean needs to be populated with a List; and setter methods are available to indicate page size and page number. The bean will do the rest. You set it in the controller, which then returns a ModelAndView object to the client layer.

mav.addObject("pagedListHolder", pagedListHolder);

Here is a description of this bean from the Spring API:

"PagedListHolder is a simple state holder for handling lists of objects, separating them into pages. Page numbering starts with 0. "

This is mainly targeted for usage in web UIs. Typically, an instance will be instantiated with a list of beans, put into the session, and exported as a model. The properties can all be set/get programmatically, but the most common way will be data binding (i.e., populating the bean from request parameters). The getters will mainly be used by the view.

The controller supports sorting the underlying list via a SortDefinition implementation, available as property "sort." By default, a MutableSortDefinition instance will be used, toggling the ascending value when you set the same property again.

The data binding names have to be called "pageSize" and "sort.ascending," as expected by BeanWrapper. Note that the names and the nesting syntax match the respective JSTL EL expressions, such as "myModelAttr.pageSize" and "myModelAttr.sort.ascending."

Here is the code portion from the source that enables pagination. In this example, the ItemDao simply returns a list of random numbers, but a real-world application would be similar, although it would get its data from a database. (See the complete source for more details.)

public ModelAndView list(HttpServletRequest request, HttpServletResponse response) throws Exception {
springAppContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
ModelAndView mav = new ModelAndView("paging");
// get data
ItemDao dao = (ItemDao) springAppContext.getBean("itemDao");
List searchResults = dao.getAllItems();
// initialize PagedListHolder with our list, set current page defaulted to 0, and pass it to the view
PagedListHolder pagedListHolder = new PagedListHolder(searchResults);
int page = ServletRequestUtils.getIntParameter(request, "p", 0);
pagedListHolder.setPage(page);
int pageSize = 10;
pagedListHolder.setPageSize(pageSize);
mav.addObject("pagedListHolder", pagedListHolder);
return mav;
}

The View Layer

On the JSP layer, you need to read this object and have a way to show page links and its contents. The hidden variable p controls the page the user is viewing, and the <c:forEach loop traverses the result and displays its contents.

<%@ include file="/jsp/includes.jsp" %>
<%@ taglib prefix="tg" tagdir="/WEB-INF/tags" %>
<%-- // use our pagedListHolder --%>
<jsp:useBean id="pagedListHolder" scope="request" 
type="org.springframework.beans.support.PagedListHolder"/> <%-- // create link for pages, "~" will be replaced
later on with the proper page number --%> <c:url value="/paging.do" var="pagedLink"> <c:param name="action" value="list"/> <c:param name="p" value="~"/> </c:url> <%-- // load our paging tag, pass pagedListHolder and the link --%> <tg:paging pagedListHolder="${pagedListHolder}" pagedLink="${pagedLink}"/> <%-- // show only current page worth of data --%> <table width="200px" border="1"> <tr> <th width="20px">No.</th> <th>Random Number</th> </tr> <c:forEach items="${pagedListHolder.pageList}" var="item"> <tr> <td>${item.key}</td> <td style="color:blue;font-weight:bold;text-align:right">${item.data}</td> </tr> </c:forEach> </table> <%-- // load our paging tag, pass pagedListHolder and the link --%> <tg:paging pagedListHolder="${pagedListHolder}" pagedLink="${pagedLink}"/>

Note that the actual page links display logic is defined in the JSP tag "<tg:paging>"(/WEB-INF/tags/paging.tag). The pagedListHolder and current page are passed to it as parameters. The tag then correctly displays page links, including arrows and gaps in pages if the number exceeds specific display limits.

<tg:paging pagedListHolder="${pagedListHolder}" pagedLink="${pagedLink}"/>

Here is the source for the paging tag, which should be included in the WEB-INFtags folder.

<%@ tag import="org.springframework.util.StringUtils" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ attribute name="pagedListHolder" required="true" type="org.springframework.beans.support.PagedListHolder" %>
<%@ attribute name="pagedLink" required="true" type="java.lang.String" %>
<link href="css/pagination.css" rel="stylesheet" type="text/css"/>
<c:if test="${pagedListHolder.pageCount > 1}">
<c:if test="${!pagedListHolder.firstPage}">
<span class="pagingItem"><a href="<%= StringUtils.replace(pagedLink, 
"~", String.valueOf(pagedListHolder.getPage()-1)) %>"><</a></span> </c:if> <c:if test="${pagedListHolder.firstLinkedPage > 0}"> <span class="pagingItem"><a href="<%= StringUtils.replace(pagedLink, "~", "0") %>">1</a></span> </c:if> <c:if test="${pagedListHolder.firstLinkedPage > 1}"> <span class="pagingDots">...</span> </c:if> <c:forEach begin="${pagedListHolder.firstLinkedPage}" end="${pagedListHolder.lastLinkedPage}" var="i"> <c:choose> <c:when test="${pagedListHolder.page == i}"> <span class="pagingItemCurrent">${i+1}</span> </c:when> <c:otherwise> <span class="pagingItem"><a href="<%= StringUtils.replace(pagedLink,
"~", String.valueOf(jspContext.getAttribute("i"))) %>">${i+1}</a></span> </c:otherwise> </c:choose> </c:forEach> <c:if test="${pagedListHolder.lastLinkedPage < pagedListHolder.pageCount - 2}"> <span class="pagingDots">...</span> </c:if> <c:if test="${pagedListHolder.lastLinkedPage < pagedListHolder.pageCount - 1}"> <span class="pagingItem"><a href="<%= StringUtils.replace(pagedLink,
"~", String.valueOf(pagedListHolder.getPageCount()-1)) %>">${pagedListHolder.pageCount}</a></span> </c:if> <c:if test="${!pagedListHolder.lastPage}"> <span class="pagingItem"><a href="<%= StringUtils.replace(pagedLink,
"~", String.valueOf(pagedListHolder.getPage()+1)) %>">></a></span> </c:if> </c:if>

Conclusion

This article discussed a pagination mechanism based on the Spring Framework. The implementation is designed for a Java application's server layer and assumes JSP client technology. If your web or portlet project is already using Spring, adding pagination should be straightforward. The article also discussed different approaches to the pagination logic and the best layer in which to implement it.

Special thanks to Joe Zhao for providing some of the sample code for this article and guiding me in the implementation details.

Code Download

  • Spring-Pagination

    For Further Reading

  • Spring Framework's PagedListHolder
  • JSP Pagination with Spring
  • "Implementing Search Result Pagination in a Web Application"

    About the Author

    Vlad Kofman works on enterprise-scale projects for major Wall Street firms. He has also worked on defense contracts for the U.S. government. His main interests are web programming methodologies, UI patterns, and SOA.
  • Sitemap | Contact Us

    Thanks for your registration, follow us on our social networks to keep up-to-date