前段时间在做模糊查询,并利用数据库分页,DAO用hibernate实现,刚开始的时候
根据业务层的数据,拼hql语句进行查询,且不说要进行一些if判断,单从结构上来说,
底层的数据访问层依赖于业务层或者表现层了.
  比如说,我想查询姓王的员工,年龄大于30岁的,在DAO显然要name like ‘%王‘ and age >30,如果业务发生变化,查询与王**姓名相同,年龄等于30的,那就改hql语句吧,
name =‘王**‘ and age =30,数据访问层依赖于业务层,觉得不合理.
 Hibernate3提供一个新的东西,DetachedCriteria可以脱离session分离查询条件,也就是把查询条件通过业务层或表现层传入,数据访问层进行数据操作,解决了数据访问层依赖于业务层的问题.
实现细节:
结合泛型设计DAO
| 
 public interface DAO<PO,ID extends java.io.Serializable> { 
  public List<Workparam> findRowsByCriteria( DetachedCriteria detachedCriteria, int first, int max);      public int findCountByCriteria( DetachedCriteria detachedCriteria); 
} 
 | 
 
DAO的实现:
| 
 public class DAOHibernate<PO,ID extends java.io.Serializable> extends HibernateDaoSupport implements DAO<PO,ID> { 
   @SuppressWarnings("unchecked")  public List<PO> findRowsByCriteria(DetachedCriteria detachedCriteria, int first, int max) {   // TODO 自动生成方法存根   return getHibernateTemplate().findByCriteria(detachedCriteria, first, max); 
  
   public int findCountByCriteria(DetachedCriteria detachedCriteria) {   // TODO 自动生成方法存根   detachedCriteria.setProjection(Projections.rowCount());   System.out.println(getHibernateTemplate().findByCriteria(detachedCriteria));   return (Integer) getHibernateTemplate().findByCriteria(detachedCriteria).get(0);  } 
  } 
  
} 
 | 
 
具体实现的DAO:
| 
 public interface WorkparamDAO extends DAO<Workparam,Integer>{ 
} 
implements: 
public class WorkparamDAOHibernate extends DAOHibernate<Workparam,Integer> implements WorkparamDAO { 
} 
 | 
 
 
在利用spring管理DAOHibernate的时候,最好设置其abstract="true",即为抽象的,
实际上DAOHibernate在没有由子类参数化PO,ID类型之前,也是不能使用的.它也不知道PO,ID是什么类型,编译时不会报错,但运行的时候就会出错.
 
接下来做一个分页工具类,封装分页信息:
| 
 public class PageSplitUtil<E> {    //存放记录集合  private List<E> items;    //每页记录数  private int pageSize = Constants.PAGE_SIZE;    //总记录数  //初始化其值,为了判断缓存  private int totalCount=-1;    //分页索引数组  private int indexs[];    //开始索引值  private int firstIndex = Constants.FIRST_INDEX;    //当前页索引  private int currentIndex;    //是否有上一个索引  private boolean hasPreIndex;    //是否有下一个索引  private boolean hasNextIndex;    //上一页索引  private int preIndex;    //上一页索引  private int nextIndex;    //最后一页  private int lastIndex;    //小分页数  private int splitCount=Constants.SPLIT_COUNT;    //存放url  private String action; 
 //constructor  public PageSplitUtil(List<E> items,int current,int totalCount){   this.items = items;   this.totalCount = totalCount ;   this.currentIndex = current;   calculateTotalPage();  }      public PageSplitUtil(List<E> items,int current,int pageSize,int totalCount){   this.items = items;   this.pageSize = pageSize;   this.totalCount = totalCount;   this.currentIndex = current;   calculateTotalPage();  }    public int getCurrentIndex() {   return currentIndex;  } 
   public void setCurrentIndex(int currentIndex) {   this.currentIndex = currentIndex<1?1:currentIndex;  } 
   public boolean isHasNextIndex() {   return currentIndex>=lastIndex || lastIndex ==1 ?false:true;  } 
   public boolean isHasPreIndex() {   return currentIndex<=firstIndex ?false:true;  } 
   public int[] getIndexs() {   indexs = new int[lastIndex];   for (int i = 0; i < lastIndex; i++) {    indexs[i]=i+1;   }   return indexs;  } 
   public List<E> getItems() {   return items;  } 
   public void setItems(List<E> items) {   this.items = items;  } 
   public int getPageSize() {   return pageSize;  } 
    public int getFirstIndex() {   return firstIndex;  } 
    public int getTotalCount() {   return totalCount;  } 
    public int getLastIndex() { 
  return lastIndex;  } 
   public int getSplitCount() {   return splitCount;  }    public int getNextIndex() {   return currentIndex>=lastIndex ? lastIndex : currentIndex+1;  } 
   public int getPreIndex() {   return currentIndex<=firstIndex ? firstIndex : currentIndex-1;  }       public String getAction() {   return action;  } 
 public void setAction(String action) {   this.action = action;  } 
  //计算总页数  private void calculateTotalPage(){   if(totalCount>0){    lastIndex=(int) Math.ceil((double)totalCount/pageSize); 
  }else{    lastIndex =0;   }  } 
} 
 | 
 
 
把detachedCriteria查询条件在业务层实现
| 
 public PageSplitUtil findRowsByCriteria(Workparam param,int current,int pageSize) {   // TODO 自动生成方法存根   DetachedCriteria dc = DetachedCriteria.forClass(param.getClass());   dc.add(Restrictions.like("workName", param.getWorkName(), MatchMode.ANYWHERE));    dc.add(Restrictions.eq("workState", param.getWorkState())).addOrder(Order.desc("workId")); 
  int totalCount=workparamDAO.findCountByCriteria(dc);      PageSplitUtil ps=new PageSplitUtil<Workparam>(list,current,pageSize,totalCount);   return ps;  } 
 | 
 
由于detachedCriteria是根据Criteria的扩展实现,Critetia利用Express来封装了判断的条件以及sql函数(count(),avg()等).在hibernate3中,利用Restrictions封装逻辑判断条件,如like,eq,between等,用Projections封装了sql函数,如count(),avg()等.
MatchMode为匹配方式:START,前边匹配,END,后边匹配,ANYWHERE前后匹配,EXACT完全匹配四种方式.
detachedCriteria支持数据库查询时的排序,addOrder(Order.asc("按属性排序"));
detachedCriteria的多表关联查询,dc.createAlias("workType", "t").add(Restrictions.like("t.typeName", "早班类"));workType作为Workparam的关联属性,在映射文件中会根据其关系对相关联的表进行模糊查询.
 
为了方便在每个页面使用,用自定义标签显示分页信息:
| 
 public class PageSplitTag extends BodyTagSupport {  private static final long serialVersionUID = -1411548776656848362L; 
 private String action=""; 
 private PageSplitUtil pageUtil;    public PageSplitTag() {   // TODO 自动生成构造函数存根  }    public int doStartTag() throws JspException{   try {    String txt=getText().toString();    pageContext.getOut().write(txt);    return this.SKIP_BODY;   } catch (IOException e) {    // TODO 自动生成 catch 块    throw new JspException("输出错误"+e.getMessage());   }  }      private StringBuffer getText() {   // TODO 自动生成方法存根   if(pageUtil==null) return new StringBuffer("初始化分页失败");      //判断action是否设置   if(action==null||"".equals(action)){    action=pageUtil.getAction();   }      StringBuffer txt=new StringBuffer("<form action="+action+" method=\"get\" name=\"splitfrm\">");   txt.append("<td height=\"27\" align=\"center\">共有<font color=\"red\">"      +pageUtil.getTotalCount()+"</font>条记录  ");   txt.append("<font color=\"red\">"+pageUtil.getPageSize()+"</font>条/页  ");   //判断是否有上一页   if(pageUtil.isHasPreIndex()){    txt.append("<a href="+action+"?page="+pageUtil.getFirstIndex()+">首页</a> ");    txt.append("<a href="+action+"?page="+pageUtil.getPreIndex()+"><<<</a>  ");   }   else{    txt.append("<font color=\"#999966\">首页</font> ");    txt.append("<font color=\"#999966\"><<<</font> ");   }   //小的分页   int indexs[]=pageUtil.getIndexs();   int currentIndex=pageUtil.getCurrentIndex();   int splitCount=pageUtil.getSplitCount();   int count=0;   //每次显示从当前页开始的后splitCount页   if(pageUtil.getLastIndex()-(currentIndex+splitCount)>=0){    count=currentIndex+splitCount;   }else{    count=indexs.length;   }    for (int i = currentIndex-1; i < count; i++) {      if(currentIndex == indexs[i] ){       txt.append("<font color=\"red\">"+currentIndex+"</font> ");      }else{       txt.append("<a href="+action+"?page="+indexs[i]+">"+indexs[i]+"</a> ");     }    }   txt.append(" ");      //判断是否还有下一页   if(pageUtil.isHasNextIndex()){    txt.append("<a href="+action+"?page="+pageUtil.getNextIndex()+">>>></a> ");    txt.append("<a href="+action+"?page="+pageUtil.getLastIndex()+">尾页</a> ");   }   else{    txt.append("<font color=\"#999966\">>>></font> ");    txt.append("<font color=\"#999966\">尾页</font> ");   }   //页数选择   txt.append("GO<select name=\"page\" onchange=\"javascript:document.splitfrm.submit();\">");   for (int i = 0; i < indexs.length; i++) {    if(currentIndex == indexs[i]){     txt.append("<option value="+indexs[i]+" selected=\"selected\">"+indexs[i]+"</option>");    }else{     txt.append("<option value="+indexs[i]+">"+indexs[i]+"</option>");    }   }   txt.append("</select>页</form></td>");   return txt;  } 
  
setter.. 
} 
 | 
 
然后在每个页面用自定义标签就可以了:
| 
 <%@ taglib uri="http://www.oamanager.com/mytag" prefix="tag"%> 
<tag:splitPage action="${splitList.action}" pageUtil="${splitList}"></tag:splitPage> 
 | 
 
 设置action请求的路径和分页信息就可以了.
 
缺点:
1:在DAO接口的设计中依赖于hibernate的实现.如果改用ibatis或ojb,需更改接口,不符合接口的设计原则.
2:分页的自定义Tag显示形式固定,如果利用DIV+CSS,或者XML+CSS的话,表现形式更加灵活.
      
  thanks
