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
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | <? 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"> < 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
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | @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
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 | @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
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | @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
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 063 064 065 066 067 068 069 070 071 072 073 074 075 076 077 078 079 080 081 082 083 084 085 086 087 088 089 090 091 092 093 094 095 096 097 098 099 100 101 102 103 104 105 106 107 108 109 | 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
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | @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.