Web 应用中分页的 hibernate 和 struts 实现
By guipei 2005-2-19
概述
在众多的web应用中,不可缺少的一项功能便是对记录结果的分页。试象一下,如果没有分页,你在Google中查找了一个网页,结果有5万个记录,假设一次全部发送给你,服务器讲会有多大的压力,你的客户机有会有多达的压力。
把结果分页实际上需要3个需求:
1. 需要知道结果的总体数量。
2. 应用适当的方法取出所需要的结果。
3. 把得到的结果积传送给客户。
好在优秀的hibernate已经完美的完成了其中的第二项工作,通过Query 接口的setFirstResult方法可以设置你所需要的开始记录,setMaxResults方法则可以返回你所需要的记录数量。本文下面会结合使用hibernate的这个功能,加上作者提供的一个分页类,完美实现通用的分页方法。
准备
在本例当中,采用hibernate 、struts1.1、tomcat5、以及mysql 4.0.20 。当然,相应的环境你也可以进行适当调整。
本例当中使用 hibernate中默认的cat表,结构如下:
+--------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +--------+-------------+------+-----+---------+-------+ | cat_id | varchar(32) | | PRI | | | | name | varchar(16) | | | | | | sex | char(1) | | | | | | weight | double | | | 0 | | +--------+-------------+------+-----+---------+-------+ |
使用映射工具生成cat 表的映射类,加入到hibernate的配置文件当中。
实现
我们来查看一下,在action 中实现的代码:
public ActionForward execute( ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) {
String url = "success";
// get request page String currentPage = request.getParameter("currentPage"); if(currentPage==null){ currentPage="1"; }
// prepare the query Session session = null; try { session = HibernateUtils.currentSession(); Query queryList = session.createQuery("from Cat"); Query queryCount = session.createQuery("select count(*) from Cat");
PageSplit pageSplit = new PageSplit();
pageSplit.setPageSize(5);
List catList = pageSplit.doPageSplit(queryList, queryCount, Integer.parseInt(currentPage)); request.setAttribute("catList", catList); request.setAttribute("pageSplit", pageSplit);
} catch (Exception e) { logger.error(e); request.setAttribute("errInfo", e); url = "fail";
}finally{ HibernateUtils.closeSession();
}
return mapping.findForward(url);
} |
再来看一下在jps 表示层中的实现:
<%@ page contentType="text/html; charset=gb2312" %> <%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %> <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<!DOCTYPE HTML PUBLIC "-//W 3C //DTD HTML 4.01 Transitional//EN"> <html> <head> <title>My JSP 'cat_list.jsp' starting page</title>
</head>
<body>
<table width="95%" align="center" border="1">
<tr> <td width="160" height="22"><div align="center">id</div></td> <td width="150"><div align="center">name</div></td> <td width="10"><div align="center">sex</div></td> <td width="100"><div align="center">weight</div></td> </tr>
<logic:iterate id="cat" name="catList"> <tr> <td height="22"> <bean:write name="cat" property="catId"/> </td> <td> <bean:write name="cat" property="name"/> </td> <td> <bean:write name="cat" property="sex"/> </td> <td> <bean:write name="cat" property="weight"/> </td> </tr> </logic:iterate>
</table>
<table width="95%" align="center"> <TR> <TD width="100" > 共计 <bean:write name="pageSplit" property="totalPage" /> 页 </TD> <TD width="100" > 当前第 <bean:write name="pageSplit" property="currentPage" /> 页 </TD> <TD width="100" > </TD>
<logic:equal name="pageSplit" property="hasPrevious" value="true"> <TD width="40" > <a href = 'catList.do?<bean:write name="pageSplit" property="firstQuery" />' > 首页 </a> </TD> <TD width="40" > <a href = 'catList.do?<bean:write name="pageSplit" property="previousQuery" />' > 前页 </a> </TD> </logic:equal> <logic:equal name="pageSplit" property="hasPrevious" value="false"> <TD width="40" > 首页 </TD> <TD width="40" > 前页 </TD> </logic:equal>
<logic:equal name="pageSplit" property="hasNext" value="true"> <TD width="40" > <a href = 'catList.do?<bean:write name="pageSplit" property="nextQuery" />' > 下页 </a> </TD> <TD width="40" > <a href = 'catList.do?<bean:write name="pageSplit" property="lastQuery" />' > 尾页 </a> </TD> </logic:equal> <logic:equal name="pageSplit" property="hasNext" value="false"> <TD width="40" > 下页 </TD> <TD width="40" > 尾页 </TD> </logic:equal>
</TR> </table>
</body> </html>
|
Ok,怎么样,很方便的处理吧,采用这个方式,去掉jsp里面的java代码,把分页所需要的工作放在PageSplit类中,然后通过 bean tag 在jsp中显示。
分析
下面我们主要类分析一下PageSplit类。
PageSplit类在这里充当了两个功能,一个是分页方法的实现, doPageSplit方法;另外一个功能便是在完成分页功能后存放相关信息到它的相关属性当作,以便传送到显示层。
我们来看一下doPageSplit方法。这里一个有3个重载方法,参看如下:
1. public List doPageSplit(Query queryList, Query queryCount, int currentPage)
2. public List doPageSplit(Query queryList, String sqlCount, int currentPage)
3. public List doPageSplit(Query queryList, int rowCount, int currentPage)
为了实现分页的操作,必须具备两个条件。就是上面提到的hibernate需要的两个参数,请求的开始记录,以及查询结果的合计数量。上面的方法主要解决了处理合计数量的问题,通过3个不同方式,Query方式, Sql方式,以及直接数量方式。
下面是本例当作使用的方式,创建两个查询作为参数传入:
Query queryList = session.createQuery("from Cat");
Query queryCount = session.createQuery("select count(*) from Cat");
第二种方式用作比较特殊的情况,hibernate的子查询功能并不能完全兼容sql方式,例如,标准的sql子查询是可以支持from 子查询以及使用group 方式的查询,所以在取得数量方便,添加了直接sql方式取得。
第三中方式其实也是第一和第二中方式调用的函数,在它们中准备好数据后,调用第三个方法返回结果,但是在不需要精确查询以及高性能要求条件下,也可以把数量存放到系统环境中,直接作为调用。
在PageSplit类中,其他的方法用作设置页数大小,查询条件,查询结果等等,属于bean的一些方式,没有什么特别之处,相信你也可以看懂。
也许应该提供一个单独的List doPageSplit(Query queryList int currentPage) 方法,利用QueryList生成查询结果数量,也许你可以完成这个功能。
结束
如果你需要全部运行代码实例,给我个mail吧,我会发一个给你。
My Mail : guipei.java@gmail.com。
附录
参看PageSplit类代码
/* * Created on 2005-1-25 * */ package com.guipei.hibernate;
import java.sql.Connection; import java.sql.ResultSet; import java.sql.Statement; import java.util.Iterator; import java.util.List;
import org.apache.log4j.Logger; import net.sf.hibernate.Query;
/** * * 分页封装类,用作和hibernate、struts的结合 * <p> * 使用方法参看 PageSplitTest.java 类 * <p> * create date 2005-01-25 * @author guip * */ public class PageSplit {
private static Logger logger = Logger.getLogger(PageSplit.class);
/**页大小描述每页有多少行*/ private int pageSize = 20;
/**是否有上一页*/ private boolean hasPrevious;
/**是否有下一页*/ private boolean hasNext;
/**总行数 */ private int totalRow;
/**总页数 */ private int totalPage;
/**当前页码*/ private int currentPage = 1;
/** 上页页码 */ private int previousPage;
/** 下页页码 */ private int nextPage;
/** 查询条件 */ private String searchValue;
private String nextQuery;
private String previousQuery;
private String lastQuery;
private String firstQuery;
/** * @return Returns the firstQuery. */ public String getFirstQuery() { return firstQuery; } /** * @return Returns the lastQuery. */ public String getLastQuery() { return lastQuery; } /** * @return Returns the nextQuery. */ public String getNextQuery() { return nextQuery; } /** * @return Returns the previousQuery. */ public String getPreviousQuery() { return previousQuery; } /** * @return Returns the nextPage. */ public int getNextPage() { return nextPage; } /** * @param nextPage The nextPage to set. */ public void setNextPage(int nextPage) { this.nextPage = nextPage; } /** * @return Returns the previousPage. */ public int getPreviousPage() { return previousPage; } /** * @param previousPage The previousPage to set. */ public void setPreviousPage(int previousPage) { this.previousPage = previousPage; }
/** * @return Returns the currentPage. */ public int getCurrentPage() { return currentPage; } /** * @param currentPage The currentPage to set. */ public void setCurrentPage(int currentPage) { this.currentPage = currentPage; } /** * @return Returns the hasNext. */ public boolean isHasNext() { return hasNext; } /** * @param hasNext The hasNext to set. */ public void setHasNext(boolean hasNext) { this.hasNext = hasNext; } /** * @return Returns the hasPrevious. */ public boolean isHasPrevious() { return hasPrevious; } /** * @param hasPrevious The hasPrevious to set. */ public void setHasPrevious(boolean hasPrevious) { this.hasPrevious = hasPrevious; } /** * @return Returns the pageSize. */ public int getPageSize() { return pageSize; } /** * @param pageSize The pageSize to set. */ public void setPageSize(int pageSize) { this.pageSize = pageSize; } /** * @return Returns the totalPage. */ public int getTotalPage() { return totalPage; } /** * @param totalPage The totalPage to set. */ public void setTotalPage(int totalPage) { this.totalPage = totalPage; } /** * @return Returns the totalRow. */ public int getTotalRow() { return totalRow; } /** * @param totalRow The totalRow to set. */ public void setTotalRow(int totalRow) { this.totalRow = totalRow; }
/** * * * @return */ public List doPageSplit(Query queryList, Query queryCount, int currentPage) throws Exception { // return list List list = null; String strInfo = null;
try {
// init information Iterator it = queryCount.iterate(); Object data = it.next();
// int rowCount = ((Integer) queryCount.iterate().next()).intValue() ; int rowCount = ((Integer)data).intValue();
// split list = doPageSplit(queryList, rowCount, currentPage);
} catch (Exception e) { logger.error(e); throw e; }
return list; }
/** * * * @return */ public List doPageSplit(Query queryList, String sqlCount, int currentPage) throws Exception {
List list = null; String strInfo = null;
Connection con = null; Statement stmt = null; ResultSet rs = null;
try {
// init information con = HibernateUtils.currentSession().connection(); stmt = con.createStatement(); rs = stmt.executeQuery(sqlCount);
rs.next(); int rowCount = rs.getInt(1) ;
// split list = doPageSplit(queryList, rowCount, currentPage);
} catch (Exception e) { logger.error(e); throw e; } finally { try { rs.close(); stmt.close(); // con.close(); HibernateUtils.closeSession(); } catch (Exception e) { logger.error(e); } }
return list;
}
/** * @param queryList * @param rowCount * @param currentPage * @return * @throws Exception */ public List doPageSplit(Query queryList, int rowCount, int currentPage) throws Exception {
List list = null; String strInfo = null;
int pageCount = rowCount / this.pageSize; if (rowCount % this.pageSize != 0) { pageCount++; }
// determine if exist currentPage if ( currentPage > pageCount ){ strInfo = "请求页数大于合计页数; 合计为:" + pageCount + "请求页数为: " + currentPage; throw new Exception(strInfo); }
// set bean info this.currentPage = currentPage; this.totalPage = pageCount; this.totalRow = rowCount;
if (currentPage > 1 ){ this.hasPrevious = true; this.previousPage = currentPage - 1; this.firstQuery = "currentPage=1&" + this.searchValue ; this.previousQuery = "currentPage=" + previousPage + "&" + searchValue ;
}else{ this.hasPrevious = false; }
if (currentPage != pageCount ){ this.hasNext = true; this.nextPage = currentPage + 1; this.nextQuery = "currentPage=" + nextPage + "&" + searchValue ; this.lastQuery = "currentPage=" + totalPage + "&" + searchValue ;
}else{ this.hasNext = false; }
int firstRow = ( this.currentPage - 1) * this.pageSize ; queryList.setFirstResult( firstRow ); logger.debug("set first row :" + firstRow ); queryList.setMaxResults( this.pageSize ); logger.debug("set Max return rows :" + this.pageSize ); list = queryList.list();
return list;
}
/** * @return Returns the searchValue. */ public String getSearchValue() { return searchValue; }
/** * @param searchValue The searchValue to set. */ public void setSearchValue(String searchValue) { this.searchValue = searchValue; }
} |