本来是打算拿到it168投稿的!!那边的速度太慢了!先贴出来先!!
分页是一种常用的页面数据显示技术,分页能够通过减少页面数据处理量从而提高了系统的性能。分页应该是做WEB开发必须掌握的一个小技术。而分页却是复杂的,倒不是它的技术有多复杂;而是有太多的重复代码,这些代码都难以重用。能不能实现一个通用的分页框架?每次只需要去覆写一两个方法,通过少量的代码就能实现分页的功能?
一般分页应该要具有的功能有
1. 灵活的设置分页大小。可以动态的设置分页大小,而不是写死到代码中。
2. 自动计算总页数。根据分页大小和总记录数自动计算总页数。
3. 获得当前页的页号。
4. 获得当前页的总记录数。一般是最后一页的时候可能会小于分页大小。
5. 判断当前页是否为第一页。
6. 判断当前页是否为最后一页。
7. 判断当前页是否有上一页。
8. 判断当前页是否有下一页。
9. 获得当前页的数据列表。
10. 获得当前页的第一条记录的索引号
11. 获得当前页的最后一条记录的索引号。
二、常用的分页技术
目前常用的分页技术有两种:
1. 第一次访问是读取所有记录,放入session中,然后每次从session对象中读取当前页的数据
2. 每次都访问数据库,从数据库中读取当前页的记录。
这两种方法都各有优缺点,当数据量比较少时,第一种方法无疑是要快一些,因为减少与数据库的连接访问。而当数据量比较大时,比如查询结果可能会是上万条,那么内存的开销是十分大的,放到session中还有一个问题是能不能及时的清除无用的对象。而且这么大数据量在网络中传输也会使系统变得很慢。
第二种方法就是专门解决这个问题的,它每次访问数据库,只读取当前页所需的记录,大大的减少网络传输量;它不会把页数据放到session中,大大提高服务器的性能。
所以第二种方式要优于第一种方法。Session不要乱用,要用也仅仅是存放一些公共变量,相对于占用空间比较少的对象。不适合存放大量的数据,否则在很多个用户同时访问时那么系统会很慢,因为服务器内存被销耗的很厉害.
三、通用分页框架需要解决的问题
作为一个通用分页框架,
1. 应该不依赖于任何其它框架
2. 应该支持多种数据库
3. 应该可以应用于任何web框架中,如:struts,spring等。
4. 应该把数据访问的具体实现留给用户去实现。
5. 应该实现关键的算法和过程,如:计算总页数,所需的实始化动作。
6. 应该减化Contrller控制器的代码,以往的分页技术在Contrller中存在太多的if…else代码。十分难懂,应该由一个辅助类来实现。
7. 应该减化jsp页面的代码,页面应该没有任何与分页相关的计算。应该由分页对象来实现。
8. 应该支持两种分页方式,采用session或不采用session由用户控制。
四、具体实现
1.通用分页接口。定义接口可以有更多不同的实现,接口只声明了分页应该具有的公共行为。
ViewPage.java
/**
* 分页接口
* @author ex_yuanguangdong
*
*/
public interface ViewPage {
/**
* 获取总页数
* @return 总页数
*/
public int getPageCount();
/**
* 获得页面大小
* @return 页面大小
*/
public int getPageSize();
/**
* 设置页面大小
* @param size
*/
public void setPageSize(int size);
/**
* 获得当前页数据
* @return 数据列表
*/
public List getPageData();
/**
* 获得当前页索引号
* @return 当前页索引号
*/
public int getPageIndex();
/**
* 获得当前页记录总数
* @return 当前页记录总数
*/
public int getPageRows();
/**
* 是否有下一页
* @return
*/
public boolean getHashNextPage();
/**
* 是否有上一页
* @return
*/
public boolean getHashPreviousPage();
/**
* 转到尾页
*
*/
public void gotoLastPage();
/**
* 转到首页
*
*/
public void gotoFirstPage();
/**
* 是否首页
* @return
*/
public boolean isFirstPage();
/**
* 是否尾页
* @return
*/
public boolean isLastPage();
/**
* 转到上一页
*
*/
public void gotoPreviousPage();
/**
* 转到下一页
*
*/
public void gotoNextPage();
/**
* 转到指定页面,pageIndex小于1时,转到第一页;pageIndex大于总页数时,转到最尾页
* @param pageIndex 指定的页号
*/
public void gotoPage(int pageIndex);
/**
* 获取当前页第一条记录的记录号
* @return int 当前页第一条记录的记录号
*/
public int getPageFirstRecord();
/**
* 获取当前页最后一条记录的记录号
* @return int 当前页最后一条记录的记录号
*/
public int getPageLastRecord();
}
2. 分页抽象实现类,实现关键的算法
AbstractViewPage.java
/**
* 分页默认抽象实现
* 初始时,分页类有下列默认值:
* 分页大小为-1,为不分页;
* 总页数为1页
* 当前页为第一页
* 总记录数为0条
* 当前页数据列表为没有任何记录的列表
* @author ex_yuanguangdong
*
*/
public abstract class AbstractViewPage implements ViewPage {
//-----------------------------------------
//私有静态常量
//-----------------------------------------
private static final int DEFAULT_PAGE_INDEX = 1;
private static final int DEFALT_PAGE_COUNT = 1;
private static final int DEFAULT_PAGE_SIZE = -1;
private static final int DEFAULT_ROWS = 0;
//-----------------------------------------
//私有成员变量
//-----------------------------------------
/**当前页索引号**/
private int pageIndex = DEFAULT_PAGE_INDEX;
/**总页数**/
private int pageCount =DEFALT_PAGE_COUNT;
/**分页大小**/
private int pageSize = DEFAULT_PAGE_SIZE ;
/**数据总记录数**/
private int rows = DEFAULT_ROWS;
//------------------------------------------
//本地成员变量getter,setter方法
//------------------------------------------
/**
* 设置新页号,只有大于等于1而且小于等于总页数并且不为当前页时,才允许设置
* @param pageIndex The pageIndex to set.
*/
private void setPageIndex(int newPageIndex) {
if( newPageIndex >= this.DEFAULT_PAGE_INDEX && newPageIndex <= this.getPageCount() && newPageIndex != this.pageIndex) {
this.pageIndex = newPageIndex;
}
}
/**
* @return Returns the rows.
*/
private int getRows() {
return rows;
}
/**
* @param rows The rows to set.
*/
private void setRows(int rows) {
this.rows = rows;
}
/**
* @param pageCount The pageCount to set.
*/
private void setPageCount(int pageCount) {
this.pageCount = pageCount;
}
//--------------------------------------
//实现Page接口方法
//--------------------------------------
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#getPageData()
*/
public List getPageData() {
List pageList =null;
//获得当前页数据
pageList = this.pageList(this.getPageFirstRecord(), this.getPageRows());
//保证不返回null
if(pageList == null) {
pageList =new ArrayList();
}
return pageList;
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#getPageIndex()
*/
public int getPageIndex() {
return this.pageIndex;
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#isFirstPage()
*/
public boolean isFirstPage() {
return this.DEFAULT_PAGE_INDEX ==this.pageIndex;
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#isLastPage()
*/
public boolean isLastPage() {
//当前页索引为总页数时为最后一页
return this.pageIndex == this.pageCount;
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#getHashNextPage()
*/
public boolean getHashNextPage() {
//当前页索引号小于总页数
return this.pageIndex < this.pageCount ;
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#getHashPreviousPage()
*/
public boolean getHashPreviousPage() {
//当前页索引号大于默认的初始页号,这里为1
return this.pageIndex > this.DEFAULT_PAGE_INDEX ;
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#getPageCount()
*/
public int getPageCount() {
return this.pageCount;
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#getPageSize()
*/
public int getPageSize() {
return this.pageSize;
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#getPageRows()
*/
public int getPageRows() {
//当页面大小为-1 时,返回总记录数
if(this.DEFAULT_PAGE_SIZE == this.pageSize ) {
return this.rows;
}
//不为最后一页时,返回pageSize
if(!this.isLastPage()) {
return this.pageSize;
}
//最后一页时
return this.rows - (this.pageSize * (this.pageCount -1));
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#getPageFirstRecord()
*/
public int getPageFirstRecord() {
//页大小为-1 时
if(this.DEFAULT_PAGE_SIZE== this.pageSize ) {
return 0;
}
return (this.pageIndex -1)* this.pageSize;
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#getPageLastRecord()
*/
public int getPageLastRecord() {
//页大小为-1时,返回总记录数
if(this.DEFAULT_PAGE_SIZE == this.pageSize) {
return this.rows;
}
return this.getPageFirstRecord() + this.getPageRows() ;
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#gotoFirstPage()
*/
public void gotoFirstPage() {
this.gotoPage(this.DEFAULT_PAGE_INDEX);
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#gotoLastPage()
*/
public void gotoLastPage() {
this.gotoPage(this.getPageCount());
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#gotoPreviousPage()
*/
public void gotoPreviousPage() {
this.gotoPage(this.getPageIndex() -1);
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#gotoNextPage()
*/
public void gotoNextPage() {
this.gotoPage(this.getPageIndex() + 1);
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#gotoPage(int)
*/
public void gotoPage(int newPageIndex) {
if( newPageIndex >= this.DEFAULT_PAGE_INDEX && newPageIndex <= this.getPageCount() ) {
this.setPageIndex(newPageIndex);
}
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#setPageSize(int)
*/
public void setPageSize(int size) {
if(size < 1) {
size = 1;
}
this.pageSize = size;
//进行初始化
this.doInit();
}
//-----------------------------------
//辅助方法
//-----------------------------------
/**
* 分页初始化方法,为了保证总是能正确的初始化,所以声明为final ,为了让子类可以调用声明为protected
*
*/
protected final void doInit() {
int rows = 0;
//获得总记录数
rows= totalRows();
//设置总记录数
this.setRows(rows);
//设置新的总页数
//计算并设置总页数
int pages = calculatePageCount();
this.setPageCount(pages);
//转到第一页
this.gotoPage(this.DEFAULT_PAGE_INDEX);
onInit();
}
/**
* 记算总页数
* @return 总页数
*/
private int calculatePageCount() {
//总记录数为0条,则返回的总页数为1
if(this.getRows() == 0) {
return this.DEFALT_PAGE_COUNT;
}
//如果页面大小为-1,则返回的总页数为1
if(this.DEFAULT_PAGE_SIZE == this.getPageSize() ) {
return this.DEFALT_PAGE_COUNT;
}
return this.getRows() / this.getPageSize() + ( this.getRows() % this.getPageSize() ==0 ? 0 :1);
}
/**
* 获得总记录数,调用queryTotalRows(),将异常封装为non-checked 异常
* @return 总记录数
* @throws ApplicationRuntimeException
*/
private int totalRows() throws ApplicationRuntimeException{
try{
return queryTotalRows();
}
catch(Exception ex){
throw new ApplicationRuntimeException(ex);
}
}
/**
* 获得当前页数据,调用queryPageList()方法,将异常封装为non-checked异常
* @param startRow 开始记录号
* @param rowCount 记录总数
* @return 当前页数据
* @throws ApplicationRuntimeException
*/
private List pageList(int startRow, int rowCount) throws ApplicationRuntimeException{
try{
return queryPageList(startRow, rowCount);
}catch(Exception ex){
throw new ApplicationRuntimeException(ex);
}
}
//-----------------------------------------
//子类实现的方法
//-----------------------------------------
/**
* 初始化附加方法,由子类扩展
*/
protected void onInit() {
}
/**
* 查询获得总记录数,由子类具体实现
* @return 总记录数
* @throws Exception
*/
protected abstract int queryTotalRows() throws Exception;
/**
* 查询当前页数据,从startRow 开始的rowCount条记录
* @param startRow 开始记录号
* @param rowCount 记录总数
* @return 当前页数据
* @throws Exception
*/
protected abstract List queryPageList(int startRow, int rowCount) throws Exception;
}
3. 分页辅助类
ViewPageHelper.java/**
* 分页辅助类,用于减化Controller中的代码
* @author yuanguangdong
* date: Oct 22, 2006
*/
public class ViewPageHelper {
private static final int FIRST_PAGE_VALUE = 1;
private static final int PREVIOUS_PAGE_VALUE = 2;
private static final int NEXT_PAGE_VALUE = 3;
private static final int LAST_PAGE_VALUE = 4;
private static final int SPECIAL_PAGE_VALUE = 5;
public static final String FIRST_PAGE = "FIRST_PAGE";
public static final String PREVIOUS_PAGE = "PREVIOUS_PAGE";
public static final String NEXT_PAGE = "NEXT_PAGE";
public static final String LAST_PAGE = "LAST_PAGE";
public static final String SPECIAL_PAGE = "SPECIAL_PAGE";
/**分页动作参数名**/
public static final String PAGE_ACTION = "page_action";
/**分页对象属性名**/
public static final String SESSION_PAGE = "session_page";
/**页号参数名**/
public static final String PAGE_NO = "page_no";
private static Map actionMap = new HashMap();
static {
actionMap.put(FIRST_PAGE, new Integer(FIRST_PAGE_VALUE));
actionMap.put(PREVIOUS_PAGE, new Integer(PREVIOUS_PAGE_VALUE));
actionMap.put(NEXT_PAGE, new Integer(NEXT_PAGE_VALUE));
actionMap.put(LAST_PAGE, new Integer(LAST_PAGE_VALUE));
actionMap.put(SPECIAL_PAGE, new Integer(SPECIAL_PAGE_VALUE));
}
/**
* 执行分页动作
* @param page 分页对象
* @param action 分页动作参数
* @param pageIndex 页号
*/
public static void doAction(ViewPage page, String action, int pageIndex) {
int actionIndex = 0;
if (page == null) {
throw new NullPointerException("Page对象null");
}
if (action == null || "".equals(action)) {
throw new IllegalArgumentException("无效的分页动作参数null");
}
action = action.toUpperCase();
if (!actionMap.containsKey(action)) {
throw new UnsupportedOperationException("不支持的分页动作参数:" + action);
}
Integer index = (Integer) actionMap.get(action);
actionIndex = index.intValue();
switch (actionIndex) {
case FIRST_PAGE_VALUE:
page.gotoFirstPage();
break;
case PREVIOUS_PAGE_VALUE:
page.gotoPreviousPage();
break;
case NEXT_PAGE_VALUE:
page.gotoNextPage();
break;
case LAST_PAGE_VALUE:
page.gotoLastPage();
break;
case SPECIAL_PAGE_VALUE:
page.gotoPage(pageIndex);
}
}
public static void doAction(ViewPage page, String action){
doAction(page, action, 1);
}
}
五、应用通用分页框架
1.继承AbstractViewPage类,实现queryPageList(int startRow, int endRow)和
queryTotalRows()方法。
protected int queryTotalRows() throws Exception
获得查询条件的总记录数
protected List queryPageList(int startRow, int rowCount)
用于查询指定范围的数据。startRow为开始记录号, rowCount为查询的记录数
queryPageList(0,20)为查询从第一条开始的20条记录。
使用Ibatis可以由queryPageList调用queryForList()方法。
/**
* 用户信息分页内部类
* @author yuanguangdong
* date: Oct 22, 2006
*/
class UserInfoPage extends AbstractViewPage{
//------------------------------------------------
//实现AbstractViewPage抽象类的抽象方法
//------------------------------------------------
/**
* @see com.prs.application.ehld.web.mvc.AbstractViewPage#getPageDate(int, int)
*/
protected List queryPageList(int startRow, int endRow) throws Exception {
return sampleAction.getUserInfoList(startRow, endRow);
}
/**
* @see com.prs.application.ehld.web.mvc.AbstractViewPage#getRows()
*/
protected int queryTotalRows() throws Exception {
return sampleAction.getUserCount();
}
}
3. 在Contrller中的实现
public ModelAndView listUser(HttpServletRequest request,
HttpServletResponse response) throws Exception {
String pageAction =
RequestUtils.getStringParameter(request,ViewPageHelper.PAGE_ACTION);
Integer pageIndex =
RequestUtils.getIntParameter(request,ViewPageHelper.PAGE_NO);
//声明分页对象
ViewPage userPage =
(ViewPage) request.getSession().getAttribute(ViewPageHelper.SESSION_PAGE);
//第一次请求
if(pageAction == null || userPage == null){
//构建一个新的分页对象
userPage = new UserInfoPage();
//设置分页大小
userPage.setPageSize(2);
}else{
if(ViewPageHelper.SPECIAL_PAGE.equals(pageAction)){
//如果页数为空,则默认为1
if (pageIndex == null)
pageIndex = new Integer(1);
ViewPageHelper.doAction(userPage,pageAction,pageIndex.intValue());
}else{
ViewPageHelper.doAction(userPage,pageAction);
}
}
//从分页对象中获得当前页数据
List userInfoList = userPage.getPageData();
ModelAndView mav = new ModelAndView(userInfoListView);
mav.addObject(this.userInfoListKey,userInfoList);
request.getSession().setAttribute(ViewPageHelper.SESSION_PAGE,userPage);
return mav;
}
4. jsp页面实现
<%@ page contentType="text/html;charset=utf-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jstl/fmt" %>
<%@ taglib prefix="tiles" uri="http://jakarta.apache.org/struts/tags-tiles" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<html>
<head>
<title>显示所有员工</title>
<SCRIPT language="javaScript">
function pageNoChange(pageNo){
location.href= "ehld.sample.getuserinfolist.do?page_action=SPECIAL_PAGE&page_no="+pageNo.value;
}
</SCRIPT>
</head>
<body>
<table width="80%" border="0">
<tr>
<td bgcolor="#F0FEFF"><div align="left"> 用户列表</div></td>
</tr>
</table>
<br>
<input name="adduser" type="submit" id="adduser" value="新增用户" οnclick="location.href='ehld.sample.edituserinfo.do'">
<table width="80%" border="0">
<tr bgcolor="#58ED64">
<th width="25%">id</th>
<th width="34%">姓名</th>
<th colspan="2">操作</th>
</tr>
<c:forEach items="${userInfoList}" var="userInfoDTO">
<tr bgcolor="#D6EBF8">
<td><c:out value="${userInfoDTO.userID}"/></td>
<td><c:out value="${userInfoDTO.userName}"/></td>
<td width="21%"><a href="ehld.sample.edituserinfo.do?id=<c:out value='${userInfoDTO.userID}'/>">编辑</a></td>
<td width="20%"><a href="#">删除</a></td>
</tr>
</c:forEach>
</table>
<c:if test="${session_page.firstPage}">
首页
</c:if>
<c:if test="${! session_page.firstPage}">
<a href="ehld.sample.getuserinfolist.do?page_action=FIRST_PAGE">首页</a>
</c:if>
<c:if test="${! session_page.hashPreviousPage}">
上一页
</c:if>
<c:if test="${session_page.hashPreviousPage}">
<a href="ehld.sample.getuserinfolist.do?page_action=PREVIOUS_PAGE">上一页</a>
</c:if>
<c:if test="${!session_page.hashNextPage}">
下一页
</c:if>
<c:if test="${session_page.hashNextPage}">
<a href="ehld.sample.getuserinfolist.do?page_action=NEXT_PAGE">下一页</a>
</c:if>
<c:if test="${session_page.lastPage}">
尾页
</c:if>
<c:if test="${!session_page.lastPage}">
<a href="ehld.sample.getuserinfolist.do?page_action=LAST_PAGE">尾页</a>
</c:if>
共有<c:out value="${session_page.pageCount}" />页,第
<select name = "pageNo" onChange = "javaScript:pageNoChange(this);">
<c:forEach begin="1" end = "${session_page.pageCount}" var = "pageIndex">
<option value="<c:out value='${pageIndex}'/>" <c:if test = "${pageIndex ==session_page.pageIndex }">selected</c:if>>
<c:out value="${pageIndex}"/>
</option>
</c:forEach>
</select>
页
</body>
</html>