Paging long lists

Don't extract all at once

When displaying long lists, one of the most common Web UI techniques is to show 10 or 15 results at a time, allowing the user to scroll through the list. In most cases, the lists can be quite large, and returning the large result set might prove impractical. You wouldn't want to load the long list in session and then loop through the results.

One way of approaching this task is to return the results from the database pertinent to the current request. The page developer shouldn't worry about the anchors at the bottom of the page; he should worry only about how he should display the current list for that request. The anchors for looping through a large list should be done through a pagination tag. Pagination means fetching and displaying the results one page at a time, and a pagination tag removes from the developer the burden of coding for navigation and anchors.

This article explains what a pagination tag can do, how to use it, and best practices for displaying long lists.

You can download the pagination tag I present in this article from Resources.

Pagination tag

Let's take a scenario in Struts-speak. Once the user completes a search, the actual search results might be 1,000, for example. But from our middle tier, we don't return 1,000 results; instead we limit our query to return 10 or 15 results.

In MySQL, we can do that like this:

  SELECT * FROM TABLE WHERE {CONDITION} LIMIT 0, 10.

Note: The above example shows the use of the LIMIT clause, which works in MySQL. The logic of limiting the query is left to the DAO (data access object) developer or the database administrator.

We also calculate the total number of results found for that particular query. Once we get the first 10 results and the total, we can set that to our ActionForm or to the request and then forward that to the JSP page (JavaServer Pages), which displays the 10 results.

To allow the user to navigate through the total list, we must build the logic that allows that functionality. Instead of laying that burden on the page developer, I have developed a pagination tag that displays the total number of pages, the current page the user is on, and the links for the next page and previous pages if required. If the user is on the first page, no Previous page will display. Similarly, if the user is on the last page, no Next page displays.

In a JSP page, we can put the tag to work like this:

  <pagination-tag:pager start="<%=start%>" range="<%=range%>" results="<%=results%>"/>

For this tag library to work correctly, we must import the tag library at the top of the JSP page, which can be done like this:

  <%@ taglib uri="http://paginationtag.miin.com" prefix="pagination-tag"%> or
  <%@ taglib uri="/WEB-INF/pagination.tld" prefix="pagination-tag"%> (provided you drop the tld in the /WEB-INF/)

The figure below shows the pagination tag at work.

Pagination tag at work. Click on thumbnail to view full-size image.

In the loaded page, we can see anchor tags for page numbers 1, 2, 3, 4, and so on. When the user clicks on 2, for example, the start and range request parameters are sent to the search action with values 10 and 20, respectively. Now the search action executes the same query, but with the new limiting values of 10 and 20. So, the new result set is returned to the JSP page, which displays it. The pagination tag generates the new anchors and displays them.

Documentation

Let's talk about the pagination tag parameters. All parameters are runtime expressions:

  • start: This required parameter defines the starting position in the list (e.g., 10, 20, and so on).
  • range: Defines how many results to display at a time, for example, 10; also a required parameter.
  • results: Defines the total number of results that would be returned if we hadn't limited our query. The pagination tag uses the results parameter (which is required) to display the number of pages.
  • styleClass: The pagination tag returns the anchors in an HTML table so you can plug that table within another table or anywhere on the page. styleClass, which is an optional parameter, defines the style for that table. If not specified, a default style is used.
  • action: The pagination tag defines an action parameter to build the anchor tags. The action parameter can be used if the search action doing the extraction redirects (instead of forwards) to a JSP page that displays the results. The pagination tag, by default, tries to figure out the URL for the anchors from the current URI. If the JSP page that displays the results using the pagination tag is reached by redirect from the search action, the pagination tag adds the JSP page's URL to the navigation anchors, instead of the search action's URL. So, in the case of redirects, if you define an action parameter, it will be used to generate the anchor's URL. action is an optional parameter.
  • title: Defines whether to show a title for the anchors on a mouse over. The options are yes or no. Anything other than yes is treated as no. If we choose yes, then the title's output by the pagination tag appears like this: First page, Previous page, Next page, and Go to page 5, and so on. title is an optional parameter.

The reason for setting the start and range parameters for the pagination tag, instead of making the tag read from the request is because the search action could be redirecting to the JSP page instead of forwarding. The pagination tag preserves all the request parameters.

If you are using the Struts framework, your search action should be designed such that you would read the start and range parameters from the request and pass them to the DAO, which executes a limiting query equivalent to the one above and returns the relevant results for the start and range. I have listed a snippet of one such search action below:

// NumberUtils is part of Jakarata-commons-lang. Please download Jakarta commons-lang. 
  public class PaginationAction extends Action {
    public static final String SUCCESS = "success";
      public ActionForward execute(
        ActionMapping mapping,
        ActionForm form,
        HttpServletRequest request,
        HttpServletResponse response) {
        PageDAO pageDAO = DAOFactory.getDAOFactory(Factory.MySQL).getPageDAO();
        int start = NumberUtils.stringToInt(((PaginationForm) form).getStart(), 0);
        int range = NumberUtils.stringToInt(((PaginationForm) form).getRange(), 10);
            
        ((PaginationForm) form).setList(pageDAO.getList(start, range)); // Setting the list
        ((PaginationForm) form).setResults(    //Setting the total number of results
        String.valueOf(pageDAO.getTotalNumberOfResults()));
        return mapping.findForward(SUCCESS);
    }
  }

In the above code snippet, I read the start and range from the ActionForm, which are populated from the request by Struts. Then I pass that information to my DAO, which returns the list of results. The results are again set to my ActionForm and back to the JSP page.

In my JSP page, I display the list of results, and then the start, range, and results parameters are supplied to the pagination tag, which displays the total number of pages, the current page the user is on, and then links for navigating to the next page, previous page, and so on:

  <c:set var="start" value="${paginationForm.start}" />
  <c:set var="range" value="${paginationForm.range}" />
  <c:set var="results" value="${paginationForm.results}" /> // this refers to total number of results
  <jsp:useBean id="start" class="java.lang.String" />
  <jsp:useBean id="range" class="java.lang.String" />
  <jsp:useBean id="results" class="java.lang.String" />
  <pagination-tag:pager start="<%=start%>" range="<%=range%>" results="<%=results%>"/>

Now the DAO can be replaced by a session bean acting as a facade, which in turn delegates the work to an entity bean.

In this article, I have shown one way for limiting the result set for a particular query, but the LIMIT clause works only for MySQL. Different relational databases have different implementations of the LIMIT clause. Refer to the reference manual for the particular database you use. In Oracle, you can achieve LIMIT clause functionality in MySQL like this:

  SELECT * FROM TABLE WHERE {CONDITION} rownum > ? AND rownum < ?

Configuration

To quickly view the pagination tag at work, deploy the ready to run pagination-tag.war file (available in this article's source code) onto your application server (Tomcat 5.0 is a good example).

Step 1

Drop the pagination-tag-x.0.jar into the WEB-INF/lib directory.

Step 2

Make sure the dependency libraries (Jakarta Commons Lang x.0) are in the same directory.

Step 3

For JSP 1.1 containers only, drop the pagination.tld into the WEB-INF/ directory.

Step 4

For JSP 1.1 containers only, drop the pagination.tld into the WEB-INF/ directory:

<taglib>
    <taglib-uri>http://paginationtag.miin.com</taglib-uri>
    <taglib-location>/WEB-INF/pagination.tld</taglib-location>
</taglib>

Final step

Import the tag into each JSP page that uses the pagination tag: <%@ taglib uri="http://paginationtag.miin.com" prefix="pagination-tag"%>

Then use the pagination tag like this: <pagination-tag:pager start="<%=start%>" range="<%=range%>" results="<%=results%>" styleClass="paginationClass"/>

The URI directives must match what you defined in either the web.xml file above or the tlds in the jar file. With JSP 1.2, containers in the jar file are automatically scanned, and you don't need to define any entry in your web.xml file. The prefix identifies the tags in the tag library within the JSP page.

Conclusion

When displaying long lists of results to the user, instead of extracting the long list up front, the pagination tag I present in this article helps the page developer concentrate on displaying only the data available for that request, and not the anchors for browsing the list. The search action developer (in Struts-speak) can give the page developer the list appropriate to that request, and the DAO developer can worry about the limiting query.

Kishore K. Senji is a J2EE architect at Daimler Chrysler. He holds a bachelor's degree from Indian Institute of Technology (IIT), Madras and a master's degree from Clemson University. In his free time, he contributes to the Java community. He has a passion for cricket and soccer too.

Learn more about this topic

Join the discussion
Be the first to comment on this article. Our Commenting Policies