(Update : fixed some of the missing images)

One common code pattern that we find ourselves writing time and again is the ability to display tables which are paginated and sortable. Ideally, this is something we should try and be able to re-use throughout our application.

Current Situation

Let’s start by looking at where we are now with the features that come with Seam out of the box. In particular, we will be looking at the dataScroller component and the EntityQuery object.

Data Scroller Problems

Richfaces, included with Seam, offers a data paginator which can be used with a standard JSF dataTable to paginate the source data of the dataTable. It
does this by taking the number of items in the source list and determining how many pages are needed to display the data. The problem with this is that it requires that all objects in the source list be represented as an object, even if it is just a memento or placeholder for the real entity. This is because the dataScroller has no sense of queries or JPA models to fetch the number of items in the list. Obviously, this can be problematic when you have thousands of items in the list. Not only does it take up precious memory resources, but it takes quite a lot of time to load the objects from the database. Even a placeholder or memento requires data from the database which
takes time and resources to iterate through,read and store.

There are also times when we needlessly load the list of objects for no reason other than the user went to a page to look at some unrelated data. Consider an address book where you view a clients details and it contains a table listing the phone calls made to the client. There could be hundreds of records, all of which are loaded because we are using the dataScroller that requires all items to be loaded. After they have all been loaded, and the page is finally displayed to the user, the user only wanted to look at the clients birthdate making the fetching redundant. For this reason, if we are going to load the data, we should do it as quickly as possible since it may not even be looked at and the user will be waiting unnecessarily.

Typically, if we have a thousand records, the chances of the user looking through all of them is low. They may just want to see the last 10 phone calls made to the client or the last time the client was called. In this case, again, we don’t want to load all the objects, we want to bring the page back to the user quickly, and then let them sort by whatever criteria they choose to get to the data they want.

Sorting Data

With the standard dataTable, or with any HTML table, we have no way of sorting the data automatically. We don’t really want to load all the data down to the client and use javascript for the same reasons we don’t want to use a paginator that requires all the data be loaded.

Both our problems, pagination and ordering, stems from the fact that we do not have a standard server side object interface by which these ‘dumb’ html component can go against in order to manipulate the data as necessary.

EntityQuery Problems

The Seam team wrote the EntityQuery component as a generic query object which was also EL aware. It includes attributes for handling where clauses, pagination, and ordering as well as a method for obtaining the number of results in the query. The Seam documentation describes ways of using the EntityQuery to provide pagination using parameters which can get messy. This is especially so when we have multiple queries on a page, for example in our address book, we not only list the most recent calls to a client, but on another tab, we have a list of recent orders for the client.

Another problem with the current entity query is that it cannot be used in PAGE scope, which means that if you want to maintain state from one request to the next, you need to either start a conversation or pass parameters around. Again, this is something we should seek to fix in our solution to avoid having to start conversations unnecessarily or waste time setting up parameters.

In order to achieve our goals, we will define two facelets that can be re-used, one for the sortable column, and the other for the pagination. These facelets provides the mechanism by which the JSF controls interact with the well defined interface on the EntityQuery.

Getting Started

Let’s start by examining our model, our basic query and our initial view html. We have a Person class that has an Id, a first and last name, and a numerical score.

@Entity
@Table(name="PEOPLE")
public class Person {
        
        @Id
        @Column(name="ID")
        private Long id;
        
        @Column(name="FIRST_NAME",length=24)
        private String firstName;

        @Column(name="LAST_NAME",length=24)
        private String lastName;
        
        @Column(name="SCORE")
        private Integer score;
}

Typically, when we are building a list using Seam’s EntityQuery, we would subclass the EntityQuery and override our methods for our particular query, or set them in the constructor. Alternatively, we could set them up in components.xml.

@Name("personQuery")
@Scope(ScopeType.CONVERSATION)
public class PersonQueryBean extends EntityQuery<Person> {

        public PersonQueryBean() {
                setEjbql("select p from Person p");

                setMaxResults(10);
                setFirstResult(0);

        }
}

We can display this query in a JSF page with the following html.

<a:outputPanel id="renderPanel">
        <h:dataTable value="#{personQuery.resultList}" var="v_person"        
                styleClass="dataTable" rowClasses="row1,row2"                
                headerClass="dataTableHeader">
                <h:column>
                        <f:facet name="header">ID</f:facet>
                        <s:link value="#{v_person.id}" view="/personView.xhtml">
                                <f:param name="personId" value="#{v_person.id}" />
                        </s:link>
                </h:column>

                <h:column>
                        <f:facet name="header">First Name</f:facet>
                        <h:outputText value="#{v_person.firstName}" />
                </h:column>

                <h:column>
                        <f:facet name="header">Last Name</f:facet>
                        <h:outputText value="#{v_person.lastName}" />
                </h:column>

                <h:column>
                        <f:facet name="header">Score</f:facet>
                        <h:outputText value="#{v_person.score}" />
                </h:column>

        </h:dataTable>
</a:outputPanel>

This gives us a list of people, where the maximum number of results is still constrained by the EntityQuery to 10. This is how things are out of the box with Seam. The outputPanel tag is used by JSF to isolate updates to that panel only. This gives the user a smoother experience as they browse the results.

This EntityQuery will become the standard interface with which our JSF will interact in order to change the order of the results, and the results page that is shown.

Improving Things

Let’s start by extending the EntityQuery. We need to abstract the original orderBy attribute of the EntityQuery in order to control and query our ordering status. The original orderBy attribute works by accepting the actual field names and the order direction. This can be cumbersome to work with, especially when trying to determine the current ordering and direction in the view. This is especially the case when your JSF page needs to work out whether the order is ascending or descending when the order is set to p.firstName DESC, p.lastName DESC. Also, with the current orderBy attribute, there is a risk of SQL injection since typically, the page is setting the value directly. Decoupling the view from the ordering logic in this case is a good
practice and also improves security.

By having two values, an order key and a direction attribute, on the EntityQuery we can easily determine the active order and the direction. These order key values can be mapped to actual model fields to prevent injection and also makes it easier for order by clauses that use multiple fields, as well as decoupling the
ordering logic from the view.

public class ExtendedEntityQuery&lt;E&gt; extends EntityQuery&lt;E&gt; {

        private String orderKey;
        private boolean ascending;
        private Map&lt;String, String&gt; orderKeyFieldMap = new HashMap&lt;String, String&gt;();

        public String getOrderKey() {
                return this.orderKey;
        }

        public void setOrderKey(String orderKey) {
                // if this value is already the order key, we are just inverting the
                // direction
                if (orderKey != null && orderKey.equals(this.orderKey)) {
                        ascending = !ascending;
                } else {
                        // else we are setting a new order field, so reset the direction
                        this.orderKey = orderKey;
                        ascending = true;
                }
                // invalidate the results
                refresh();
        }

        public void setAscending(boolean ascending) {
                this.ascending = ascending;
                // invalidate the results after changing the order direction
                refresh();
        }

        public boolean getAscending() {
                return this.ascending;
        }

        @Override
        public String getOrder() {	 
                // get the order fields from the key
                String order = orderKeyFieldMap.get(orderKey);
                // if not set, then there is no ordering
                if (order == null) {
                        return null;
                }
                // parse out fields and add order
                String[] fields = order.split(",");
                order = "";
                // concatenate the fields with the direction, put commas in between
                for (String field : fields) {
                        if (order.length() != 0) {
                                order = order + ", ";
                        }
                        order = order + field + (ascending ? " ASC " : " DESC ");
                }

                return order;
        }

        public Map&lt;String, String&gt; getOrderKeyFieldMap() {
                return orderKeyFieldMap;
        }

        public void setOrderKeyFieldMap(Map&lt;String, String&gt; orderKeyFieldMap) {
                this.orderKeyFieldMap = orderKeyFieldMap;
        }


        /**
         * Calculates and returns the current page number based on the first result
         * value
         * 
         * @return The current page number
         */
        public int getActivePage() {
                Integer fr = getFirstResult();
                Integer mr = getMaxResults();
                if (fr != null && mr != null) {
                        return 1 + (fr / mr);
                }
                return 0;
        }

}

This is essentially the bulk of our code since it builds on the existing EntityQuery class which implements the meat of our query. We added two new attributes ascending and orderKey. The ascending attribute indicates which direction the index is ordered in, and the orderKey is used to indicate how we want to order the results. We use the orderKey to lookup the actual fields to order by using the orderKeyFieldMap. When initializing the query, we add the key/field values to the map to use for lookups. We’ll modify our PersonQuery to extend from our ExtendedEntityQuery, and we add the key/field pairs to the orderKeyFieldMap.

@Name("personQuery")
@Scope(ScopeType.CONVERSATION)
public class PersonQueryBean extends ExtendedEntityQuery&lt;Person&gt; {

        public PersonQueryBean() {
                setEjbql("select p from Person p");

                setMaxResults(10);
                setFirstResult(0);

                getOrderKeyFieldMap().put("id", "p.id");
                getOrderKeyFieldMap().put("firstname", "p.firstName");
                getOrderKeyFieldMap().put("lastname", "p.lastName");
                getOrderKeyFieldMap().put("name", "p.lastName,p.firstName");		
                getOrderKeyFieldMap().put("score", "p.score");
        }
        
}

This takes care of handling the SQL injection problem, lets us isolate the handling of field ordering in one place and it makes it easier to deal with ordering by multiple fields. Note that if you want to define your components in components.xml then you can specify this map in the definition. Our query logic will insert the ASC and DESC where needed depending on the value of the ascending flag.

Ordering the results

To let the user change the ordering at run time, we need to provide a link which sets the orderKey. We can easily do this by changing the header to contain a link to set the orderKey. Here we change it for the last name column.

<h:column>
        <f:facet name="header">
                <a:commandLink value="Last Name"
                        action="#{personQuery.setOrderKey('lastname')}"
                        reRender="renderPanel" />
        </f:facet>
        <h:outputText value="#{v_person.lastName}" />
</h:column>

If you run this, and click the link, you will notice that the results will order by the field. Click the link again and…nothing happens.

This is because we haven’t introduced state into the equation. Each time we click the link, we are posting back to a new instance of the PersonQueryBean, not the same one. We could start a conversation which will make our query bean stateful and it will remember the current order key. Alternatively, the simplest way is to put two hidden fields on the page to contain the orderKey and the ascending values. As we’ll see later, we will move these out of our page so we don’t have to add them for each table. For now though, just add two hidden inputs.

<h:inputHidden value="#{personQuery.orderKey}" />
<h:inputHidden value="#{personQuery.ascending}" />

Now, we could have left the ordering information in one attribute and used values like "lastname ASC, firstname ASC" and "lastname DESC, firstname DESC" to test and set the ordering without needing two fields and combined it with a list of acceptable fields to prevent SQL injection. However, I prefer to abstract the clause since it decouples it from the view, typos are less of an issue, and if a field name or your model structure changes (i.e. person names are spun off into a separate object), we can change it in one place in the query bean.

Now let us add an icon to the column header so we can see which column we are ordering by and which direction we are ordering the column by. We can do this by evaluating the orderKey and ascending values.

<h:column>
        <f:facet name="header">
                <s:span>
                        <h:commandLink action="#{personQuery.setOrderKey('score')}"
                                value="Score" reRender="#{p_reRender}" />

                        <s:fragment rendered="#{personQuery.orderKey == 'score'}">
                                <h:graphicImage value="/img/sort_asc.gif"
                                        rendered="#{personQuery.ascending}" />
                                <h:graphicImage value="/img/sort_desc.gif"
                                        rendered="#{!personQuery.ascending}" />
                        </s:fragment>

                        <s:fragment rendered="#{ not (personQuery.orderKey == 'score')}">
                                <rich:spacer width="19" height="9" />
                        </s:fragment>
                </s:span>
        </f:facet>
        <h:outputText value="#{v_person.score}" />
</h:column>

Our example is getting a little unwieldy now with 15 lines of html to add sorting to a column. What we’ll do is wrap this column definition in a reusable facelet. Let’s call it sortableColumn.xhtml. Note that the caption, the query bean, the order key and AJAX render target have all been parameterized.

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
        xmlns:ui="http://java.sun.com/jsf/facelets"
        xmlns:h="http://java.sun.com/jsf/html"
        xmlns:f="http://java.sun.com/jsf/core"
        xmlns:s="http://jboss.com/products/seam/taglib"
        xmlns:rich="http://richfaces.org/rich"
        xmlns:a="http://richfaces.org/a4j">
        <f:facet name="header">

                <s:span>
                        <a:commandLink action="#{p_queryBean.setOrderKey(p_field)}"
                                value="#{p_caption}" reRender="#{p_reRender}" />

                        <s:fragment rendered="#{p_queryBean.orderKey == p_field}">
                                <h:graphicImage value="/img/sort_asc.gif"
                                        rendered="#{p_queryBean.ascending}" />
                                <h:graphicImage value="/img/sort_desc.gif"
                                        rendered="#{!p_queryBean.ascending}" />
                        </s:fragment>

                        <s:fragment rendered="#{ not (p_queryBean.orderKey == p_field)}">
                                <rich:spacer width="19" height="9" />
                        </s:fragment>
                </s:span>
        </f:facet>

</ui:composition>

We can use this facelet to define every column that we want to apply ordering to. Here is the html for the Id column.

        <h:column>
                <ui:include src="sortableHeader.xhtml">
                        <ui:param name="p_queryBean" value="#{personQuery}" />
                        <ui:param name="p_caption" value="ID" />
                        <ui:param name="p_field" value="id" />
                        <ui:param name="p_reRender" value="renderPanel" />
                </ui:include>
                <h:outputText value="#{v_person.id}" />
        </h:column>

If we apply this for each column, we end up with the following person tables. I also added a name column which is last and first name combined. Sorting by last name and then by name, we can see that the ordering is different. This is because the name column orders by last and then first name whereas the last name column orders by the last name
only (consider the order of Brian,Frank, Jerry and Harrison Ford)

Paginating the Results

We will use the same type of technique to handle pagination. We will create a pagination facelet which will call the methods on the query bean that it is passed and then trigger an update to the page. Our pagination is fairly simple and the facelet code is included below.

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
        xmlns:ui="http://java.sun.com/jsf/facelets"
        xmlns:h="http://java.sun.com/jsf/html"
        xmlns:f="http://java.sun.com/jsf/core"
        xmlns:s="http://jboss.com/products/seam/taglib"
        xmlns:rich="http://richfaces.org/rich"
        xmlns:a="http://richfaces.org/a4j">

        <h:panelGrid columns="5" styleClass="paginator" cellpadding="0"
                cellspacing="0"
                columnClasses="pagSmall,pagSmall,pagBig,pagSmall,pagSmall"
                rendered="#{p_queryBean.nextExists || p_queryBean.previousExists}">

                <a:commandLink action="#{p_queryBean.first}" value=" First"
                        styleClass="paginatorLink" reRender="#{p_reRender}" />

                <a:commandLink action="#{p_queryBean.previous}"
                        reRender="#{p_reRender}" value="Previous" styleClass="paginatorLink" />

                <h:outputText value="Page #{p_queryBean.activePage}" />
                <a:commandLink action="#{p_queryBean.next}" value="Next"
                        disabled="#{!p_queryBean.nextExists}" reRender="#{p_reRender}"
                        styleClass="paginatorLink" />


                <a:commandLink action="#{p_queryBean.last}" reRender="#{p_reRender}"
                        style="align : right" disabled="#{!p_queryBean.nextExists}"
                        value="Last" styleClass="paginatorLink" />

        </h:panelGrid>

        <h:inputHidden value="#{p_queryBean.firstResult}" />
        <h:inputHidden value="#{p_queryBean.orderKey}" />
        <h:inputHidden value="#{p_queryBean.ascending}" />

</ui:composition>

If you look at the end of our pagination facelet, we have included the hidden fields to handle ordering, and a new one indicating the first result value. Again, this is so we don’t have to manage the view state on the server, it gets passed back from the client. We don’t have to set up parameters nor worry about multiple grids on a page. We have taken the two other hidden values and put them into the pagination facelet which will get included per table or query.

Our code is simple, we have links for first,next, previous and last, and only enable them if the query is able to move in those directions. The links call the appropriate methods on the entity query. We also make use of the AJAX reRender attribute so we only update the table and not the whole page.

So finally, we have a page where we can easily introduce sorting and pagination onto a standard JSF data table. Listed below is the complete page listing. As you can see, we haven’t really added that much JSF to end up with a table which automatically handles sorting and pagination and AJAX updates to the table.

<h:form>

        <a:outputPanel id="renderPanel">
                <h:dataTable value="#{personQuery.resultList}" var="v_person"
                        styleClass="dataTable" rowClasses="row1,row2"
                        headerClass="dataTableHeader">
                        <h:column>
                                <ui:include src="sortableHeader.xhtml">
                                        <ui:param name="p_queryBean" value="#{personQuery}" />
                                        <ui:param name="p_caption" value="ID" />
                                        <ui:param name="p_field" value="id" />
                                        <ui:param name="p_reRender" value="renderPanel" />
                                </ui:include>
                                <h:outputText value="#{v_person.id}" />
                        </h:column>

                        <h:column>
                                <ui:include src="sortableHeader.xhtml">
                                        <ui:param name="p_queryBean" value="#{personQuery}" />
                                        <ui:param name="p_caption" value="First Name" />
                                        <ui:param name="p_field" value="firstname" />
                                        <ui:param name="p_reRender" value="renderPanel" />
                                </ui:include>

                                <h:outputText value="#{v_person.firstName}" />
                        </h:column>

                        <h:column>
                                <ui:include src="sortableHeader.xhtml">
                                        <ui:param name="p_queryBean" value="#{personQuery}" />
                                        <ui:param name="p_caption" value="Last Name" />
                                        <ui:param name="p_field" value="lastname" />
                                        <ui:param name="p_reRender" value="renderPanel" />
                                </ui:include>

                                <h:outputText value="#{v_person.lastName}" />
                        </h:column>

                        <h:column>
                                <ui:include src="sortableHeader.xhtml">
                                        <ui:param name="p_queryBean" value="#{personQuery}" />
                                        <ui:param name="p_caption" value="Name" />
                                        <ui:param name="p_field" value="name" />
                                        <ui:param name="p_reRender" value="renderPanel" />
                                </ui:include>
                                <h:outputText value="#{v_person.name}" />
                        </h:column>

                        <h:column>
                                <ui:include src="sortableHeader.xhtml">
                                        <ui:param name="p_queryBean" value="#{personQuery}" />
                                        <ui:param name="p_caption" value="Score" />
                                        <ui:param name="p_field" value="score" />
                                        <ui:param name="p_reRender" value="renderPanel" />
                                </ui:include>

                                <h:outputText value="#{v_person.score}" />
                        </h:column>

                </h:dataTable>

                <ui:include src="paginator.xhtml">
                        <ui:param name="p_queryBean" value="#{personQuery}" />
                        <ui:param name="p_reRender" value="renderPanel" />
                </ui:include>

        </a:outputPanel>

</h:form>

This about wraps it up. This code resolves all our problems. We have a mechanism by which we can create sortable tables with pagination that return results to the user quickly without writing any additional code to implement this. The only code we need to write is inserting the orderKey / Field mapping values. By adding the hidden fields into the paginator, we make our tables stateless and remove the need to start a conversation or set up parameters for the query values for each table. We have decoupled setting the order type with the actual fields used to achieve it, and made it more secure by removing SQL injection risks. If that were all not enough we have AJAX-ified our table for a better user experience.

If you want to use this code in your own applications, the pieces you will need are the ExtendedEntityQuery class, and the 2 pieces of xhtml in sortableHeader.xhtml and pagiantor.xhtml. Simply extend your entity queries from the ExtendedEntityQuery and they can be used by the sortable columns.