Effective pagination example in jsf 2

Introduction

In this tutorial I will show how to create effective pagination example in jsf 2, hibernate and in-memory database – hsqdb . In memory database like hsqldb is good when you want to do some quick POC or testing the application without setting up a large database.

There are lots of built-in API for datatable paginations like PrimeFaces, RichFaces, Openfaces, SmartFaces, ICEFaces etc. but if someone wants to create his/her own application’s pagination then it might be useful at least for beginners.

You can create this web application in any JEE based IDE like Netbeans, Eclipse etc.

The pagination is required when you have lots of data in database or any persistent system and you want to display them on web page but it’s not feasible to display them in a single page. You can display them in a single page but users have to scroll down the page and it may not be a good web page browsing experience for your users. So it is recommended to break the data into multiple page for displaying and it will be easier for your users to navigate the page without going down much.

Prerequisites

JSF 2.2, Java at least 1.8, Hibernate 3.6

We will see how to create effective pagination example in jsf 2. We will create here maven based web application in Eclipse.

Project Setup

Create maven based web application in Eclipse with the following artifact and group ids.

Group Id: com.roytuts
Artifact Id: jsf2-pagination

pom.xml

Make sure you have updated your pom.xml file to have the similar dependencies:

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.roytuts</groupId>
	<artifactId>jsf2-pagination</artifactId>
	<packaging>war</packaging>
	<version>0.0.1-SNAPSHOT</version>
	<url>http://maven.apache.org</url>
	<dependencies>
		<dependency>
			<groupId>com.sun.faces</groupId>
			<artifactId>jsf-api</artifactId>
			<version>2.2.4</version>
		</dependency>
		<dependency>
			<groupId>com.sun.faces</groupId>
			<artifactId>jsf-impl</artifactId>
			<version>2.2.4</version>
		</dependency>
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-core</artifactId>
			<version>3.6.3.Final</version>
		</dependency>
		<dependency>
			<groupId>javassist</groupId>
			<artifactId>javassist</artifactId>
			<version>3.12.1.GA</version>
		</dependency>
		<dependency>
			<groupId>org.hsqldb</groupId>
			<artifactId>hsqldb</artifactId>
			<version>2.3.1</version>
		</dependency>
	</dependencies>
	<build>
		<finalName>jsf2-pagination</finalName>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.6.1</version>
				<configuration>
					<source>1.8</source>
					<target>1.8</target>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

Hibernate Configuration

Once you create the web application, create a hibernate configuration file (hibernate.cfg.xml) in the classpath. The below hibernate configuration file contains database dialect, database connection details, hibernate XML and Java mapping files. It builds the Session Factory from all these configurations.

We will use here in-memory database hsqldb, if you want you can use other database, such as, MySQL, Oracle etc.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
	<session-factory>
		<property name="hibernate.dialect">org.hibernate.dialect.HSQLDialect</property>
		<property name="hibernate.connection.driver_class">org.hsqldb.jdbcDriver</property>
		<property name="hibernate.connection.url">jdbc:hsqldb:mem:test</property>
		<property name="hibernate.connection.username">sa</property>
		<property name="hibernate.connection.password"></property>
		<property name="hibernate.hbm2ddl.auto">update</property>
		<property name="hibernate.current_session_context_class">thread</property>
		<property name="hibernate.query.factory_class">org.hibernate.hql.classic.ClassicQueryTranslatorFactory</property>
		<property name="hibernate.show_sql">true</property>
		<property name="hibernate.format_sql">true</property>
		<mapping class="com.roytuts.jsf.hibernate.domain.Cds" />
	</session-factory>
</hibernate-configuration>

Entity Class

Now create entity class, which will be used to represent the table in hsqldb database.

The below class is required to map the table columns with Java attributes.

package com.roytuts.jsf.hibernate.domain;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "cds")
public class Cds implements Serializable {
	private static final long serialVersionUID = 1L;
	@Id
	private Long id;
	private String title;
	private String interpret;
	public Cds() {
	}
	public Cds(String title, String interpret) {
		this.title = title;
		this.interpret = interpret;
	}
	public Long getId() {
		return this.id;
	}
	public void setId(Long id) {
		this.id = id;
	}
	public String getTitle() {
		return this.title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
	public String getInterpret() {
		return this.interpret;
	}
	public void setInterpret(String interpret) {
		this.interpret = interpret;
	}
}

HibernateUtil Class

Create hibernate util class for creating single SessionFactory. This SessionFactory later will be used to produce non-thread-safe sessions.

package com.roytuts.jsf.hibernate.util;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class HibernateUtil {
	private static final SessionFactory sessionFactory;
	static {
		try {
			// Create the SessionFactory from standard (hibernate.cfg.xml)
			// config file.
			sessionFactory = new Configuration().configure().buildSessionFactory();
		} catch (Throwable ex) {
			// Log the exception.
			System.err.println("Initial SessionFactory creation failed." + ex);
			throw new ExceptionInInitializerError(ex);
		}
	}
	public static SessionFactory getSessionFactory() {
		return sessionFactory;
	}
}

QueryHelper Class

Now create the helper class which is used to execute the queries for database transactions. We have used hibernate’s Criteria API to query the database.

We will also create one method to insert some initial data into the database for testing the application. In real world application you don’t need such method.

package com.roytuts.jsf.hibernate.sql;
import java.util.List;
import org.hibernate.Criteria;
import org.hibernate.Session;
import com.roytuts.jsf.hibernate.domain.Cds;
import com.roytuts.jsf.hibernate.util.HibernateUtil;
public class QueryHelper {
	Session session = null;
	public QueryHelper() {
		session = HibernateUtil.getSessionFactory().getCurrentSession();
	}
	/**
	 * this method should not be used in real application
	 */
	public void insertRecords() {
		try {
			session.beginTransaction();
			Cds cd = new Cds();
			cd.setId(1l);
			cd.setInterpret("Singham");
			cd.setTitle("Rohit Setty");
			session.save(cd);
			cd = new Cds();
			cd.setId(2l);
			cd.setInterpret("Singham Returns");
			cd.setTitle("Rohit Setty");
			session.save(cd);
			cd = new Cds();
			cd.setId(3l);
			cd.setInterpret("Golmal");
			cd.setTitle("Rohit Setty");
			session.save(cd);
			cd = new Cds();
			cd.setId(4l);
			cd.setInterpret("Golmal Returns");
			cd.setTitle("Rohit Setty");
			session.save(cd);
			cd = new Cds();
			cd.setId(5l);
			cd.setInterpret("Golmal Retuens 2");
			cd.setTitle("Rohit Setty");
			session.save(cd);
			cd = new Cds();
			cd.setId(6l);
			cd.setInterpret("Welcome");
			cd.setTitle("Rohit Setty");
			session.save(cd);
			cd = new Cds();
			cd.setId(7l);
			cd.setInterpret("Toofan");
			cd.setTitle("RGV");
			session.save(cd);
			cd = new Cds();
			cd.setId(8l);
			cd.setInterpret("Alag Alag");
			cd.setTitle("Kishore Kumar");
			session.save(cd);
			cd = new Cds();
			cd.setId(9l);
			cd.setInterpret("Sholay");
			cd.setTitle("Amitav Bacchan");
			session.save(cd);
			cd = new Cds();
			cd.setId(10l);
			cd.setInterpret("Khiladi");
			cd.setTitle("Akshay Kumar");
			session.save(cd);
			cd = new Cds();
			cd.setId(11l);
			cd.setInterpret("Taal Songs");
			cd.setTitle("A R Reheman");
			session.save(cd);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	@SuppressWarnings("unchecked")
	public List<Cds> getListOfCds(int firstRow, int rowCount) {
		List<Cds> cdList = null;
		try {
			session.beginTransaction();
			Criteria criteria = session.createCriteria(Cds.class);
			criteria.setFirstResult(firstRow);
			criteria.setMaxResults(rowCount);
			if (criteria != null) {
				cdList = (List<Cds>) criteria.list();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return cdList;
	}
	public int countRows() {
		try {
			session.beginTransaction();
			Criteria criteria = session.createCriteria(Cds.class);
			if (criteria != null) {
				return criteria.list().size();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return 0;
	}
}

JSF 2 Managed Bean

Now create the jsf managed bean class which will be used to interact with the view and display data to the view.

Here I am not using faces-config.xml file so using the annotation. I have also kept the managed bean in ViewScope so that the state will be automatically maintained by the jsf API.

We have handled using the below code different scenarios. We will show total pages for the returned data. We will provide links for first page, next page, page numbers etc.

package com.roytuts.jsf.mbean;
import java.io.Serializable;
import java.util.List;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.component.UICommand;
import javax.faces.event.ActionEvent;
import com.roytuts.jsf.hibernate.domain.Cds;
import com.roytuts.jsf.hibernate.sql.QueryHelper;
@ViewScoped
@ManagedBean
public class JsfPaginationBean implements Serializable {
	private static final long serialVersionUID = 1L;
	private List<Cds> cdList;
	private QueryHelper queryHelper;
	/**
	 * pagination stuff
	 */
	private int totalRows;
	private int firstRow;
	private int rowsPerPage;
	private int totalPages;
	private int pageRange;
	private Integer[] pages;
	private int currentPage;
	/**
	 * Creates a new instance of JsfPaginationBean
	 */
	public JsfPaginationBean() {
		queryHelper = new QueryHelper();
		/**
		 * the below function should not be called in real world application
		 */
		queryHelper.insertRecords();
		// Set default values somehow (properties files?).
		rowsPerPage = 5; // Default rows per page (max amount of rows to be displayed at once).
		pageRange = 10; // Default page range (max amount of page links to be displayed at once).
	}
	public List<Cds> getCdList() {
		if (cdList == null) {
			loadCdList();
		}
		return cdList;
	}
	public void setCdList(List<Cds> cdList) {
		this.cdList = cdList;
	}
	public int getTotalRows() {
		return totalRows;
	}
	public void setTotalRows(int totalRows) {
		this.totalRows = totalRows;
	}
	public int getFirstRow() {
		return firstRow;
	}
	public void setFirstRow(int firstRow) {
		this.firstRow = firstRow;
	}
	public int getRowsPerPage() {
		return rowsPerPage;
	}
	public void setRowsPerPage(int rowsPerPage) {
		this.rowsPerPage = rowsPerPage;
	}
	public int getTotalPages() {
		return totalPages;
	}
	public void setTotalPages(int totalPages) {
		this.totalPages = totalPages;
	}
	public int getPageRange() {
		return pageRange;
	}
	public void setPageRange(int pageRange) {
		this.pageRange = pageRange;
	}
	public Integer[] getPages() {
		return pages;
	}
	public void setPages(Integer[] pages) {
		this.pages = pages;
	}
	public int getCurrentPage() {
		return currentPage;
	}
	public void setCurrentPage(int currentPage) {
		this.currentPage = currentPage;
	}
	private void loadCdList() {
		cdList = queryHelper.getListOfCds(firstRow, rowsPerPage);
		totalRows = queryHelper.countRows();
		// Set currentPage, totalPages and pages.
		currentPage = (totalRows / rowsPerPage) - ((totalRows - firstRow) / rowsPerPage) + 1;
		totalPages = (totalRows / rowsPerPage) + ((totalRows % rowsPerPage != 0) ? 1 : 0);
		int pagesLength = Math.min(pageRange, totalPages);
		pages = new Integer[pagesLength];
		// firstPage must be greater than 0 and lesser than totalPages-pageLength.
		int firstPage = Math.min(Math.max(0, currentPage - (pageRange / 2)), totalPages - pagesLength);
		// Create pages (page numbers for page links).
		for (int i = 0; i < pagesLength; i++) {
			pages[i] = ++firstPage;
		}
	}
	// Paging actions
	// -----------------------------------------------------------------------------
	public void pageFirst() {
		page(0);
	}
	public void pageNext() {
		page(firstRow + rowsPerPage);
	}
	public void pagePrevious() {
		page(firstRow - rowsPerPage);
	}
	public void pageLast() {
		page(totalRows - ((totalRows % rowsPerPage != 0) ? totalRows % rowsPerPage : rowsPerPage));
	}
	public void page(ActionEvent event) {
		page(((Integer) ((UICommand) event.getComponent()).getValue() - 1) * rowsPerPage);
	}
	private void page(int firstRow) {
		this.firstRow = firstRow;
		loadCdList();
	}
}

Creating JSF 2 View

Now create the view file – index.xhtml. I am not using jsp file because xhtml has better support for jsf 2.x and it also supports facelets.

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:f="http://xmlns.jcp.org/jsf/core"
      xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
    <h:head>
        <title>List of CDs</title>
    </h:head>
    <h:body>
        <h:form id="form" prependId="false">
            <!--The sortable datatable-->
            <h:dataTable value="#{jsfPaginationBean.cdList}" var="cd" rendered="#{jsfPaginationBean.cdList ne null}">
                <h:column>
                    <f:facet name="header">
                        CD ID
                    </f:facet>
                    <h:outputText value="#{cd.id}" />
                </h:column>
                <h:column>
                    <f:facet name="header">
                        Title
                    </f:facet>
                    <h:outputText value="#{cd.title}" />
                </h:column>
                <h:column>
                    <f:facet name="header">
                        Interpret
                    </f:facet>
                    <h:outputText value="#{cd.interpret}" />
                </h:column>
            </h:dataTable>
            <!--The paging buttons-->
            <h:commandButton value="first" action="#{jsfPaginationBean.pageFirst}"
                             disabled="#{jsfPaginationBean.firstRow == 0}" />
            <h:commandButton value="prev" action="#{jsfPaginationBean.pagePrevious}"
                             disabled="#{jsfPaginationBean.firstRow == 0}" />
            <h:outputText value="&nbsp;" escape="false"/>
            <h:commandButton value="next" action="#{jsfPaginationBean.pageNext}"
                             disabled="#{jsfPaginationBean.firstRow + jsfPaginationBean.rowsPerPage >= jsfPaginationBean.totalRows}" />
            <h:outputText value="&nbsp;" escape="false"/>
            <h:commandButton value="last" action="#{jsfPaginationBean.pageLast}"
                             disabled="#{jsfPaginationBean.firstRow + jsfPaginationBean.rowsPerPage >= jsfPaginationBean.totalRows}" />
            <h:outputText value="&nbsp;" escape="false"/>
            <h:outputText value="Page #{jsfPaginationBean.currentPage} / #{jsfPaginationBean.totalPages}" />
            <br />
            <!--The paging links-->
            <ui:repeat value="#{jsfPaginationBean.pages}" var="page">
                <h:commandLink value="#{page}" actionListener="#{jsfPaginationBean.page}"
                               rendered="#{page != jsfPaginationBean.currentPage}" />
                <h:outputText value="<b>#{page}</b>" escape="false"
                              rendered="#{page == jsfPaginationBean.currentPage}" />
            </ui:repeat>
            <br />
            <!-- Set rows per page -->
            <h:outputLabel for="rowsPerPage" value="Rows per page" />
            <h:inputText id="rowsPerPage" value="#{jsfPaginationBean.rowsPerPage}" size="3" maxlength="3" />
            <h:commandButton value="Set" action="#{jsfPaginationBean.pageFirst}" />
            <h:message for="rowsPerPage" errorStyle="color: red;" />
        </h:form>
    </h:body>
</html>

Deployment Descriptor – web.xml

You can verify your deployment descriptor file as well.

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemalocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
    <context-param>
        <param-name>javax.faces.PROJECT_STAGE</param-name>
        <param-value>Development</param-value>
    </context-param>
    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>*.jsf</url-pattern>
    </servlet-mapping>
    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
    <welcome-file-list>
        <welcome-file>index.jsf</welcome-file>
    </welcome-file-list>
</web-app>

Testing the Application

When your home page is displayed you will see the page similar to the below image:

effective pagination example in jsf 2

Now you can navigate to other pages by clicking on the page number links or first, prev, next, last.

You can also set how many entries should be displayed on a single page.

Let’s say you want to set Rows per page as 2, then you will see the following page

effective pagination example in jsf 2

Now you see the page number links have been increased as the entries on a page have been decreased.

Source Code

download source code

That’s all. Thanks for reading.

4 thoughts on “Effective pagination example in jsf 2

Leave a Reply

Your email address will not be published. Required fields are marked *