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.
Reference: Primefaces DataTable Lazy Loading with pagination, filtering and sorting using JPA Criteria, @ViewScoped from our JCG partner Leonardo Shikida at the Leo’s Notepad blog.
Hi. Can you, please, post the the full project for download? I really appreciate that! Thanks. and congratulations for your work!
Hi Marcel. Right now, I can’t publish the full project due to legal reasons. Sorry.
Ok, I understand that. I’ll try to build it. Thanks for answering.
Hi, I would like to know the code of the “GenericDAO” -class. Can you post it please?
there are many actually.
e.g [1] http://code.google.com/p/generic-dao/, [2] http://blog.xebia.com/2009/03/09/jpa-implementation-patterns-data-access-objects/, [3] http://spring.io/blog/2011/02/10/getting-started-with-spring-data-jpa/
mine is mostly a wrapper around entity manager methods + some specific methods
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
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 »
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?
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!
you’re right. A SQL just for count would be better in this case.
What is this SiteType_.name and where you have defined this?
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/
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.
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));’
thanks!
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));
thanks!
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.
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
Thank you man for sharing
Hello!
This implementation works with richfaces 4?
Or in primefaces only?
only primefaces
Excellent! Can you post the the full project for download? Thanks.
Sorry, the project source code belongs to my client :-(
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.
the override should look like this after PF5:
load(int first, int pageSize, String sortField, SortOrder sortOrder, Map filters)
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.