Spring & JSF integration: Pagination

When working with large datasets you often need to present data in a paged format. Pagination is an interesting problem because it tends to cut across all layers of your application, from the view tier though application services down to the raw calls to your database.
When it comes to fetching paged data there are some pretty good solutions available. If you are using JPA then you are probably familiar with the setFirstResult() and setMaxResult() methods available on javax.persistence.Query. Even better is the Spring Data JPA project that provides org.springframework.data.domain.Pageable and org.springframework.data.domain.Page interfaces for use directly in your repositories.
With JSF there are also some well documented methods of displaying and fetching paged data. The exact solution will depend on the component suite that you are using but most of them are based around creating a custom javax.faces.model.DataModel implementation. For example MyFaces have suggestions on their wiki, RichFaces have blogged about the problem and PrimeFaces provide a Lazy Loading DataTable.
Recently I have been trying to develop something to ease the burden of the JSF developer and remove the need to create the custom DataModels and the backing beans that expose them. The basic idea is that a JSF component will create a lazy loading DataModel on your behalf using EL expressions to fetch the data as it is need.
Here is an example:
<s:pagedData 
  var="myDataModel" 
  value="#{userRepository.findByLastName(
    backingBean.lastName, pageRequest.offset, pageRequest.pageSize)}"
  pageSize="20" />
This will create a myDataModel variable that will fetch 20 rows of data at a time by calling userRepository.findByLastName(). The EL expression will be called several time as the DataModel is scrolled.
(I am assuming that you are using EL 2.2, if you an older server such as Tomcat 6 you may need to install an updated el-impl.jar.)
Each time the EL expression is called a pageRequest variable is made available. This variable provides access the following context information that may be required when fetching a page of data:
pageNumberThe page number to display
pageSizeThe page size requested
offsetThe offset (first result)
sortColumnThe column used to sort data
sortAscendingIf the sort is in ascending or descending order
filtersA map of filters to apply
One problem with the DataModel created in the above example is that the total number of rows is unknown. To get this information we need to provide an additional expression:
<s:pagedData
  value="#{userRepository.findByLastName(
    backingBean.lastName,pageRequest.offset, pageRequest.pageSize)}"
  rowCount="#{userRepository.countByLastName(backingBean.lastName)}" />
The example above has also dropped the var and pageSize attributes, this will use a default page size of 10 and use a variable name of pagedData.
If you have used Spring Data you may have noticed how similar the pageRequest variable is to the org.springframework.data.domain.Pageable interface. In fact, as long as Spring Data is on your classpath, pageRequest can be cast to Pageable. Furthermore the component understands the org.springframework.data.domain.Page object so you no longer need the rowCount expression.
Here is an example that calls a spring data repository and presents data using MyFaces Tomahawk components. This example also allows you to sort the data by clicking on a column header:
<s:pagedData value="#{userRepository.findByLastName(backingBean.lastName, pageRequest)}" />
<t:dataTable value="#{pagedData}" rows="#{pagedData.pageSize}"
    sortColumn="#{pagedData.sortColumn}" sortAscending="#{pagedData.sortAscending}" var="user">
  <t:column>
    <f:facet name="header">
      <t:commandSortHeader columnName="name">
        <h:outputText value="User Name" />
      </t:commandSortHeader>
    </f:facet>
    <h:outputText value="#{user.name}" />
  </t:column>
  <f:facet name="footer">
    <t:dataScroller paginator="true" paginatorMaxPages="9" />
  </f:facet>
</t:dataTable>
One final trick up our sleeves is to ensure that when using PrimeFaces the created DataModel is compatible with org.primefaces.model.LazyDataModel. Here the same example as above but using PrimeFaces components:
<s:pagedData value="#{userRepository.findByLastName(backingBean.lastName, pageRequest)}" />
<p:dataTable value="#{pagedData}" rows="#{pagedData.pageSize}" 
     paginator="true" lazy="true" var="user">
  <p:column headerText="User Name" sortBy="#{user.name}">
    <h:outputText value="#{user.name}" />
  </p:column>
</p:dataTable>
If you want to take a look at any of the code for this it is available on GitHub (look at the org.springframework.springfaces.page.ui and org.springframework.springfaces.model packages). I also have a basic sample application showing page mark-up. As always this code is a moving target so you might encounter some problems running the demos.

Do you want to know how to develop your skillset to become a Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

JPA Mini Book

Learn how to leverage the power of JPA in order to create robust and flexible Java applications. With this Mini Book, you will get introduced to JPA and smoothly transition to more advanced concepts.

JVM Troubleshooting Guide

The Java virtual machine is really the foundation of any Java EE platform. Learn how to master it with this advanced guide!

Given email address is already subscribed, thank you!
Oops. Something went wrong. Please try again later.
Please provide a valid email address.
Thank you, your sign-up request was successful! Please check your e-mail inbox.
Please complete the CAPTCHA.
Please fill in the required fields.

Leave a Reply


eight + = 11



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.
Do you want to know how to develop your skillset and become a ...
Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

Get ready to Rock!
You can download the complementary eBooks using the links below:
Close