Enterprise Java

Lazy JSF Primefaces Datatable Pagination – Part 1

Today we will do JSF datatable pagination with a Lazy List with a view scoped managed bean. What all those words/expressions mean?

There are several JSF frameworks today that provide datatables with a ready pagination, columns sorter and others functionalities. Today we will use the Primefaces datatable.

Usually the datatables will put the displayed List, with the entities, inside the user http session. Increasing the objects inside the user session will have a direct impact on the server performance; each user that displays a datatable, that keeps a list in the session, will be allocating more and more memory in the server.

To look like real life, our post will use JPA and HSQLDB as database, and we will use JPQL to query the data.

In the end of this post you will find the link to download the source code.

We will use:

  • JSF 2.0 – JBoss 7 Implementation
  • JBoss 7.1 – the code of this post should be applied to all servers
  • Eclipse Indigo
  • JPA 2.0 – JBoss 7 Implementation
  • HSQLDB (2.2.8) – The HSQL it is a memory database, it will be easier to run it.
  • Primefaces 3.2 

This post is not about good development practices nor about adding classes layers of project modeling. I just intend to show how to do a pagination without a session managed bean.

We will have only one entity that will be persisted, the Player class. Bellow the class code:

package com.model;

import java.io.Serializable;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Player implements Serializable{

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;

    private String name;
    private int age;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public int hashCode() {
        return getId();
    }

    @Override
    public boolean equals(Object obj) {
        if(obj instanceof Player){
            Player player = (Player) obj;
            return player.getId() == getId();
        }

        return false;
    }
}

We will need to create a persistence.xml file inside the “src/META-INF” folder:

<?xml version="1.0" encoding="UTF-8"?>

<persistence version="2.0"
    xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">

    <persistence-unit name="JSFPU" transaction-type="JTA">
        <jta-data-source>java:/JSFDS</jta-data-source>

        <properties>
            <property name="hibernate.show_sql" value="false" />
            <property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect" />
            <property name="hibernate.connection.shutdown" value="true" />
            <property name="hibernate.hbm2ddl.auto" value="create-drop" />
        </properties>
    </persistence-unit>
</persistence>

To abstract the database transaction we will use a class named MyTransaction:

package com.connection;

import java.io.Serializable;

import javax.persistence.EntityManager;
import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.NotSupportedException;
import javax.transaction.RollbackException;
import javax.transaction.SystemException;

public class MyTransaction implements Serializable {

    /**
     *
     */
    private static final long serialVersionUID = 1L;

    private Connection connection = new Connection();   

    public void begin() throws NotSupportedException, SystemException {
        connection.begin();
    }

    public void commit() throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, IllegalStateException,
            SystemException {
        connection.commit();
    }

    public int getStatus() throws SystemException {
        return connection.getStatus();
    }

    public void rollback() throws IllegalStateException, SecurityException, SystemException {
        connection.rollback();
    }

    public void setRollbackOnly() throws IllegalStateException, SystemException {
        connection.setRollbackOnly();
    }

    public void setTransactionTimeout(int timeout) throws SystemException {
        connection.setTransactionTimeout(timeout);
    }

    public static MyTransaction getNewTransaction() {
        return new MyTransaction();
    }

    public EntityManager getEntityManager() {
        return connection.getEntityManager();
    }
}

You can see in the code above that the class is just an abstraction to the database connection; it will help us with the database queries. You could use any type of connection or you could use even a EJB to be avoiding this manually connection management.

Check the Connection class code:

package com.connection;

import java.io.Serializable;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.persistence.EntityManager;
import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.NotSupportedException;
import javax.transaction.RollbackException;
import javax.transaction.SystemException;
import javax.transaction.UserTransaction;

public class Connection implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * Get the user transaction by JNDI
     *
     * @return the user transaction
     */
    public UserTransaction getUserTransaction() {
        UserTransaction ut = null;
        try {
            Context c = new InitialContext();
            ut = (UserTransaction) c.lookup("java:comp/UserTransaction");
        } catch (Exception e) {
            e.printStackTrace();
        }

        return ut;
    }

    /**
     * Get the EntityManayger by JNDI
     *
     * @return the entity manager
     */
    public EntityManager getEntityManager() {
        EntityManager em = null;

        try {
            Context initCtx = new InitialContext();
            // The JSFPU must be written in the web.xml
            em = (EntityManager) initCtx.lookup("java:comp/env/JSFPU");
        } catch (Exception e) {
            e.printStackTrace();
        }

        return em;
    }

    public void begin() throws NotSupportedException, SystemException {
        getUserTransaction().begin();
    }

    public void commit() throws SecurityException, IllegalStateException, RollbackException, HeuristicMixedException, HeuristicRollbackException, SystemException {
        getUserTransaction().commit();
    }

    public int getStatus() throws SystemException {
        return getUserTransaction().getStatus();
    }

    public void rollback() throws IllegalStateException, SecurityException, SystemException {
        getUserTransaction().rollback();
    }

    public void setRollbackOnly() throws IllegalStateException, SystemException {
        getUserTransaction().setRollbackOnly();
    }

    public void setTransactionTimeout(int timeout) throws SystemException {
        getUserTransaction().setTransactionTimeout(timeout);
    }
}

We could use an injected UserTransaction by the JSF but we chose to use JNDI lookup. There are several Primefaces calls that are invoked outside the JSF context, you may get some NullPointerException if you try to access the references that should be injected. There are several ways to work with this issue, but we will use the JNDI Lookup to the EntityManager and to the UserTransaction.

Our last class will be the PlayerDAO:

package com.dao;

import java.io.Serializable;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.Query;

import com.connection.MyTransaction;
import com.model.Player;

public class PlayerDAO implements Serializable {

    private static final long serialVersionUID = 1L;
    private MyTransaction myTransaction;

    public PlayerDAO(MyTransaction transaction) {
        this.myTransaction = transaction;
    }

    /**
     * Find players in the DB
     *
     * @param startingAt the first "row" db that the query will search
     * @param maxPerPage the amount of records allowed per "trip" in the DB
     * @return a players java.util.List
     */
    @SuppressWarnings("unchecked")
    public List<Player> findPlayers(int startingAt, int maxPerPage) {
        EntityManager em = myTransaction.getEntityManager();

        // regular query that will search for players in the db
        Query query = em.createQuery("select p from Player p");
        query.setFirstResult(startingAt);
        query.setMaxResults(maxPerPage);

        return query.getResultList();
    }

    /**
     * Creates 100 players in the DB
     */
    public void create100Players() {
        EntityManager em = myTransaction.getEntityManager();

        Player player;

        for (int x = 0; x < 100; x++) {
            player = new Player();
            player.setName("Player: " + x);
            player.setAge(x);
            em.persist(player);
        }

        em.flush();
    }

    /**
     * Sum the number of players in the DB
     *
     * @return an int with the total
     */
    public int countPlayersTotal() {
        EntityManager em = myTransaction.getEntityManager();
        Query query = em.createQuery("select COUNT(p) from Player p");

        Number result = (Number) query.getSingleResult();

        return result.intValue();
    }
}

In the PlayerDAO class we have only 3 methods to work with our pagination. Notice that there is no method to list all the players in our database.

Create the folder “YOU_JBOSS/modules/org/hsqldb/main“. Inside this folder create a file named “module.xml“. Write the code bellow inside the “module.xml” file:

<module xmlns="urn:jboss:module:1.0" name="org.hsqldb">
    <resources>
        <resource-root path="hsqldb.jar" />
    </resources>
    <dependencies>
        <module name="javax.api" />
        <module name="javax.transaction.api" />
    </dependencies>
</module>

Copy the “hsqldb.jar” file to the folder “main” that you just created. This file you will find inside the HSQLDB jar you download in the path “hsqldb-2.2.8.zip/hsqldb-2.2.8/hsqldb/lib“.

Edit the file “YOU_JBOSS/standalone/configuration/standalone.xml” and add the code bellow in the “datasources” node:

<datasource jndi-name="java:/JSFDS" pool-name="JSFDS_POOL"
    enabled="true" jta="true" use-java-context="true" use-ccm="true">
    <connection-url>
        jdbc:hsqldb:mem:jsfinmemory
    </connection-url>
    <driver>
        hsqldb
    </driver>
    <pool>
        <prefill>false</prefill>
        <use-strict-min>false</use-strict-min>
        <flush-strategy>FailingConnectionOnly</flush-strategy>
    </pool>
    <security>
        <user-name>
            sa
        </user-name>
        <password>
        </password>
    </security>
</datasource>

In the drivers node add:

<driver name="hsqldb" module="org.hsqldb"/>

Reference: Lazy JSF Datatable Pagination (Primefaces) from our JCG partner Hebert Coelho at the uaiHebert blog.

Hebert Coelho

Senior Java Development, with 4 certifications and a published book about JSF (portuguese only). Founder of the blog uaiHebert.com visited from more than 170 different countries.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Roger Keays
11 years ago

Here’s a LazyList implementation that can be used wherever normal Lists are accpeted:

http://www.ninthavenue.com.au/blog/paging-large-data-sets-with-a-lazylist

sb
sb
11 years ago

Do u know how to update pagelink in each load in a lazy loading datatable. Say there are 3 page links. When user clicks on 3rd button the page links will be increased to 6.

Back to top button