Codeless Ajax Ordered and Paginated Tables in Seam

(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.

22 thoughts on “Codeless Ajax Ordered and Paginated Tables in Seam

  1. Thanks for the insightful article. You have obviously thought this through more than the Seam team. However, I find this to still be a bit wordy. Is there any chance that you could make this more like the tag, which takes a sortBy attribute?

    PS. I spent some time extending the EntityQuery class to build a custom DataModel that works with JSF and the and . It appears to work, but seems to be making to many calls to the database. I have only been using seem for a few weeks, and have never used AJAX, JSF, etc. I am willing to share the code, if your interested.

  2. The only way to make it so simple is to put the code somewhere else which would mean writing a new component which contains all the other pieces, which for a data table is quite a task. However, it is something which may be worth a try at some point.

    Regarding your code, shoot some code over (contact(at)andygibson.net) and I’ll take a look. What’s the goal of the extensions and the custom Data Model?

  3. Alexander R?hl

    Hi Andy,

    nice article, I stumpled over the same problems you mentioned.

    But still, I don’t understand one thing:
    I used the ExtendedEntityQuery class and the two facelets and applied them to my entity and session class and my xhtml page.
    But when I put a breakpoint in the session class’ constructor, it will be reached (even twice!?) each time I click something.
    Is that how it should be?

    Because this gives me trouble with another feature – I like to extend your paging mechanism with the option of letting the user set the page size with a combo box (e.g. 5, 10, 15).
    It works fine if I bind that to the maxresults attribute, but once I change pages it will be overwritten by the (initial) setting in the constructor. But if I don’t set it in the constructor it won’t work at all.

    I hope you can help me on that.

    Thanks in advance,
    Alex

  4. Hi Alexander,

    Regarding the reason you have the object created per call, it sounds like it is because the page is not in a long running conversation. The solution will work as-is, you don’t need a long running conversation since the state is stored in the paginator facelet in the hidden text boxes.
    Regarding your problem with the page size, since you don’t have along running conversation, you need to add a hidden edit box to hold the state for the page size just as it does for the first results and ordering etc.. This way, even without a long running conversation, the page will work.

    Set the value in your constructor, and when the page first loads, this value is used. If the user posts back with a new value, the bean is re-created (assuming you are not using a long running conversation) and then JSF updates the values on the bean (including your page size value) as part of the JSF lifecycle.

    Cheers,

    Andy Gibson

  5. Alexander R?hl

    Hi Andy,

    I stumpled again over the topic while reading Dan Allen’s book “Seam in Action”. In chapter 3.3.4 “Page Parameters/The search form paradox” he points out that “..use of hidden form elements..becomes a tangled mess” and “Page parameters in Seam make this situation trivial.”
    So why is it in your opinion still necessary to use hidden fields to store the sorting state instead of taking advantage of the page parameters?

    Cheers,
    Alex

  6. Pingback: Andy Gibson’s Blog » Blog Archives » In defence of hidden fields with Seam

  7. Hi Alexander,

    Good question, and one I answered with another blog post as my response was getting quite lengthy. Hope it clears up the issue for you,

    Cheers,

    Andy

  8. Guillaume Jeudy

    Hi Andy,

    Nice article, I wish I would have stumbled upon this article before I implemented pagination. Nevertheless my solution is roughly equivalent to yours except that I use a long running conversation so dont need any hidden field tricks. I also created my own custom facelets taglib and created a custom tag sortLink which can be used likeso:

    no more ui:include wordy XML.

    This article gives info about how to create custom tags with facelets (no java code needed):

    http://www.ibm.com/developerworks/java/library/j-facelets2.html

  9. Guillaume Jeudy Guillaume Jeudy

    my XML snippet doesnt show in my previous comment here it is:

  10. Thanks Guillaume, Wrapping it in a custom tag would make it more easier to use and your link looks like a useful article. Perhaps at some point I’ll take a look and write another post about the process when I get some time,

    Cheers,

    Andy

  11. Hi Andy,

    Thanks for the excellent article. It helped me a lot. In my code i have the pagination to show page numbers , i would appreciate your help and guidance on how to make the click on page numbers load the AJAX-ified page using reusable code.

    Thanks,
    Mani

  12. Hi Mani,

    Personally, I’ve never really thoroughly implemented page numbers using this method as it requires a record count of the results. However, I would just add a gotoPage method on the entity query and pass in the value from the jsf page link. In the method, I would have something like :


    public void gotoPage(int pageNum) {
    firstResult = pageNum * pageSize;
    refresh();
    }

    I’d add a method to get a list of page numbers, put a ui:repeat on there to access the values and use the var as a parameter to the gotoPage method in the paginator:


    <ui:repeat value="#{p_queryBean.getPageList}" var="v_page">
    <a:commandLink action="#{p_queryBean.gotoPage(v_page)}" value="#{v_page}"/>
    </ui:repeat>

    You can put this in the paginator to get re-use each time by adding the getPageList() method on the entity bean.

    By using a pageList method returning a value, you can just return 1 through N, or 1 through 9 and then 15 to 19 if you have a lot of pages. You can also not render the page numbers if your pageList returns null or is empty. Also, if you change your page list strategy down the road, you can change it for all queries by implementing it in the extended entity query.

    Hope this helps,

    Cheers,

    Andy Gibson

  13. VK, The source you refer to doesn’t use a Seam type of query, the code works off a bare bones hibernate query which is different.

    In theory, you could override the getEntityManager() method in the EntityQuery or the ExtendedEntityQuery I use and return the FullTextEntityManager they use in your reference. However, the style of querying is completely different so I’m not sure they share enough common code to work well.

    To be honest, though, the pagination code isn’t that difficult, you could just create a full text search query which takes a class as a parameter and re-write the code for firstResult, count, first, next, last, etc in no time.

    The EntityQuery objects do subclass some Query objects which you can look and see how good the hierarchy they implemented is. I don’t think it is very open to extension though. I wrote my own query hierarchy that implements the basic pagination stuff and enhanced ordering so you could get a list of results from anything (file list etc) and then extended them for JPA, Hibernate queries, and also to allow for better handling of EL expressions in restrictions.

    The code isn’t that tricky, especially since the EntityQuery pretty much describes what the query needs to do.

    Cheers,

    Andy

  14. Do you have .css for this dataTable?

    Thanks for this tutorial

  15. THanks for this tutorial, i’m finally getting my pagination and ordering works!

    I have a question: How do I set a class extending the ExtendedEntityQuery in components.xml. Is that the “class” attribute? And how can i ovverride the constructor for settings lines like getOrderKeyFieldMap().put(“id”, “p.id”) in the components.xml? Thanks

  16. In a nutshell, you can’t. The components.xml definitions for the Seam Framework used a special syntax. Typically, I override the ExtendedEntityQuery, give it a name, put the order key setup in the constructor and I’m done. Plus, you can add other related bits of information to the class.

    Cheers,

    Andy Gibson

  17. Amazing – Thanks a lot for shedding light on this. I am amazed that richfaces and Seam haven’t published something similar. Thanks a lot.

  18. Thanks so much for this article! It’s been EXTREMELY helpful. I’ve implemented the code in my application with no problem. The ‘Next’ button works just fine, but for whatever reason my ‘Previous’ button doesn’t. I added a few debugging statements and can see that ‘previousExists == true’, however nothing happens when I click the Previous button. I don’t even see the call getting back to my EntityQuery object. Have you ever seen something like this?

    1. Do you have disabled=”#{not bean.previousExists}” or something like that? That will break it. I found a bug where when the model is rebuilt, the first page is 0 and previousexists = false and the component is disabled. It doesn’t matter that when the values are applied you are on page 10 or whatever. I already filed a bug for it.