Monday, 17 November 2008

Paging using Display Tags

One problem that crops up over and over again is that of breaking up a long list of items to be displayed into pages. There's a very handy tag library that handles the presentation for you: Display Tags. If you're using Spring and Hibernate, there are a couple of articles that give some detailed code examples of how to link these tags with Hibernate queries: pagingappfuse in GoogleCode, and a blog entry by Ram Gorti.

Using these techniques, it's very easy to create a simple paged and sorted display. The display tags library creates a table with one list item per row, with or without column headers that can be clicked. It generates links to URLs with parameters page (the page number), sort (the field name to sort by), and dir (the sort direction). My project was built using Struts 2, not Spring MVC as in the examples, so I couldn't use them directly, but it was simple enough to adapt. The elements of this solution are:

1. a JSP including the tag library calls to generate the table:

<display:table name="userList" id="element" cellspacing="0"
requestURI="/cms/user/index.html" sort="external">
<display:column property="firstname" title="First Name" sortable="true" sortname="firstname">
... other columns omitted ...
</display:table>


2. a Struts action with page, sort and dir parameters, as described above. It also has a getUserList() method which returns...

3. an implementation of the PaginatedList interface, which populates itself using service layer calls:

public class PagedUserList implements PaginatedList {

private int pageNumber;
private int pageSize;
private String sortCriterion;
private String dir;
private int fullListSize;
private List<User> list;

public PagedUserList(UserService userService, int pageNumber, int pageSize,
String sortCriterion, String dir) {
super();
this.pageNumber = pageNumber;
this.pageSize = pageSize;
this.sortCriterion = sortCriterion;
this.dir = dir;
this.fullListSize = userService.getAllUserCount();
this.list = userService.listAllUsers(pageNumber - 1, pageSize, sortCriterion, dir);
}
... getter methods omitted ....

4. service layer methods which simply delegate to DAO methods;

5. A DAO method using a Hibernate Criteria query to retrieve the appropriate data:

public List<User> listAllUsers(int page, int pageSize, String sort, String dir) {
Criteria criteria = getSession ().createCriteria (User.class);

Order nameOrder = "desc".equalsIgnoreCase(dir) ? Order.desc(sort) : Order.asc (sort);
criteria.addOrder (nameOrder.ignoreCase());
criteria.addOrder (Order.asc ("id"));
criteria.setFirstResult (page * pageSize);
criteria.setMaxResults (pageSize);

return criteria.list ();
}

Of course these methods could be generalized to operate on any class, not just a specific class as in this example, and to add some kind of selection to the query, instead of returning all the data. However, for a quick and easy way to display the data, this worked fine.

The next problem was not simply to select data, but to display it in a different way. Isn't it often the case that once a technique has been well enough understood to be codified into a library, users' expectations have moved on, and the technique isn't relevant any more? If you're happy with data displayed in columns, one item per row, with clickable column headers for sorting, then the display tags library does a fine job. However, I needed to create a different kind of page layout, so a slightly different technique was needed. That's the subject of the next article.

No comments: