Enterprise Java

Primefaces DataTable Lazy Loading with pagination, filtering and sorting using JPA Criteria, @ViewScoped

Primefaces datatable lazy pagination works, but I was really frustrated after searching for a full example using Criteria in the web. So I’ve mixed solutions from

  • http://stackoverflow.com/questions/13972193/how-to-query-data-for-primefaces-datatable-with-lazy-loading-and-pagination
  • http://www.primefaces.org/showcase/ui/datatableLazy.jsf
  • http://www.javacodegeeks.com/2012/04/lazy-jsf-primefaces-datatable.html

To put all together in a complete example using:
 
 

  • Primefaces 4.0
  • TomEE 1.6.0+
  • Criteria JPA

With:

  • Filtering
  • Sorting
  • Pagination
  • Using @ViewScoped Managed Beans

So let’s go:

xhtml snippet

<?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:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:p="http://primefaces.org/ui">
<h:body>
    <h:form id="am">

            <p:dataTable
                var="element"
                value="#{inventoryManagerMB.model}"
                lazy="true"
                paginator="true"
                rows="10"
                paginatorTemplate="{RowsPerPageDropdown} {FirstPageLink} {PreviousPageLink} {CurrentPageReport} {NextPageLink} {LastPageLink}"
                rowsPerPageTemplate="10,50,100"
                id="sites">
 
            <p:column sortBy="id" filterBy="id" filterMatchMode="contains" headerText="ID">                
                <h:outputText value="#{element.id}" />
            </p:column>
            <p:column sortBy="name" filterBy="name" filterMatchMode="contains" headerText="Name">                
                <h:outputText value="#{element.name}" />
            </p:column>
            <p:column sortBy="siteType.name" filterBy="siteType.name" filterMatchMode="contains" headerText="Type">                
                <h:outputText value="#{element.siteType.name}" />
            </p:column>
            <p:column sortBy="ip" filterBy="ip" filterMatchMode="contains" headerText="IP">                
                <h:outputText value="#{element.ip} " />
            </p:column>
            <p:column sortBy="description" filterBy="description" filterMatchMode="contains" headerText="Description">                
                <h:outputText value="#{element.description}" />
            </p:column>
            </p:dataTable>
    </h:form>
</h:body>
</html>

ManagedBean

@ManagedBean
@ViewScoped
public class InventoryManagerMB implements Serializable {

    private static final long serialVersionUID = -1201944101993687165L;

    @EJB
    private InventoryManagerEJB inventoryManagerEJB;
   
    private LazyDataModel<Site> model;
 


     @PostConstruct
    public void init() {
        try {
            this.model = new LazyDataModel<Site>(){
                private static final long    serialVersionUID    = 1L;
                @Override
                public List<Site> load(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String, String> filters) {
                    List<Site> result = inventoryManagerEJB.getResultList(first, pageSize, sortField, sortOrder, filters);
                    model.setRowCount(inventoryManagerEJB.count(sortField, sortOrder, filters));
                    return result;
                }
            };
}

    public LazyDataModel<Site> getModel() {
        return model;
    }

    public void setModel(LazyDataModel<Site> model) {
        this.model = model;
    }
 
(...)

EJB

@Stateless
public class InventoryManagerEJB {
   
    @Inject
    private BaseService baseService;

    public List<Site> getResultList(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String, String> filters) {
        List<Site> all = new ArrayList<Site>();
        all.addAll(this.baseService.getSiteDAO().getAll(first,pageSize,sortField,sortOrder,filters));
        return all;
    }

    public int count(String sortField, SortOrder sortOrder, Map<String, String> filters) {
        return this.baseService.getSiteDAO().getAll(-1,-1,null,null,filters).size();
    }
}

BaseService

@ApplicationScoped
public class BaseService implements Serializable{
   
    private static Logger log = Logger.getLogger(BaseService.class);
   
    /*
     * persistence
     */
   
    private static final long serialVersionUID = 588696475267901772L;

    @PersistenceContext
    private EntityManager entityManager;

    private SiteDAO siteDAO;

    @PostConstruct
    public void init() {
            siteDAO = new SiteDAO(entityManager);
    }


    public SiteDAO getSiteDAO() {
        return siteDAO;
    }

SiteDAO

public class SiteDAO extends GenericDAO<Site>{

    public SiteDAO(EntityManager entityManager) {
        super(entityManager);
    }

    public Collection<Site> getAll(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String, String> filters) {
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<Site> q = cb.createQuery(Site.class);
        Root<Site> site = q.from(Site.class);
        Join<Site,SiteType> siteType = site.join(Site_.siteType);
        q.select(site);


        Path<?> path = getPath(sortField, site, siteType);
        if (sortOrder == null){
            //just don't sort
        }else if (sortOrder.equals(SortOrder.ASCENDING)){
            q.orderBy(cb.asc(path));
        }else if (sortOrder.equals(SortOrder.DESCENDING)){
            q.orderBy(cb.asc(path));
        }else if (sortOrder.equals(SortOrder.UNSORTED)){
            //just don't sort
        }else{
            //just don't sort
        }
       
        //filter
        Predicate filterCondition = cb.conjunction();
        for (Map.Entry<String, String> filter : filters.entrySet()) {
            if (!filter.getValue().equals("")) {
                //try as string using like
                Path<String> pathFilter = getStringPath(filter.getKey(), site, siteType);
                if (pathFilter != null){
                    filterCondition = cb.and(filterCondition, cb.like(pathFilter, "%"+filter.getValue()+"%"));
                }else{
                    //try as non-string using equal
                    Path<?> pathFilterNonString = getPath(filter.getKey(), site, siteType);
                    filterCondition = cb.and(filterCondition, cb.equal(pathFilterNonString, filter.getValue()));
                }
            }
        }
        q.where(filterCondition);
       
        //pagination
        TypedQuery<Site> tq = entityManager.createQuery(q);
        if (pageSize >= 0){
            tq.setMaxResults(pageSize);
        }
        if (first >= 0){
            tq.setFirstResult(first);
        }
        return tq.getResultList();
    }

    private Path<?> getPath(String field, Root<Site> site, Join<Site, SiteType> siteType) {
        //sort
        Path<?> path = null;
        if (field == null){
            path = site.get(Site_.name);
        }else{
            switch(field){
                case "id":
                    path = site.get(Site_.id);
                    break;
                case "name":
                    path = site.get(Site_.name);
                    break;
                case "siteType.name":
                    path = siteType.get(SiteType_.name);
                    break;
                case "ip":
                    path = site.get(Site_.ip);
                    break;
                case "description":
                    path = site.get(Site_.description);
                    break;
            }
        }
        return path;
    }

    private Path<String> getStringPath(String field, Root<Site> site, Join<Site, SiteType> siteType) {
        //sort
        Path<String> path = null;
        if (field == null){
            path = site.get(Site_.name);
        }else{
            switch(field){
                case "id":
                    path = null;
                    break;
                case "name":
                    path = site.get(Site_.name);
                    break;
                case "siteType.name":
                    path = siteType.get(SiteType_.name);
                    break;
                case "ip":
                    path = site.get(Site_.ip);
                    break;
                case "description":
                    path = site.get(Site_.description);
                    break;
            }
        }
        return path;
    }
}

Entity

@Entity
@Table(uniqueConstraints=@UniqueConstraint(columnNames={"name"}))
public class Site implements Serializable {
    /**
     *
     */
    private static final long serialVersionUID = 8008732613898597654L;
   
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
   
    @Nameable
    @Column(nullable=false)
    private String name;
   
    private String ip;   
   
    private String description;
   
    @ManyToOne
    @JoinColumn(name="siteTypeId")
    private SiteType siteType;

(...)
}

That’s it. Enjoy.
 

Leonardo Shikida

Leonardo is a Systems Analyst and Java Senior Developer at IBM (The postings on his site are his own and don't necessarily represent IBM's positions, strategies or opinions.)
Subscribe
Notify of
guest

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

27 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
MarceL
MarceL
10 years ago

Hi. Can you, please, post the the full project for download? I really appreciate that! Thanks. and congratulations for your work!

Leo
Leo
10 years ago

Hi Marcel. Right now, I can’t publish the full project due to legal reasons. Sorry.

MarceL
MarceL
10 years ago
Reply to  Leo

Ok, I understand that. I’ll try to build it. Thanks for answering.

Alex
Alex
9 years ago

Hi, I would like to know the code of the “GenericDAO” -class. Can you post it please?

Leo (@shikida)
Leo (@shikida)
9 years ago
Tet
Tet
9 years ago

hello, i use oracle db and i have blob field containing an image in the database. i would like to load a photo in xhtml file using EntityManager, for db connection (without DAO) , can you publish such example, thanks in advance

Leo (@shikida)
Leo (@shikida)
9 years ago

You may find this stackoverflow thread useful – http://stackoverflow.com/questions/8304967/how-to-use-pgraphicimage-with-streamedcontent-within-pdatatable But basically, it seems there are at least two things about p:graphicimage that you must know (I don’t know why primefaces oficial documentation and demo doesn’t say a word about this but anyway) [1] your image is not available during the RENDER_RESPONSE phase [2] @ViewScoped won’t work So here’s the code that worked for me [1] Managed bean is session scoped and the bean attribute is not DefaultStreamedContent (which is not serializable) but byte[]. @ManagedBean @SessionScoped public class ImageController implements Serializable{ private byte[] img; [2] the dynamic image getter method is… Read more »

Felix
Felix
9 years ago

Works great! But I don’t like the count implementation. That means, that you select all filtered values from the database just to execute “.size()” on the result list. Wouldn’t it be better to use a SQL query to do that? Like:
“SELECT COUNT(s) FROM Site s” and then add the creterias?

Leo
Leo
9 years ago
Reply to  Felix

you’re right.

a much better approach for the count() method is to duplicate the query method in the DAO, changing the return result to a Long (although, primefaces will expect an Integer) like this — CriteriaQuery q = cb.createQuery(Long.class); , removing the pagination code (setMaxResults and setFirstResult) and returning just the count like this — return entityManager.createQuery(q).getSingleResult(); — thanks!

Leo
Leo
9 years ago

you’re right. A SQL just for count would be better in this case.

Sanjay
9 years ago

What is this SiteType_.name and where you have defined this?

Leo
Leo
9 years ago
Reply to  Sanjay

This class is a Criteria metamodel class, generated during compilation. Check this link for instructions on how to generate them manually — http://openjpa.apache.org/builds/2.1.1/apache-openjpa/docs/ch13s04.html — or this link to configure eclipse to do it automatically for you — http://www.javabeat.net/jpa-2-metamodel-eclipse/

Sanjay
9 years ago

I have the similar issue where I have to join multiple sub object to the main query like Subscription–> Customer, SubscriptionMode, Payment Type and etc.

Dominik
Dominik
9 years ago

Nice sample, thanks for publishing!
It seems like there is a small error in SiteDAO line 21. It should be ‘q.orderBy(cb.desc(path));’

Leo
Leo
9 years ago
Reply to  Dominik

thanks!

André
André
9 years ago

Thanks for this very helpful sample.
Little typo @:

}else if (sortOrder.equals(SortOrder.DESCENDING)){
021 q.orderBy(cb.asc(path));

should be q.orderBy(cb.desc(path));

Leo
Leo
9 years ago
Reply to  André

thanks!

Daniel Gonzalez
Daniel Gonzalez
9 years ago

I’m having some problems for sorting and filtering Date fields. Do I have to do something specific for Date?

Thank you for your excellent post.

Leo
Leo
9 years ago

Hi Daniel

Recently, I had to add a date filter to a column. My solution was some sort of a hack, but it seems to work. Check it out here – http://leonotepad.blogspot.com.br/2015/03/adding-date-range-filter-to-primefaces.html

zafar
zafar
8 years ago

Thank you man for sharing

Edson
Edson
8 years ago

Hello!
This implementation works with richfaces 4?
Or in primefaces only?

leonardo kenji
leonardo kenji
8 years ago
Reply to  Edson

only primefaces

Marlon Pessoa
Marlon Pessoa
8 years ago

Excellent! Can you post the the full project for download? Thanks.

Leonardo Kenji
Leonardo Kenji
8 years ago
Reply to  Marlon Pessoa

Sorry, the project source code belongs to my client :-(

Jon
Jon
7 years ago

Hi Leonardo,

Did you find the same for the data schedule component?

I need to data bind the Primefaces schedule component
http://www.primefaces.org/showcase/ui/data/schedule.xhtml

Any suggestions greatly appreciated.

Peter Taylor
6 years ago

the override should look like this after PF5:
load(int first, int pageSize, String sortField, SortOrder sortOrder, Map filters)

Peter Taylor
6 years ago

Look at this if override isn’t working: https://stackoverflow.com/questions/25946081/primefaces-lazydatamodel-load-override-not-working I tried to submit before but forgot to escape the brackets.

Back to top button