Friday, April 3, 2009

Generic Data Access Objects with Pagnation under Spring and Hibernate Framework

Page.java
import java.util.List;

public class Page<T> {
private List<T> rowsInPage;
private int pageSize;
private int pageNo;
private int totalRows;

/**
*
* @param pageNo
* @param pageSize
* @param rowsInPage
* @param totalRows
*/
public Page(int pageNo, int pageSize, List<T> rowsInPage, int totalRows) {
if(pageSize > 0){
this.pageNo = (pageNo > 0) ? pageNo : 1;
this.pageSize = pageSize;
this.rowsInPage = rowsInPage;
this.totalRows = totalRows;
}else if(pageSize == 0){
this.pageNo = 0;
this.pageSize = 0;
this.rowsInPage = null;
this.totalRows = 0;
}else{
this.pageNo = 1;
this.pageSize = -1;
this.rowsInPage = rowsInPage;
this.totalRows = -1;
}
}

/**
*
* @return
*/
public boolean hasNext() {
return getPages() > pageNo;
}

/**
*
* @return
*/
public boolean hasPrevious() {
return pageNo > 1;
}

/**
*
* @return
*/
public List<T> getRowsInPage() {
return rowsInPage;
}

/**
*
* @return
*/
public int getPageSize() {
return pageSize;
}

/**
*
* @return
*/
public int getPageNo() {
return pageNo;
}

/**
*
* @return
*/
public int getTotalRows() {
return totalRows;
}

/**
*
* @return
*/
public int getPages() {
if(totalRows > 0)
return (totalRows%pageSize == 0) ? totalRows / pageSize : totalRows / pageSize + 1;
else if(totalRows == 0)
return 0;
else
return 1;
}
}


GenericDAO.java
import java.io.Serializable;
import org.hibernate.criterion.Order;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.aais.core.utils.Page;

@Transactional(rollbackFor=Exception.class)
public interface GenericDAO<T, ID extends Serializable> {

/**
*
* @param id
* @param lock
* @return
*/
@Transactional(readOnly=true)
public T findById(ID id, boolean lock);

/**
*
* @param pageNo
* @param pageSize
* @param order
* @return
*/
@Transactional(readOnly=true)
public Page<T> findAll(int pageNo, int pageSize, Order order);

/**
*
* @param pageNo
* @param pageSize
* @param exampleInstance
* @param order
* @param excludeProperty
* @return
*/
@Transactional(readOnly=true)
public Page<T> findByExample(int pageNo, int pageSize, T exampleInstance, Order order, String[] excludeProperty);

/**
*
* @param pageNo
* @param pageSize
* @param queryName
* @param paramNames
* @param values
* @return
*/
@Transactional(readOnly=true)
public Page<T> findByHQL(int pageNo, int pageSize, String queryName, String[] paramNames, Object[] values);

/**
*
* @param entity
* @return
*/
@Transactional(propagation=Propagation.REQUIRED)
public T saveOrUpdate(T entity);

/**
*
* @param entity
*/
@Transactional(propagation=Propagation.REQUIRED)
public void delete(T entity);
}


GenericDAOHibernateImpl.java
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.LockMode;
import org.hibernate.Session;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import com.aais.core.dao.GenericDAO;
import com.aais.core.utils.Page;

public abstract class GenericDAOHibernateImpl<T, ID extends Serializable> extends HibernateDaoSupport implements GenericDAO<T, ID> {

private Class<T> persistentClass;
private final static Log logger = LogFactory.getLog(GenericDAOHibernateImpl.class);

/**
*
*/
@SuppressWarnings("unchecked")
public GenericDAOHibernateImpl() {
this.persistentClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}

/**
*
* @return
*/
public Class<T> getPersistentClass() {
return persistentClass;
}

/**
* (pageNo > 0 && pageSize > 0) paginate
* (pageNo < 0 && pageSize < 0) don't paginate
*/
public Page<T> findAll(int pageNo, int pageSize, Order order) {
return findByCriteria(pageNo, pageSize, order, (Criterion[])null);
}

/**
* (pageNo > 0 && pageSize > 0) paginate
* (pageNo < 0 && pageSize < 0) don't paginate
*/
@SuppressWarnings("unchecked")
protected Page<T> findByCriteria(int pageNo, int pageSize, Order order, Criterion...criterion) {
Criteria crit = getCriteria();
if(criterion != null){
for (Criterion c : criterion) {
crit.add(c);
}
}
if(order != null)
crit.addOrder(order);
int rowCount = 0;
if(pageSize > 0){
rowCount = (Integer) ((Criteria) crit.setProjection(Projections.rowCount())).uniqueResult();
crit.setProjection(null);
if(pageNo < 1)
pageNo = 1;
((Criteria) crit).setFirstResult((pageNo - 1) * pageSize);
((Criteria) crit).setMaxResults(pageSize);
if(pageSize > rowCount)
pageSize = rowCount;
}
crit.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
List<T> list = crit.list();
return new Page<T>(pageNo, pageSize, list, rowCount);
}

/**
*
* @return
*/
protected Criteria getCriteria(){
Criteria crit = this.getSession().createCriteria(getPersistentClass());
return crit;
}

/**
* (pageNo > 0 && pageSize > 0) paginate
* (pageNo < 0 && pageSize < 0) don't paginate
*/
@SuppressWarnings("unchecked")
protected Page<T> findByCriteria(int pageNo, int pageSize, Criteria crit, Order order){
if(order != null)
crit.addOrder(order);
int rowCount = 0;
if(pageSize > 0){
rowCount = (Integer) ((Criteria) crit.setProjection(Projections.rowCount())).uniqueResult();
crit.setProjection(null);
if(pageNo < 1)
pageNo = 1;
((Criteria) crit).setFirstResult((pageNo - 1) * pageSize);
((Criteria) crit).setMaxResults(pageSize);
if(pageSize > rowCount)
pageSize = rowCount;
}
crit.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
List<T> list = crit.list();
return new Page<T>(pageNo, pageSize, list, rowCount);
}

/**
* (pageNo > 0 && pageSize > 0) paginate
* (pageNo < 0 && pageSize < 0) don't paginate
*/
@SuppressWarnings("unchecked")
public Page<T> findByExample(int pageNo, int pageSize, T exampleInstance, Order order, String[] excludeProperty) {
Criteria crit = getCriteria();
Example example = Example.create(exampleInstance).enableLike();
if(excludeProperty != null){
for (String exclude : excludeProperty) {
example.excludeProperty(exclude);
}
}
crit.add(example);
if(order != null)
crit.addOrder(order);
int rowCount = 0;
if(pageSize > 0){
rowCount = (Integer) ((Criteria) crit.setProjection(Projections.rowCount())).uniqueResult();
crit.setProjection(null);
if(pageNo < 1)
pageNo = 1;
((Criteria) crit).setFirstResult((pageNo - 1) * pageSize);
((Criteria) crit).setMaxResults(pageSize);
if(pageSize > rowCount)
pageSize = rowCount;
}
crit.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
List<T> list = crit.list();
return new Page<T>(pageNo, pageSize, list, rowCount);
}

/**
*
*/
public T findById(ID id, boolean lock) {
T entity = null;
if (lock)
entity = (T) getHibernateTemplate().get(getPersistentClass(), id, LockMode.UPGRADE);
else
entity = (T) getHibernateTemplate().get(getPersistentClass(), id);
if(entity == null){
logger.warn("Could not find out " + getPersistentClass().getName() + " instance with id=" + id);
}else{
if(logger.isDebugEnabled())
logger.debug("Getting "+getPersistentClass().getName()+" instance with id: " + id);
}
return entity;
}

/**
*
* @param queryName
* @param paramNames
* @param values
* @return
*/
private Query createQuery(String queryName, String[] paramNames, Object[] values) {
Query query = getSession().getNamedQuery(queryName);
query.setCacheable(true);
if ((paramNames != null) && (values != null)) {
for (int i = 0; i < paramNames.length; i++) {
query.setParameter(paramNames[i], values[i]);
}
}
return query;
}

/**
* (pageNo > 0 && pageSize > 0) paginate
* (pageNo < 0 && pageSize < 0) don't paginate
* don't use for full table paginate, that would be low efficency
*/
@SuppressWarnings("unchecked")
public Page<T> findByHQL(int pageNo, int pageSize, String queryName, String[] paramNames, Object[] values){
long rowCount = 0;
Query query = createQuery(queryName, paramNames, values);
if(pageSize > 0){
rowCount = ((Long)getRowCountByHQL(appendRowCountHQL(queryName), paramNames, values)).longValue();
if(pageNo < 1)
pageNo = 1;
if(pageSize > rowCount)
pageSize = (int)rowCount;
query.setFirstResult((pageNo - 1) * pageSize);
query.setMaxResults(pageSize);
}
List<T> list = query.list();
return new Page<T>(pageNo, pageSize, list, (int)rowCount);
}

/**
*
* @param queryName
* @return
*/
private String appendRowCountHQL(String queryName){
return queryName + ".Count";
}

/**
*
* @param queryName
* @param paramNames
* @param values
* @return
*/
@SuppressWarnings("unchecked")
private Object getRowCountByHQL(final String queryName, final String[] paramNames, final Object[] values) {
return ((Object) getHibernateTemplate().execute(new HibernateCallback() {
public Object doInHibernate(Session session) throws HibernateException {
Query query = createQuery(queryName, paramNames, values);
if (paramNames != null && values != null && paramNames.length == values.length) {
for (int i = 0, max = paramNames.length; i < max; i++) {
query.setParameter(paramNames[i], values[i]);
}
}
return query.uniqueResult();
}
}));
}


/**
*
*/
public T saveOrUpdate(T entity) throws DataIntegrityViolationException{
getHibernateTemplate().saveOrUpdate(entity);
return entity;
}

/**
*
*/
public void delete(T entity) {
getHibernateTemplate().delete(entity);
}
}

1 comment:

  1. Cool work, but please look at http://opensource.atlassian.com/projects/hibernate/browse/HB-520

    DISTINCT_ROOT_ENTITY could not be used with setMaxResults.
    and http://floledermann.blogspot.com/2007/10/solving-hibernate-criterias-distinct.html

    for workaround.

    ReplyDelete