这两天,有一些时间,研究一下springside的源代码,结果感觉不错,于是对自己做过一些代码做了一些优化与重构.
分页的代码如下:
(说明:这里的代码是我参考了springside的源代码后作了一些比较小的改动,这里没有给出完整的例子,只是讨论一下核心的代码)
这里想说的代码主要是泛型DAO层的应用与分页的写法.
分页,采用了hibernate的一些API来分页,这里同时采用了两种分页方式,CriteriaPage分页方式,适用于多表单时查询后分页用的,而第二种方式是采用Hql语句查询后分页的.代码如下:
CriteriaPage.java文件:
package org.mmc.commons;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.Criteria;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.Projection;
import org.hibernate.criterion.Projections;
import org.hibernate.impl.CriteriaImpl;
import org.hibernate.impl.CriteriaImpl.OrderEntry;
import org.mmc.utils.BeanUtils;
/**
* 使用Hql查询的的分页查询类.
* 支持执行Count查询取得总记录条数
* 本类参考了springside项目的分页设计
*/
public class CriteriaPage {
/**
* 得到一个PageInstance实例,.
*/
public static Page getPageInstance(Criteria criteria, int pageNo, int pageSize) {
return CriteriaPage.getPageInstanceByCount(criteria, pageNo, pageSize);
}
/**
* 以查询Count的形式获取totalCount的函数
*/
protected static Page getPageInstanceByCount(Criteria criteria, int pageNo, int pageSize) {
CriteriaImpl impl = (CriteriaImpl) criteria;
//先把Projection和OrderBy条件取出来,清空两者来执行Count操作
Projection projection = impl.getProjection();
List<OrderEntry> orderEntries;
try {
orderEntries = (List) BeanUtils.getPrivateProperty(impl, "orderEntries");
BeanUtils.setPrivateProperty(impl, "orderEntries", new ArrayList());
}
catch (Exception e) {
throw new InternalError(" Runtime Exception impossibility throw ");
}
//执行查询
int totalCount = (Integer) criteria.setProjection(Projections.rowCount()).uniqueResult();
//将之前的Projection和OrderBy条件重新设回去
criteria.setProjection(projection);
if (projection == null) {
criteria.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
}
try {
BeanUtils.setPrivateProperty(impl, "orderEntries", orderEntries);
} catch (Exception e) {
throw new InternalError(" Runtime Exception impossibility throw ");
}
return getPageResult(criteria, totalCount, pageNo, pageSize);
}
/**
* 取得totalCount后,根据pageNo和PageSize, 执行criteria的分页查询,取得Page变量
*/
private static Page getPageResult(Criteria criteria, int totalCount, int pageNo, int pageSize) {
if (totalCount < 1) return new Page();
int startIndex = Page.getStartOfPage(pageNo, pageSize);
List list = criteria.setFirstResult(startIndex).setMaxResults(pageSize).list();
return new Page(startIndex, totalCount, pageSize, list);
}
}
HqlPage.java文件的代码
package org.mmc.commons;
import java.util.List;
import org.hibernate.Query;
/**
* 使用Hql查询的的分页查询类.
* 支持执行getCount查询取得总记录条数
* 本类参考了springside项目的分页设计
*
* @author lighter
* @see org.mmc.commons.Page
*/
public class HqlPage {
public static Page getPageInstanceByCount(Query query, int pageNo, int pageSize, int totalCount) {
return getPageResult(query, totalCount, pageNo, pageSize);
}
private static Page getPageResult(Query q, int totalCount, int pageNo, int pageSize) {
if (totalCount < 1) return new Page();
int startIndex = Page.getStartOfPage(pageNo, pageSize);
List list = q.setFirstResult(startIndex).setMaxResults(pageSize).list();
return new Page(startIndex, totalCount, pageSize, list);
}
}
Page.java文件的源代码如下:
package org.mmc.commons;
import java.util.ArrayList;
/**
* 分页对象. 包含数据及分页信息.
*
* @author lighter 天马行空
*/
public class Page implements java.io.Serializable {
/**
* 当前页第一条数据的位置,从0开始
*/
private int start;
/**
* 每页的记录数
*/
private int pageSize = Constants.DEFAULT_PAGE_SIZE;
/**
* 当前页中存放的记录
*/
private Object data;
/**
* 总记录数
*/
private int totalCount;
/**
* 构造方法,只构造空页
*/
public Page() {
this(0, 0, Constants.DEFAULT_PAGE_SIZE, new ArrayList());
}
/**
* 默认构造方法
*
* @param start
* 本页数据在数据库中的起始位置
* @param totalSize
* 数据库中总记录条数
* @param pageSize
* 本页容量
* @param data
* 本页包含的数据
*/
public Page(int start, int totalSize, int pageSize, Object data) {
this.pageSize = pageSize;
this.start = start;
this.totalCount = totalSize;
this.data = data;
}
/**
* 取数据库中包含的总记录数
*/
public int getTotalCount() {
return this.totalCount;
}
/**
* 取总页数
*/
public int getTotalPageCount() {
if (totalCount % pageSize == 0)
return totalCount / pageSize;
else
return totalCount / pageSize + 1;
}
/**
* 取每页数据容量
*/
public int getPageSize() {
return pageSize;
}
/**
* 当前页中的记录
*/
public Object getResult() {
return data;
}
/**
* 取当前页码,页码从1开始
*/
public int getCurrentPageNo() {
return (start / pageSize) + 1;
}
/**
* 是否有下一页
*/
public boolean hasNextPage() {
return (this.getCurrentPageNo() < this.getTotalPageCount() - 1);
}
/**
* 是否有上一页
*/
public boolean hasPreviousPage() {
return (this.getCurrentPageNo() > 1);
}
/**
* 获取任一页第一条数据的位置,每页条数使用默认值
* 关键字设为pretected
*/
protected static int getStartOfPage(int pageNo) {
return getStartOfPage(pageNo, Constants.DEFAULT_PAGE_SIZE);
}
/**
* 获取任一页第一条数据的位置,startIndex从0开始
* 关键字设为pretected
*/
protected static int getStartOfPage(int pageNo, int pageSize) {
return (pageNo - 1) * pageSize;
}
}
分页的改造已经完成,现在重构一下DAO层的写法,利用了spring对hibernate支持的一些的API.
DAO层:
AbstractHibernateDao.java的源代码
package org.mmc.dao;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Criteria;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.mmc.utils.GenericsUtils;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 纯Hibernate Entity DAO基类.
* 通过泛型,子类无需扩展任何函数即拥有完整的CRUD操作.
*
*/
abstract public class AbstractHibernateDao<T> extends HibernateDaoSupport {
protected Log logger = LogFactory.getLog(getClass());
/**
* Dao所管理的Entity类型.
*/
protected Class<T> entityClass;
/**
* 取得entityClass的函数.
* JDK1.4不支持泛型的子类可以抛开Class<T> entityClass,重新实现此函数达到相同效果。
*/
protected Class getEntityClass() {
return entityClass;
}
/**
* 在构造函数中将泛型T.class赋给entityClass
*/
public AbstractHibernateDao() {
entityClass = GenericsUtils.getGenericClass(getClass());
}
public T get(Serializable id) {
T o = (T) getHibernateTemplate().get(getEntityClass(), id);
if (o == null)
throw new ObjectRetrievalFailureException(getEntityClass(), id);
return o;
}
public List<T> getAll() {
return getHibernateTemplate().loadAll(getEntityClass());
}
public void save(Object o) {
getHibernateTemplate().saveOrUpdate(o);
}
public void removeById(Serializable id) {
remove(get(id));
}
public void remove(Object o) {
getHibernateTemplate().delete(o);
}
public List<T> find(String hsql, Object... values) {
if (values.length == 0)
return getHibernateTemplate().find(hsql);
else
return getHibernateTemplate().find(hsql, values);
}
/**
* 根据属性名和属性值查询对象.
*
* @return 符合条件的唯一对象
*/
public T findUniqueBy(String name, Object value) {
Criteria criteria = getSession().createCriteria(getEntityClass());
criteria.add(Restrictions.eq(name, value));
return (T) criteria.uniqueResult();
}
/**
* 根据属性名和属性值查询对象.
*
* @return 符合条件的对象列表
*/
public List<T> findBy(String name, Object value) {
Assert.hasText(name);
Criteria criteria = getSession().createCriteria(getEntityClass());
criteria.add(Restrictions.eq(name, value));
return criteria.list();
}
/**
* 根据属性名和属性值以Like AnyWhere方式查询对象.
*/
public List<T> findByLike(String name, String value) {
Assert.hasText(name);
Criteria criteria = getSession().createCriteria(getEntityClass());
criteria.add(Restrictions.like(name, value, MatchMode.ANYWHERE));
return criteria.list();
}
/**
* 根据Map中过滤条件进行查询.
*
* @param filter 过滤条件.
* @param criteriaSetup 将Map中条件转换为criteria的call back类
*/
public List<T> findBy(Map filter, CriteriaSetup criteriaSetup) {
Criteria criteria = getEntityCriteria();
if (!CollectionUtils.isEmpty(filter)) {
criteriaSetup.setup(criteria, filter);
}
return criteria.list();
}
/**
* 函数作用同{@link #findBy(Map,CriteriaSetup)}
* 如果不需要分页,子类可直接重载此函数.
*/
public List<T> findBy(Map filter) {
return findBy(filter, getDefaultCriteriaSetup());
}
protected CriteriaSetup getDefaultCriteriaSetup() {
return new CriteriaSetup() {
public void setup(Criteria criteria, Map filter) {
if (filter != null && !filter.isEmpty()) {
Set keys = filter.keySet();
for (Object key : keys) {
String value = (String) filter.get(key);
if (StringUtils.isNotBlank(value))
criteria.add(Restrictions.eq((String) key, value));
}
}
}
};
}
/**
* 取得Entity的Criteria.
*/
protected Criteria getEntityCriteria() {
return getSession().createCriteria(getEntityClass());
}
/**
* 构造Criteria的排序条件默认函数.可供其他查询函数调用
*
* @param criteria Criteria实例.
* @param sortMap 排序条件.
* @param entity entity对象,用于使用反射来获取某些属性信息
*/
protected void sortCriteria(Criteria criteria, Map sortMap, Object entity) {
if (!sortMap.isEmpty()) {
for (Object o : sortMap.keySet()) {
String fieldName = o.toString();
String orderType = sortMap.get(fieldName).toString();
//处理嵌套属性如category.name,modify_user.id,暂时只处理一级嵌套
if (fieldName.indexOf('.') != -1) {
String alias = StringUtils.substringBefore(fieldName, ".");
String aliasType = alias;
try {
aliasType = PropertyUtils.getProperty(entity, alias).getClass().getSimpleName();
} catch (Exception e) {
logger.error("Get property" + alias + " error");
}
criteria.createAlias(aliasType, alias);
}
if ("asc".equalsIgnoreCase(orderType)) {
criteria.addOrder(Order.asc(fieldName));
} else {
criteria.addOrder(Order.desc(fieldName));
}
}
}
}
/**
* 判断对象某列的值在数据库中不存在重复
*
* @param names 在POJO里相对应的属性名,列组合时以逗号分割<br>
* 如"name,loginid,password"
*/
public boolean isNotUnique(Object entity, String names) {
Assert.hasText(names);
Criteria criteria = getSession().createCriteria(entity.getClass()).setProjection(Projections.rowCount());
String[] nameList = names.split(",");
try {
for (String name : nameList) {
criteria.add(Restrictions.eq(name, PropertyUtils.getProperty(entity, name)));
}
String keyName = getSessionFactory().getClassMetadata(entity.getClass()).getIdentifierPropertyName();
if (keyName != null) {
Object id = PropertyUtils.getProperty(entity, keyName);
//如果是update,排除自身
if (id != null)
criteria.add(Restrictions.not(Restrictions.eq(keyName, id)));
}
}
catch (Exception e) {
logger.error(e.getMessage());
return false;
}
return ((Integer) criteria.uniqueResult()) > 0;
}
}
再建一个文件:
BaseHibernateDao.java
package org.mmc.dao;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.criterion.CriteriaSpecification;
import org.mmc.commons.CriteriaPage;
import org.mmc.commons.HqlPage;
import org.mmc.commons.Page;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
/**
* Hibernate Entity Dao基类.
* 扩展的分页函数,具体的分页代码在Page类中.
*
* @see AbstractHibernateDao
* @see HqlPage
* @see CriteriaPage
*/
abstract public class BaseHibernateDao<T> extends AbstractHibernateDao<T> {
/**
* 根据Map中过滤条件和分页参数进行分页查询.
*
* @param filterMap 过滤条件.
* @param pageNo 当前页码
* @param pageSize 每页显示记录数.
*/
public Page findBy(Map filterMap, int pageNo, int pageSize) {
return findBy(filterMap, null, pageNo, pageSize);
}
/**
* 根据Map中过滤条件,排序条件和分页参数进行分页查询.
*
* @param filterMap 过滤条件.
* @param orderMap 排序条件.
* @param pageNo 当前页码
* @param pageSize 每页显示记录数.
*/
public Page findBy(Map filterMap, Map orderMap, int pageNo, int pageSize, CriteriaSetup criteriaSetup) {
Criteria criteria = getSession().createCriteria(getEntityClass());
if (!CollectionUtils.isEmpty(filterMap)) {
try {
criteriaSetup.setup(criteria, filterMap);
} catch (Exception e) {
}
}
if (!CollectionUtils.isEmpty(orderMap))
sortCriteria(criteria, orderMap, null);
criteria.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
return pagedQuery(criteria, pageNo, pageSize);
}
public Page findBy(Map filterMap, Map orderMap, int pageNo, int pageSize) {
return findBy(filterMap, orderMap, pageNo, pageSize, getDefaultCriteriaSetup());
}
/**
* Criteria分页查询,采用了getCount模式
*/
public Page pagedQuery(Criteria criteria, int pageNo, int pageSize) {
return CriteriaPage.getPageInstance(criteria, pageNo, pageSize);
}
/**
* HQL分页查询,可以指定具体的模式,
* 采用getCount方式,须在此层完成hsql的转换与查询。
* 注意参数Object...args的应用,可以在查询的设置查询条件用的(JDK5.0语法)
*/
public Page pagedQuery(String hql, int pageNo, int pageSize, Object... args) {
Assert.hasText(hql);
Query query = getSession().createQuery(hql);
for (int i = 0; i < args.length; i++) {
query.setParameter(i, args[i]);
}
String countQueryString = " select count (*) " + removeSelect(removeOrders(hql));
List countlist = getHibernateTemplate().find(countQueryString, args);
int totalCount = (Integer) countlist.get(0);
return HqlPage.getPageInstanceByCount(query, pageNo, pageSize, totalCount);
}
/**
* 去除select 子句,未考虑union的情况
*/
private static String removeSelect(String hql) {
Assert.hasText(hql);
int beginPos = hql.toLowerCase().indexOf("from");
Assert.isTrue(beginPos != -1, " hql : " + hql + " must has a keyword 'from'");
return hql.substring(beginPos);
}
/**
* 去除orderby 子句
*/
private static String removeOrders(String hql) {
Assert.hasText(hql);
Pattern p = Pattern.compile("order//s*by[//w|//W|//s|//S]*", Pattern.CASE_INSENSITIVE);
Matcher m = p.matcher(hql);
StringBuffer sb = new StringBuffer();
while (m.find()) {
m.appendReplacement(sb, "");
}
m.appendTail(sb);
return sb.toString();
}
}
BaseHibernateDAO负责抽象一个Entity 的CRUD函数。
所有子类只要声明如下,即拥有如下函数:
public class UserDAO extends BaseHibernateDao<User>
-
protected final Log logger
-
public Object get(Serializable id)
-
public List getAll()
-
public void save(Object o)
-
public void remove(Serializable id)
-
public void remove(Object o)
-
public List find(String hql)
-
public List find(String hql,Object value)
-
public List find(String hql,object[])
-
public Object findBy(String name,Object value)
-
public List findAllBy(String name,Object value)
-
public List findAllByLike(String name,Object value)
-
public List findAllBy(Map filter)
上面只是简单的说了一下的springside项目源代码的一些优雅的地方,如果有需要的要求的话,我会贴出完整的一个简单例子的demo的,包括分页的与泛型DAO的应用.