Home > Java > PrimeFaces Lazy Loading DataTable for JSF2

PrimeFaces Lazy Loading DataTable for JSF2

Summary
This post discusses the benefits of paginating and lazy-loading large datasets that are displayed to users as well as the benefits of leveraging 3rd party JSF2 UI component suites. I’ve developed a simple application to demonstrate the concept which you can download and run. It is a maven2 project which uses PrimeFaces, Spring Framework, Hibernate/JPA2, and Ehcache.

Background
Often times your application must display a large data set to its users so they can operate on it. The users might need to create, update, and delete rows in this data set. The problem is these data sets are too large to display on a single web page in a friendly manner. If you tried to show all the data at once, the scroll bar on the web page would be longer than a freight train and the page would feel just as heavy.


To make these large data sets manageable, pagination is often used. Pagination involves choosing and displaying a small number of rows of the total number of rows, such as the first 20 rows of 1000 rows, and providing controls to view the next or previous page of rows. The screenshot below from the demo application illustrates this concept.

While a paginated data table component such as the one shown makes the data easier to manage, it is a non-trivial task to implement this type of component from scratch and make it reusable. For example, the user can often change the page size, which then affects the number of pages calculated from the data set. Also, the navigation buttons need to be made active or inactive based on where the user is in the dataset. Filtering and sorting provide additional challenges. The time spent developing and maintaining in-house User Interface (UI) components takes away time that could be spent solving business problems. This is not a good thing for aggressive project timelines.

Thanks to Java Server Faces (JSF), the Java community has access to a number of UI component suites to absolve the need to develop in-house UI components. A few UI component suites for JSF2 are JBoss RichFaces, Icesoft ICEFaces, and PrimeFaces. As the title suggests, I’ll be focusing on the PrimeFaces DataTable component in this post.

Solution
PrimeFaces is well-documented, easy to use, and provides over 100 reusable UI components that have the look, feel, and AJAX behaviour you would expect to find in today’s modern web applications.
Some example components include the Calendar, TabView, PickList, and Slider.

Focusing on the DataTable component, you can see in the reference documentation that there are hooks to provide data to the component (for instance, column filters) and hooks to process requests from the component (lazy loading).

Now that you have a friendly way to present the data to the users by using the DataTable component, you must decide how to retrieve it from the database. One approach is to execute a query one time for each user to load all the data and then retain it in the user’s session (or have it part of their view scoped bean). This would make moving forward and backward in datatable component fast but it is a lot of data to retain per user resulting in high memory usage per user. This would not be good if your application has many concurrent users. Besides memory issues, the retained data would quickly become stale and your application would be showing outdated values to its users.

Another approach is to fetch all the data from the database each time a user changes pages in the datatable component. Since the data is not stored per user, the data shown would be fresher than the previous approach and the memory use would be much lower. The problem is retrieving all the data from the database and only using a tiny part of it when navigating pages would make the application feel slow and unresponsive.

The middle ground between these approaches is called Lazy Loading. In this approach, the application retrieves from the database only the tiny set of data the user wants to view for the current page out of the entire possible dataset. For example, if the user wants to view the first 20 rows out of a 1000 row dataset, only the first 20 rows are retrieved from the database. When the user changes to page2, only rows 21-40 are retrieved and displayed to the user. The Lazy Loading approach has good performance, low memory use per user, and good freshness of data.

To use lazy loading with the PrimeFaces DataTable component, you must provide the component with a LazyDataModel object such as

private LazyDataModel<Employee> lazyModel;

This model is used to display Employee objects in the data table of the demo application. Your LazyDataModel class must override the following method and implement it to retrieve the data appropriately.

public List<Employee> load(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String,String> filters)

Reviewing the method parameters

  • first: is the first row of the dataset being asked for in this request
  • pageSize: is the number of rows to retrieve
  • sortField: is the field name that should be used to sort the data
  • sortOrder: is how the data specified by sortField should be ordered – ascending order or descending order
  • filters: (name,value) pairs of all the column filters chosen by the user. For example
    {office.city=Boston, jobTitle=Sales Champion}

If your application does not provide column filtering or column ordering, your implementation of the load method can ignore those parameters. Here is a simple implementation of this situation:

@Override
public List<Employee> load(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String,String> filters) {
  
List<Employee> empList = classicModelsService.findEmployees(first, first + pageSize);

//total number of rows for the query when not using pagination
this.setRowCount(classicModelsService.getEmployeeCount());
return empList;
}

Implementing this load method is very easy if you are using the Java Persistence API. My demo application uses JPA2/Hibernate 3.6.10. A simple implementation without filtering or ordering is shown below

public List<Employee> findRange(int start, int end) {
TypedQuery<Employee> query = entityManager.createNamedQuery("Employee.findAll", Employee.class);
query.setMaxResults(end - start);
query.setFirstResult(start);
return query.getResultList();
}

The beauty of JPA and the setMaxResults/setFirstResult methods is that it takes care of the database vendor specific syntax to paginate your database query. For MySQL5, it uses the limit [offset, number of rows] syntax.
For example,
SELECT * FROM Employees WHERE officeCode = 1 LIMIT 0,10
only retrieves the first 10 rows of the full result set

SELECT * FROM Employees WHERE officeCode = 1 LIMIT 20,10
only retrieves rows 21 through 30 of the full result set

The Java Persistence Query Language, JPQL, is great when you have a fixed query string with or without named parameters. In this case, you can create a named query. This is the situation faced when the only feature of the PrimeFaces lazy loading DataTable component you are using is pagination because user input is not factored into crafting the query string. An example of a named query with JPQL to get all the rows from the Employee table is

@NamedQuery(name = "Employee.findAll", query = "SELECT e FROM Employee e")

However, once you start using the DataTable column filters to limit the rows returned, you understand that multiple column filters can optionally be applied at the same time while also sorting by a column. The complexity increases as you add more column filters because it creates more combinations of possible query strings. This quickly becomes a sticky situation using JPQL because the query must be composed dynamically.

The dynamic nature of the query statement when filters and sorting are taken into consideration makes this situation a good candidate for the JPA2 Criteria API. The Criteria API allows you to programmatically compose type safe queries. Here is a simple example of the Criteria API which returns Employee objects sorted by their email address

	CriteriaBuilder cb = entityManager.getCriteriaBuilder();
	CriteriaQuery<Employee> cq = cb.createQuery(Employee.class);
	Root<Employee> emp = cq.from(Employee.class);
	cq.select(emp);
	cq.orderBy(cb.desc(emp.get(Employee_.email)));
	TypedQuery<Employee> q = entityManager.createQuery(cq);
	q.setMaxResults(end - start);
	q.setFirstResult(start);
	return q.getResultList();

At first glance, the Criteria API may seem overly complex compared to JPQL but composing queries moves along quickly using your IDEs autocompletion. It also shines when you are using conditionals to optionally include parts of the query to create the final statement.

You can view the implementation for sorting and filtering in my demo application source code.

An example native SQL query generated from the Criteria API for sorting, filtering, and paginating is

select employee0_.employeeNumber as employee1_0_, employee0_.email as email0_, employee0_.extension as extension0_, employee0_.firstName as firstName0_, employee0_.jobTitle as jobTitle0_, employee0_.lastName as lastName0_, employee0_.officeCode as officeCode0_, employee0_.reportsTo as reportsTo0_, employee0_.version as version0_ 
from Employees employee0_ cross join Offices office1_ 
where employee0_.officeCode=office1_.officeCode and employee0_.jobTitle=? and office1_.city=? 
order by employee0_.firstName asc limit ? 

The Criteria API makes heavy use of Metamodel classes, which are auto-generated from each of your entity classes. Employee_ (note the trailing underscore in the name) is the Metamodel class for Employee. These classes can be auto-generated by Maven and/or your IDE. Details on how to get this up and running with maven2 and Hibernate can be found in the hibernate docs for jpamodelgen or in my demo application by viewing the POM file.

A good article on the Criteria API, its motivation, and its benefits can be found on the IBM developerWorks site.

Demo
This simple application demonstrates the PrimeFaces DataTable component and how it is used to lazy load data.

The full sourcecode can be downloaded from SVN with this command or by using your favorite IDE.

svn checkout http://hrycan-blog.googlecode.com/svn/trunk/prime

It is a Maven2 project which uses Java Server Faces2/PrimeFaces 3.1.1 for the presentation layer, Spring 3.0.6 for the business layer, JPA2/Hibernate3.6/Ehcache for the data layer and MySQL 5 for the database. Once you’ve downloaded it and configured your database, to run it with jetty you simply execute the command

mvn jetty:run

The database used in the project is the sample database from the Eclipse BIRT project. BIRT is an Eclipse-based open source reporting system. The schema is for Classic Models, a retailer of scale models of classic cars.

My goal was to keep changes to it minimal but a few are required – adding a version column for JPA optimistic concurrency control, a table for primary keys, and a few foreign key constraints. Instructions are included in the project in the db folder.

There is also a load script to insert additional employees into the table which brings the total to roughly 1400 employee rows. The names used to generate the rows were obtained from here.

Integration between Spring and JSF is done using Spring’s SpringBeanFacesELResolver configured in faces-config.xml. This enables injection of the spring service layer (ClassicModelsService) into the JSF ViewScoped Managed bean.

The spring beans are configured using annotations with the service layer executing the methods on the JPA DAOs. The service layer methods are made transactional using the @Transactional annotation along with JpaTransactionManager in application.xml

Spring and Hibernate/JPA are configured using LocalContainerEntityManagerFactoryBean along with a persistence.xml and the EntityManager is injected using the @PersistenceContext annotation.

Regarding the Entities, primary keys are generated using a configured table generator via the @TableGenerator annotation. Ehcache and JPA optimistic concurrency control will be explored in a later blog post but their annotations are on the Employee and Office entities and both are enabled in the application.

Moving to the actual UI of the application, clicking on the ‘Show All Employees’ link in the application shows the PrimeFaces DataTable component displaying the contents of the Employees database table as well as a form below it to create/edit employees.


The data set can be sorted by last name, first name, email, job title, and office by clicking on the respective column. To filter the data by job title or office, select the appropriate value from the DropList in the column header.

The DataTable itself has buttons on the bottom to jump to a page and buttons to scroll the results forwards or backwards. There is also a DropList to adjust the number of rows shown.

Each column displayed in the table has 3 links: edit, view, and delete.

  • Edit: clicking this link dynamically updates the content of the create/edit form displayed on the bottom of the page with the values from the selected row
  • View: clicking this link displays a PrimeFaces dialog with additional employee information not shown in the row.
  • Delete: clicking this displays the modal PrimeFaces ConfirmDialog component asking if the user wants to proceed with the delete of the current row.

Create/Edit Form:

  • Manually populating all the values in the form and then clicking save causes Hibernate to insert a new record in the database.
  • Clicking save after the form was populated with data from the table above it causes the entry to be updated in the database.
  • In both cases, the application does some AJAX magic to refresh the values in the table above, echoing your changes.
  • Clear resets the form so new values can be entered to create a new Employee record in the db.

Conclusion
Using JSF2/PrimeFaces you have a rich set of reusable UI components that allow you to develop modern web applications while focusing your effort on solving business problems. Using lazy loading, you obtain a nice balance of speed, memory use, and data freshness.

Links
PrimeFaces http://primefaces.org
Hibernate http://www.hibernate.org/docs
Spring http://www.springsource.org/spring-framework
BIRT http://www.eclipse.org/birt/phoenix/db/

Advertisements
Categories: Java Tags: , , , ,
  1. Gilson D.
    March 30, 2012 at 11:42 am

    Wow, this sample is very useful.
    It’s exactly what i was looking for.
    PrimeFaces doesn’t have a truly sample using datatable with JPA + Spring.
    Thanks a lot.

  2. April 5, 2012 at 12:13 pm

    Thank you Nick.
    Very nice tutorial.

    Just one thing about JPA Criteria! I don’t know why did JPA expert group make such an awful API !
    About 80 lines of code for just one simple query!!
    Hibernate criteria is better 😉
    Do you know any better alternative?

    Again thanks for great tutorial.
    Continue writing such great tutorial about Primefaces.

  3. Gilson
    April 6, 2012 at 8:42 am

    Great Job! Thanks for sharing it. Primefaces has no real datatable jpa related on showcase link and there aren’t complete samples envolving Spring/JPA.

  4. Tavo
    April 16, 2012 at 3:31 pm

    Very clear, thank you for using existing libraries in the POM, it was waiting for a long time, congratulations on this blog

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: