real pagination


Since I start working on java web application , every time I found that people don’t care if we really do a database pagination or only an ajax pagination (some JavaScript code).

both methods are good, but it depends on the context and how many lines we have to handle.

My post is not about the choice (advantage and disadvantage ) but it will be talking about an approach ( a home-made java code) that looks like Value List Handler design pattern. this code helps me every time I’ve to show an html table with thousands of lines with filters, number of line per page….

So lets see what i did, and please if you have any comment or remarks or any advice that will make the code better, you will be welcome.

The class diagram:

class diagram

Real Pagination Diagram home-made

the Java Code:


package org.pagination.realpagination;
import java.io.Serializable;
import java.util.List;
import java.util.Map;

/**
* Pagination Service that brings to yo
*
* @author Younss AZZAYANI
*
*/
public interface IPagination {
/**
* return a the Business Object List.
*
* @param <code><T></code> Object Class
* @param clazz
*            object Class
* @param firstResult
* @param maxResult
* @param orderBy
* @param order
* @param filter
* @param clazzes
*            sub classes of <code><T></code>
* @return List<T>
*/
public List getPaggedObjectList(Class clazz, int firstResult,
int maxResult, String orderBy, String order,
Map filter, Class… clazzes);

/**
* count the number of lines returned by the dao query.
*
* @param <T>
*            Object Class
* @param clazz
*            Object Class
* @param filter
* @param clazzes
*            sub classes of <code><T></code>
* @return
*/
public int countNumberOfObject(Class clazz,
Map filter, Class… clazzes);

/**
* get element from database
*
* @param <code><T></code> object class
* @param clazz
*            object class
* @param id
*            serializable identifier
* @return instance of <code><T></code> if found null if not.
*/
public T findElement(Class clazz, Serializable id);
}

————————————————————-
package org.pagination.realpagination;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
*
* @author Younss AZZAYANI
*
*/
public class Page implements Serializable {

private static final long serialVersionUID = 1L;

public final static String PAGE_ACTION = “PAGE_ACTION”;

public static final String NEXT_PAGE = “NEXT_PAGE”;

public static final String PREVIOUS_PAGE = “PREVIOUS_PAGE”;

public static final String FIRST_PAGE = “FIRST_PAGE”;

public static final String LAST_PAGE = “LAST_PAGE”;

public static final String SIZE_PAGE = “SIZE_PAGE”;

public static final String PARAM_SIZE_PAGE = “PARAM_SIZE_PAGE”;

public static final String SORT_TABLE = “SORT”;

public static final String FILTER = “FILTER”;

public static final String PARAM_ORDER_BY = “orderBy”;

public static final String PARAM_ORDER_TYPE = “orderType”;

public static final String PARAM_OBJECT_ID = “objectId”;

public enum Order {
DESC(“DESC”), ASC(“ASC”);
private String value;

Order(String value) {
this.value = value;
}

public String getValue() {
return this.value;
}
}

public enum RowNum {
FIVE(5), TEN(10), TWENTY(20), THIRTY(30), FORTY(40), FIFTY(50);
private int value;

RowNum(int value) {
this.value = value;
}

public int getValue() {
return this.value;
}

public static RowNum valueOf(int value) {
switch (value) {
case 5:
return FIVE;
case 10:
return TEN;
case 20:
return TWENTY;
case 30:
return THIRTY;
case 40:
return FORTY;
case 50:
return FIFTY;

default:
return TEN;
}

}
}

/**
* user selected number of lines to show
*/
private RowNum maxResult = RowNum.TEN;

private Order order = Order.ASC;

/**
* DB current firstResult, from where we start retrieve data.
*/
private int firstResult = 0;

/**
* current page number.
*/
private int currentPageNumber = 0;

/**
* total pages Number.
*/
private int numOfPages = 0;

/**
* total data lines count.
*/
private int numOfLines = 0;

/**
* current loaded list
*/
List currentList = null;

private String orderBy = “”;

private Map<String, Object> filter = null;

/**
* let us to update pages number in depend with the number of line to show.
*
*/
protected void initPagination() {
this.currentPageNumber = 0;
this.firstResult = 0;
this.orderBy = “”;
this.numOfPages = numOfLines / maxResult.value;

if (numOfLines % maxResult.value != 0) {
this.numOfPages++;
}
}

/**
* @param currentList
*            the currentList to set
*/
protected void setCurrentList(List bindedList) {
this.currentList = bindedList;
}

/**
* @return the firstResult
*/
public int getFirstResult() {
return firstResult;
}

/**
* @param firstResult
*            the firstResult to set
*/
public void setFirstResult(int index) {
this.firstResult = index;
}

/**
* @return the numOfLines
*/
public int getNumOfLines() {
return numOfLines;
}

/**
* @param numOfLines
*            the numOfLines to set
*/
public void setNumOfLines(int numOfLines) {

this.numOfLines = numOfLines;
initPagination();
}

/**
* @return the numOfPages
*/
public int getNumOfPages() {
return numOfPages;
}

/**
* @param numOfPages
*            the numOfPages to set
*/
public void setNumOfPages(int numOfPages) {
this.numOfPages = numOfPages;
}

/**
* @return the currentPageNumber
*/
public int getCurrentPageNumber() {
return currentPageNumber;
}

/**
* @param currentPageNumber
*            the currentPageNumber to set
*/
public void setCurrentPageNumber(int pageNumber) {
this.currentPageNumber = pageNumber;
}

/**
* @return the orderBy
*/
public String getOrderBy() {
return orderBy;
}

/**
* @param orderBy
*            the orderBy to set
*/
public void setOrderBy(String orderBy) {
this.orderBy = orderBy;
}

/**
* @return the maxResult
*/
public RowNum getMaxResult() {
return maxResult;
}

/**
* @param maxResult
*            the maxResult to set
*/

protected void setMaxResult(RowNum numOfLineToShow) {

this.maxResult = numOfLineToShow;

initPagination();

}

/**
* go to the previous page.
*
*/
protected void goToPrevious() {
this.currentPageNumber–;
this.firstResult = this.currentPageNumber * this.maxResult.value;
}

/**
* go to the next page.
*
*/
protected void goToTheNext() {
this.currentPageNumber++;
this.firstResult = this.currentPageNumber * this.maxResult.value;
}

/**
* go to the first page.
*
*/
protected void goToTheFirst() {
this.currentPageNumber = 0;
this.firstResult = this.currentPageNumber * this.maxResult.value;
}

/**
* go to the last page.
*
*/
protected void goToTheLast() {
this.currentPageNumber = this.numOfPages – 1;
this.firstResult = this.currentPageNumber * this.maxResult.value;
}

/**
* @return the order
*/
public Order getOrder() {
return order;
}

/**
* @param order
*            the order to set
*/
protected void setOrder(Order order) {
this.order = order;
}

/**
* @return the filter
*/
protected Map getFilter() {
if (filter == null) {
filter = new HashMap<String, Object>();
}

return filter;
}

/**
* @param filter
*            the filter to set
*/
@SuppressWarnings(“unused”)
protected void setFilter(Map filter) {
this.filter = filter;
}

protected void sortTheList(String orderBy, String order) {
initPagination();
setOrderBy(orderBy);
setOrder(Order.valueOf(order));

}

public Object getFilterValue(String key) {
return getFilter().get(key);
}

public final RowNum[] getRowNumList() {

return RowNum.values();
}

public Iterator iterator() {
return this.currentList.iterator();
}

}

—————————————————–
package org.pagination.realpagination;

import java.lang.reflect.Field;
import java.util.Map;

/**
* it’s a controller class that makes pagination possible, the current page is
* stocked in page
*
* @author Younss AZZAYANI
*
* @param <code><T></code>
*/
public class PaginationHandler<T> {

IPagination pagination = null;

Page<T> page = null;

/**
* @param pagination
*            the pagination service
* @param page
*            the page stocked in a scope
*/
public PaginationHandler(IPagination pagination, Page page) {
super();
setPagination(pagination);
setPage(page);
}

/**
*
* @param clazz
*            Business Object
* @param paramMap
*            Mapparams from request
* @param classes
*            “instance of <code>Class<T></code> if you are doing
*            inheritance
* @throws SecurityException
* @throws NoSuchFieldException
*/
public void paginate(Class clazz, Map paramMap,
Class… classes) throws SecurityException,
NoSuchFieldException {
String action = null;
String sizeOfPage = null;
if (paramMap != null && paramMap.size() > 0) {
action = paramMap.get(Page.PAGE_ACTION);
sizeOfPage =  paramMap.get(Page.PARAM_SIZE_PAGE);
}

if (sizeOfPage != null
&& sizeOfPage.trim().length() > 0
&& Page.RowNum.valueOf(sizeOfPage).getValue() != getPage()
.getMaxResult().getValue()) {
// set the number of lines per page
getPage().setMaxResult(Page.RowNum.valueOf(sizeOfPage));
}

if (action == null || action.length() == 0) {
// when no action sent brings me the first page
countNumberOfObject(clazz, classes);
} else {

if (action.equals(Page.PREVIOUS_PAGE)) {
getPage().goToPrevious();
}
if (action.equals(Page.NEXT_PAGE)) {
getPage().goToTheNext();
}
if (action.equals(Page.FIRST_PAGE)) {
getPage().goToTheFirst();
}
if (action.equals(Page.LAST_PAGE)) {
getPage().goToTheLast();
}

if (action.equals(Page.SORT_TABLE)) {
getPage().sortTheList(
paramMap.get(Page.PARAM_ORDER_BY),
paramMap.get(Page.PARAM_ORDER_TYPE));
}
if (action.equals(Page.FILTER)) {
// upfate the filter
updateFilter(clazz, paramMap);
// reinit the num of lines per page
countNumberOfObject(clazz, classes);
}
}

// update the page element list contents
fillTheList(clazz, classes);

}

/**
* populate the filter. the filter keys are the attributes of the Business
* Object (a Java Bean). so in your interface (a jsp interface for example)
* the input names must correspond to the Java Bean Properties.
*
* @param paramMap
*            Map ,may be your request params Map
* @param fieldArray
*            Field[] array of your JAva Bean
*/
private void populateFilter(Map paramMap,
Field[] fieldArray) {

for (Field field : fieldArray) {

String paramName = field.getName();
String paramValue = (String) paramMap.get(paramName);
if (paramValue != null && paramValue.length() > 0) {
Class<?> c = null;
try {
c = Class.forName(field.getType().getName());
} catch (ClassNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
// when the field is an Enum
if (field.getType().isEnum()) {

@SuppressWarnings(“unchecked”)
ClassEnum> ce = (Class) c;
@SuppressWarnings(“unchecked”)
Enum enumValue = Enum.valueOf(ce, paramValue);
getPage().getFilter().put(paramName, enumValue);

} else if (c == Boolean.class) {
getPage().getFilter().put(
paramName,
(paramValue.equals(“true”) ? Boolean.valueOf(true)
: Boolean.valueOf(false)));
} else {

getPage().getFilter().put(paramName, paramValue);
}
// this filter can be more developped (agregation…)
// add code here

}
}
}

/**
* popultate the page list of Business Object returned by the service
*
* @param clazz
*            master Business Object
* @param classes
*            sub Classes Of Business Object Class
*
*/
private void fillTheList(Class clazz, Class… classes) {

getPage().setCurrentList(
getPagination().getPaggedObjectList(clazz,
getPage().getFirstResult(),
getPage().getMaxResult().getValue(),
getPage().getOrderBy(),
getPage().getOrder().getValue(), getPage().getFilter(),
classes));

}

/**
* update the filter
*
* @param clazz
*            business Object(java bean) Class instance
* @param paramMap
*            Map ,may be the request params Map
* @throws SecurityException
* @throws NoSuchFieldException
*/
private void updateFilter(Class clazz, Map paramMap)
throws SecurityException, NoSuchFieldException {
getPage().setFilter(null);
// get the field array of Business Object
Field[] fieldArray = clazz.getDeclaredFields();
// init the filter with new values
populateFilter(paramMap, fieldArray);

}

/**
* set the number of lines founded
*
* @param clazz
* @param classes
*/
private void countNumberOfObject(Class clazz,
Class<? extends T>… classes) {

getPage().setNumOfLines(
getPagination().countNumberOfObject(clazz,
getPage().getFilter(), classes));

}

/**
* @return the page
*/
public Page getPage() {
return page;
}

/**
* @param page
*            the page to set
*/
private void setPage(Page page) {
this.page = page;
}

/**
* @return the pagination
*/
public IPagination getPagination() {
return pagination;
}

/**
* @param pagination
*            the pagination to set
*/
public void setPagination(IPagination pagination) {
this.pagination = pagination;
}

}

——————————————-

an example of IPagination implementation:

package some.package;
import org.pagination.realpagination.IPagination;
public class MyMasterBusinessObjectDao implements IPagination{



//this may be your hibernate dao or others


//the implementation
public List getPaggedObjectList(Class clazz, int firstResult,
int maxResult, String orderBy, String order,
Map<String, Object> filter,Class<? extends T>… classes) {

String query = “SELECT obj FROM ” + clazz.getName() + ” obj “;
boolean clauseWhereAdded = false;
if (filter != null && !filter.isEmpty()) {

for (Iterator iter = filter.keySet().iterator(); iter
.hasNext();) {
String key = iter.next();
if (filter.get(key) == null ||(filter.get(key) instanceof String
&&((String) filter.get(key)).lenght==0))  {
continue;
}

if (!clauseWhereAdded) {
query += “WHERE”;
clauseWhereAdded = true;
} else {
query += ” AND “;
}
query += ” obj.” + key + ”  like :_” + key + ” “;

}
}

if(classes != null && classes.length >0){
if(!clauseWhereAdded){
query += “WHERE”;
clauseWhereAdded = true;
}else{
query += ” AND “;
}
query += ” obj.class in(“;
boolean addComma = false;
for(Class instanceClass : classes){
if(!addComma){

addComma = true;
}else{
query+=”,”;
}
query+=instanceClass.getCanonicalName();
}
query += “) “;
}

if (orderBy !=null && orderBy.length()>0) {
query += ” ORDER BY obj.” + orderBy + ” ” + order;
} else {
query += ” ORDER BY obj.id ASC”;
}
logger.debug(“hqlQuery=[“+query+”]”);
//hibernate query
Query hqlQuery = createQuery(query);
if (filter != null && !filter.isEmpty()) {
for (Iterator iter = filter.keySet().iterator(); iter
.hasNext();) {
String key = iter.next();
if (filter.get(key) == null ||(filter.get(key) instanceof String
&& ((String) filter.get(key))length()==0) ) {
continue;
}

hqlQuery.setParameter(“_” + key, filter.get(key));
}
}
return hqlQuery.setFirstResult(firstResult).setMaxResults(maxResult)
.getResultList();

}

//the implementation
public int countNumberOfObject(Class clazz,
Map<String, Object> filter, Class<? extends T>… classes) {

String query = “SELECT count(*) FROM ” + clazz.getName() + ” obj “;
boolean clauseWhereAdded = false;
if (filter != null && !filter.isEmpty()) {

for (Iterator iter = filter.keySet().iterator(); iter
.hasNext();) {
String key = iter.next();
if (filter.get(key) == null || (filter.get(key) instanceof String
&& ((String) filter.get(key)).length()==0)) {
continue;
}

if (!clauseWhereAdded) {
query += “WHERE”;
clauseWhereAdded = true;
} else {
query += ” AND “;
}
query += ” obj.” + key + ”  like :_” + key + ” “;

}
}
if(classes != null && classes.length >0){
if(!clauseWhereAdded){
query += “WHERE”;
clauseWhereAdded = true;
}else{
query += ” AND “;
}
query += ” obj.class in(“;
boolean addComma = false;
for(Class<?extends T> instanceClass : classes){
if(!addComma){
addComma = true;
}else{
query+=”,”;
}
query+=instanceClass.getCanonicalName();
}
query += “) “;
}
Query hqlQuery = createQuery(query);
if (filter != null && !filter.isEmpty()) {
for (Iterator<String> iter = filter.keySet().iterator(); iter
.hasNext();) {
String key = iter.next();
if (filter.get(key) == null || (filter.get(key) instanceof String
&& ((String) filter.get(key)).length()==0)) {
continue;
}

hqlQuery.setParameter(“_” + key, filter.get(key));
}
}
logger.debug(“hql query = [” + query +”]”);
return ((Long) hqlQuery.getSingleResult()).intValue();
}


public T findElement(Class clazz, Serializable id){
return (T) getEntityManager().find(clazz, id);
}


}

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: