使用工具类实现通用分页处理

原文发表在JavaResearch.org

http://www.misslog.com/blog/detail.asp?blog_id=6&content_id=3954 
目前比较广泛使用的分页方式是将查询结果缓存在HttpSession或有状态bean中,翻页的时候从缓存中取出一页数据显示。这种方法有两个主要的缺点:一是用户可能看到的是过期数据;二是如果数据量非常大时第一次查询遍历结果集会耗费很长时间,并且缓存的数据也会占用大量内存,效率明显下降。
  其它常见的方法还有每次翻页都查询一次数据库,从ResultSet中只取出一页数据(使用rs.last();rs.getRow()获得总计录条数,使用rs.absolute()定位到本页起始记录)。这种方式在某些数据库(如oracle)的JDBC实现中差不多也是需要遍历所有记录,实验证明在记录数很大时速度非常慢。
  至于缓存结果集ResultSet的方法则完全是一种错误的做法。因为ResultSet在Statement或Connection关闭时也会被关闭,如果要使ResultSet有效势必长时间占用数据库连接。

  因此比较好的分页做法应该是每次翻页的时候只从数据库里检索页面大小的块区的数据。这样虽然每次翻页都需要查询数据库,但查询出的记录数很少,网络传输数据量不大,如果使用连接池更可以略过最耗时的建立数据库连接过程。而在数据库端有各种成熟的优化技术用于提高查询速度,比在应用服务器层做缓存有效多了。

在oracle数据库中查询结果的行号使用伪列ROWNUM表示(从1开始)。例如select * from employee where rownum<10 返回前10条记录。但因为rownum是在查询之后排序之前赋值的,所以查询employee按birthday排序的第100到120条记录应该这么写:
        select * from (select my_table.*, rownum as my_rownum from (select name, birthday from employee order by birthday) my_table where rownum <120) where my_rownum>=100

  mySQL可以使用LIMIT子句:
    select name, birthday from employee order by birthday LIMIT 99,20
  DB2有rownumber()函数用于获取当前行数。
  SQL Server没研究过,可以参考这篇文章:http://www.csdn.net/develop/article/18/18627.shtm

  在Web程序中分页会被频繁使用,但分页的实现细节却是编程过程中比较麻烦的事情。大多分页显示的查询操作都同时需要处理复杂的多重查询条件,sql语句需要动态拼接组成,再加上分页需要的记录定位、总记录条数查询以及查询结果的遍历、封装和显示,程序会变得很复杂并且难以理解。因此需要一些工具类简化分页代码,使程序员专注于业务逻辑部分。下面是我设计的两个工具类:
  PagedStatement 封装了数据库连接、总记录数查询、分页查询、结果数据封装和关闭数据库连接等操作,并使用了PreparedStatement支持动态设置参数。
  RowSetPage 参考PetStore的page by page iterator模式, 设计RowSetPage用于封装查询结果(使用OracleCachedRowSet缓存查询出的一页数据,关于使用CachedRowSet封装数据库查询结果请参考JSP页面查询显示常用模式)以及当前页码、总记录条数、当前记录数等信息, 并且可以生成简单的HTML分页代码。
  PagedStatement 查询的结果封装成RowsetPage。


  下面是简单的使用示例

public RowSetPage getEmployee(String gender, int pageNo) throws Exception{String sql=;PagedStatement pst =new PagedStatementOracleImpl(sql,  pageNo, 5);pst.setString(1, gender);return pst.executeQuery();}…int pageNo;try{pageNo = Integer.parseInt(request.getParameter() );}catch(Exception ex){pageNo=1;}String gender = request.getParameter( );request.setAttribute(, myBean.getEmployee(gender, pageNo) );…<%@ page import = %>…<script language=>function doQuery(){form1.actionType.value=;form1.submit();}</script>…<form name=form1 method=get><input type=hidden name=actionType>性别:<input type=text name=gender size=1 value=gender><input type=button value= οnclick=>
<%RowSetPage empPage = (RowSetPage)request.getAttribute();if (empPage == null ) empPage = RowSetPage.EMPTY_PAGE;
%>…<table  cellspacing= width=><tr> <td>ID</td> <td>代码</td> <td>用户名</td> <td>姓名</td>  </tr>
<%javax.sql.RowSet empRS = (javax.sql.RowSet) empPage.getRowSet();if (empRS!=null) while (empRS.next() ) {
%><tr>  <td><%= empRS.getString()%></td> <td><%= empRS.getString()%></td>  <td><%= empRS.getString()%></td> <td><%= empRS.getString()%></td>  </tr>
<%}
%><tr>
<%%><td colspan=4><%= empPage .getHTML(, )%></td></tr></table></form>

  效果如图:

  因为分页显示一般都会伴有查询条件和查询动作,页面应已经有校验查询条件和提交查询的javascript方法(如上面的doQuery),所以RowSetPage.getHTML()生成的分页代码在用户选择新页码时直接回调前面的处理提交查询的javascript方法。注意在显示查询结果的时候上次的查询条件也需要保持,如<input type=text name=gender size=1 value="<%=request.getParameter("gender")%>">。同时由于页码的参数名可以指定,因此也支持在同一页面中有多个分页区。
  另一种分页代码实现是生成每一页的URL,将查询参数和页码作为QueryString附在URL后面。这种方法的缺陷是在查询条件比较复杂时难以处理,并且需要指定处理查询动作的servlet,可能不适合某些定制的查询操作。
  如果对RowSetPage.getHTML()生成的默认分页代码不满意可以编写自己的分页处理代码,RowSetPage提供了很多getter方法用于获取相关信息(如当前页码、总页数、 总记录数和当前记录数等)。
  在实际应用中可以将分页查询和显示做成jsp taglib, 进一步简化JSP代码,屏蔽Java Code。

附:分页工具类的源代码, 有注释,应该很容易理解。

1.Page.java
2.RowSetPage.java(RowSetPage继承Page)
3.PagedStatement.java
4.PagedStatementOracleImpl.java(PagedStatementOracleImpl继承PagedStatement)

您可以任意使用这些源代码,但必须保留author evan_zhao@hotmail.com字样

package page;import java.util.List;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;public  class Page implements java.io.Serializable {public static final Page EMPTY_PAGE = new Page();public static final int  DEFAULT_PAGE_SIZE = 20;public static final  int MAX_PAGE_SIZE = 9999;private int myPageSize = DEFAULT_PAGE_SIZE;private int start;private int avaCount,totalSize;private Object data;private int currentPageno;private int totalPageCount;protected Page(){this.init(0,0,0,DEFAULT_PAGE_SIZE,new Object());}protected void init(int start, int avaCount, int totalSize, int pageSize, Object data){this.avaCount =avaCount;this.myPageSize = pageSize;this.start = start;this.totalSize = totalSize;this.data=data;if (avaCount>totalSize) {}this.currentPageno = (start -1)/pageSize +1;this.totalPageCount = (totalSize + pageSize -1) / pageSize;if (totalSize==0 && avaCount==0){this.currentPageno = 1;this.totalPageCount = 1;}}public  Object getData(){return this.data;}public int getPageSize(){return this.myPageSize;}public boolean hasNextPage() {return (this.getCurrentPageNo()<this.getTotalPageCount());}public boolean hasPreviousPage() {return (this.getCurrentPageNo()>1);}public int getStart(){return start;}public int getEnd(){int end = this.getStart() + this.getSize() -1;if (end<0) {end = 0;}return end;}public int getStartOfPreviousPage() {return Math.max(start-myPageSize, 1);}public int getStartOfNextPage() {return start + avaCount;}public static int getStartOfAnyPage(int pageNo){return getStartOfAnyPage(pageNo, DEFAULT_PAGE_SIZE);}public static int getStartOfAnyPage(int pageNo, int pageSize){int startIndex = (pageNo-1) * pageSize + 1;if ( startIndex < 1) startIndex = 1;return startIndex;}public int getSize() {return avaCount;}public int getTotalSize() {return this.totalSize;}public int getCurrentPageNo(){return  this.currentPageno;}public int getTotalPageCount(){return this.totalPageCount;}public String getHTML(String queryJSFunctionName, String pageNoParamName){if (getTotalPageCount()<1){return +pageNoParamName+;}if (queryJSFunctionName == null || queryJSFunctionName.trim().length()<1) {queryJSFunctionName = ;}if (pageNoParamName == null || pageNoParamName.trim().length()<1){pageNoParamName = ;}String gotoPage = +queryJSFunctionName;StringBuffer html = new StringBuffer();html.append().append().append(gotoPage).append().append(  ).append(  ).append(pageNoParamName).append().append(  ).append(pageNoParamName).append().append(  ).append(queryJSFunctionName).append().append(  ).append(  ).append(  ).append(queryJSFunctionName).append().append(  ).append(pageNoParamName).append().append(  ).append(  ).append(  ).append(  ).append(  );html.append( ).append( ).append( );html.append(   ).append( getTotalPageCount() ).append( ).append(  ) .append(getStart()).append().append(getEnd()).append().append(this.getTotalSize()).append().append( ).append( );if (hasPreviousPage()){html.append( ).append(gotoPage).append() .append(getCurrentPageNo()-1) .append( );}html.append(  ).append(   ).append(pageNoParamName).append().append(gotoPage).append();String selected = ;for(int i=1;i<=getTotalPageCount();i++){if( i == getCurrentPageNo() )selected = ;else selected = ;html.append( ).append(i).append().append(selected).append().append(i).append();}if (getCurrentPageNo()>getTotalPageCount()){html.append( ).append(getCurrentPageNo()).append().append(getCurrentPageNo()).append();}html.append( );if (hasNextPage()){html.append( ).append(gotoPage).append().append((getCurrentPageNo()+1)) .append( );}html.append( );return html.toString();}
}package page;import javax.sql.RowSet;public class RowSetPage extends Page {private javax.sql.RowSet rs;public static final RowSetPage EMPTY_PAGE = new RowSetPage();public RowSetPage(){this(null, 0,0);}public RowSetPage(RowSet crs, int start, int totalSize) {this(crs,start,totalSize,Page.DEFAULT_PAGE_SIZE);}public RowSetPage(RowSet crs, int start, int totalSize, int pageSize) {try{int avaCount=0;if (crs!=null) {crs.beforeFirst();if (crs.next()){crs.last();avaCount = crs.getRow();}crs.beforeFirst();}rs = crs;super.init(start,avaCount,totalSize,pageSize,rs);}catch(java.sql.SQLException sqle){throw new RuntimeException(sqle.toString());}}public javax.sql.RowSet getRowSet(){return rs;}}package page;import foo.DBUtil;import java.math.BigDecimal;
import java.util.List;
import java.util.Iterator;
import java.util.Collections;import java.sql.Connection;
import java.sql.SQLException;
import java.sql.ResultSet;
import java.sql.Statement;
import java.sql.PreparedStatement;
import java.sql.Timestamp;
import javax.sql.RowSet;public abstract class PagedStatement {public final static int MAX_PAGE_SIZE = Page.MAX_PAGE_SIZE;protected String countSQL, querySQL;protected int pageNo,pageSize,startIndex,totalCount;protected javax.sql.RowSet rowSet;protected RowSetPage rowSetPage;private List boundParams;public PagedStatement(String sql){this(sql,1,MAX_PAGE_SIZE);}public PagedStatement(String sql, int pageNo){this(sql, pageNo, Page.DEFAULT_PAGE_SIZE);}public PagedStatement(String sql, int pageNo, int pageSize){this.pageNo = pageNo;this.pageSize = pageSize;this.startIndex = Page.getStartOfAnyPage(pageNo, pageSize);this.boundParams = Collections.synchronizedList(new java.util.LinkedList());this.countSQL =  + sql +;this.querySQL = intiQuerySQL(sql, this.startIndex, pageSize);}protected abstract  String intiQuerySQL(String sql, int startIndex, int size);public void setObject(int index, Object obj) throws SQLException{BoundParam bp = new BoundParam(index, obj);boundParams.remove(bp);boundParams.add( bp);}public void setObject(int index, Object obj, int targetSqlType) throws SQLException{BoundParam bp = new BoundParam(index, obj, targetSqlType);boundParams.remove(bp);boundParams.add(bp );}public void setObject(int index, Object obj, int targetSqlType, int scale) throws SQLException{BoundParam bp = new BoundParam(index, obj, targetSqlType, scale) ;boundParams.remove(bp);boundParams.add(bp);}public void setString(int index, String str)throws SQLException{BoundParam bp = new BoundParam(index, str)  ;boundParams.remove(bp);boundParams.add(bp);}public void setTimestamp(int index, Timestamp timestamp)throws SQLException{BoundParam bp = new BoundParam(index, timestamp)  ;boundParams.remove(bp);boundParams.add( bp );}public void setInt(int index, int value)throws SQLException{BoundParam bp =  new BoundParam(index, new Integer(value))  ;boundParams.remove(bp);boundParams.add( bp );}public void setLong(int index, long value)throws SQLException{BoundParam bp =  new BoundParam(index, new Long(value))  ;boundParams.remove(bp);boundParams.add( bp );}public void setDouble(int index, double value)throws SQLException{BoundParam bp =  new BoundParam(index, new Double(value))   ;boundParams.remove(bp);boundParams.add( bp);}public void setBigDecimal(int index, BigDecimal bd)throws SQLException{BoundParam bp =   new BoundParam(index, bd )   ;boundParams.remove(bp);boundParams.add( bp);}private  void setParams(PreparedStatement pst) throws SQLException{if (pst==null || this.boundParams==null || this.boundParams.size()==0 ) return ;BoundParam param;for (Iterator itr = this.boundParams.iterator();itr.hasNext();){param = (BoundParam) itr.next();if  (param==null) continue;if (param.sqlType == java.sql.Types.OTHER){pst.setObject(param.index, param.value);}else{pst.setObject(param.index, param.value, param.sqlType, param.scale);}}}public  RowSetPage executeQuery() throws SQLException{System.out.println();Connection conn = DBUtil.getConnection();PreparedStatement pst = null;ResultSet rs = null;try{pst = conn.prepareStatement(this.countSQL);setParams(pst);rs =pst.executeQuery();if (rs.next()){totalCount = rs.getInt(1);} else {totalCount = 0;}rs.close();pst.close();if (totalCount < 1 ) return RowSetPage.EMPTY_PAGE;pst = conn.prepareStatement(this.querySQL);System.out.println(querySQL);pst.setFetchSize(this.pageSize);setParams(pst);rs =pst.executeQuery();this.rowSet = populate(rs);rs.close();rs = null;pst.close();pst = null;this.rowSetPage = new RowSetPage(this.rowSet,startIndex,totalCount,pageSize);return this.rowSetPage;}catch(SQLException sqle){sqle.printStackTrace();throw sqle;}catch(Exception e){e.printStackTrace();throw new RuntimeException(e.toString());}finally{DBUtil.close(rs, pst, conn);}}protected abstract RowSet populate(ResultSet rs) throws SQLException;public javax.sql.RowSet getRowSet(){return this.rowSet;}public RowSetPage getRowSetPage() {return this.rowSetPage;}public void close(){}private class BoundParam {int index;Object value;int sqlType;int scale;public BoundParam(int index, Object value) {this(index, value, java.sql.Types.OTHER);}public BoundParam(int index, Object value, int sqlType) {this(index, value, sqlType, 0);}public BoundParam(int index, Object value, int sqlType, int scale) {this.index = index;this.value = value;this.sqlType = sqlType;this.scale = scale;}public boolean equals(Object obj){if (obj!=null && this.getClass().isInstance(obj)){BoundParam bp = (BoundParam)obj;if (this.index==bp.index) return true;}return false;}}}package page;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.sql.RowSet;
import oracle.jdbc.rowset.OracleCachedRowSet;public class PagedStatementOracleImpl extends PagedStatement {public PagedStatementOracleImpl(String sql){super(sql);}public PagedStatementOracleImpl(String sql, int pageNo){super(sql, pageNo);}public PagedStatementOracleImpl(String sql, int pageNo, int pageSize){super(sql, pageNo, pageSize);}protected String intiQuerySQL(String sql, int startIndex, int size){StringBuffer querySQL = new StringBuffer();if (size != super.MAX_PAGE_SIZE) {querySQL.append().append(  sql).append().append(startIndex + size).append().append(startIndex);} else {querySQL.append().append(sql).append().append().append(startIndex);}return querySQL.toString();}protected  RowSet populate(ResultSet rs) throws SQLException{OracleCachedRowSet ocrs = new OracleCachedRowSet();ocrs.populate(rs);return ocrs;}}

相关连接
  JSP页面查询显示常用模式,介绍查询结果集封装的几种常用模式。本程序使用了其中部分代码
  RowSet规范原来是JDBC(TM) 2.0 Optional Package的一部分,现在已经并入JDBC3.0规范,并且将成为J2SE1.5的组成部分。
  关于RowSet的实现各个数据库的jdbc driver应该都有提供,oracle实现可以到http://otn.oracle.com/software/tech/java/sqlj_jdbc/content.html下载(Additional RowSet support)
  Sun也提供了RowSet的参考实现,应该可以支持大多数数据库:http://java.sun.com/products/jdbc/download.html
  PetStore 是Sun关于J2EE设计模式的一个示例程序。

evan   2003-11-27 1:37:49  | 回复
本文引用通告(trackback)URL:

引用:JSP分页技术实现

本文发表在该站点
发布在 Java研究者组织 2003-12-24 16:34:28

2004-11-18

mysql实现:
protected String intiQuerySQL(String sql, int startIndex, int size){
        StringBuffer querySQL = new StringBuffer();
        querySQL.append("select * from (")
                    .append(  sql)
                    .append(") limit " + startIndex +", "+ size);
        return querySQL.toString();
    }
sql server实现:
"select top " + size +" * from tablename where id not in (select top " + (startIndex -1) + " id from tablename order by id desc) order by id desc"
不怎么好处理

转载于:https://www.cnblogs.com/sunsonbaby/archive/2005/01/31/99859.html

使用工具类实现通用分页处理相关推荐

  1. java rabbitmq 工具类_RabbitMq通用管理工具类

    import java.io.IOException; import java.util.concurrent.TimeoutException; import com.rabbitmq.client ...

  2. java 工具类sort_Java 通用排序工具类ListSortUtils

    场景:Java 类 需 重新排序,有时升序.有时倒叙,有时是多字段排序 代码: package GenericTest; import java.lang.reflect.InvocationTarg ...

  3. hibernate高级工具类(含分页)

    最近笔者在研究一套系统(jeeCMS)的源码,看到他对于hibernate的封装,顿时生出一种惊为天人的感觉,特与诸君分享. 这里面用到了两个基础知识,笔者在这里列一下,对于hibernate不熟悉读 ...

  4. JAVA使用POI写入excel 工具类【通用】

    使用写入excel工具类的例子 说明:改工具类是网上公开的,来源现在找不着了,我只是在原基础上修改了下,抽离了泛型.如有侵权,请通知博主 工具类代码: package com.system.util; ...

  5. java sqlite 工具类_Java 工具类 - JDBC通用操作基类 BaseDao

    封装了增删改查功能 适用于MySQL.Oracle.SQLServer.DB2.Sybase.JTDS.PostgreSql.SQLite.Derby.H2.HSQLDB.ODBC 等等数据库,有需要 ...

  6. java datatable用法_Java中实现DataTable工具类,并利用其实现简单分页控件。

    具体工具类代码,请见我上一个博客. 一.工具类的使用 1.1 DataTable工具类的使用 1.1.1DataTable简单解析 顾名思义,DataTable其实就是一张虚拟数据表,用于存储由数据库 ...

  7. php分页类示例下载,PHP 通用分页类的简单示例

    这篇文章主要为大家详细介绍了PHP 通用分页类的简单示例,具有一定的参考价值,可以用来参考一下.对PHP通用分页类感兴趣的小伙伴,下面一起跟随512笔记的小编两巴掌来看看吧! 写了个php的通用分页类 ...

  8. java rowmapper 通用实现_springmvc工具类封装RowMapper详解

    springmvc通常是先写实体,在数据库查询,最后增删改差,最感觉代码很冗余,自己在封装了一下. 常见的结构是: entity:如 package com.liuxinquan.entiry; /* ...

  9. java时间随机数_java开发代码工具类(时间戳/随机数/日期等)

    简介 java开发代码工具类,提高开发效率,持续更新~ 实践 package com.springboot.sixmonth.common.util; import java.math.BigInte ...

最新文章

  1. vue3.0环境搭建
  2. socket select模型
  3. Oracle 常用查询
  4. const应用和作用
  5. 为什么大公司一定要使用DevOps
  6. 小程序引入百度地图与uni.getLocation的使用
  7. 直播丨Oracle 12cR2 ADG LGWR Library Cache案例分享
  8. 【华为云技术分享】STM32L476移植华为LiteOS系列教程---Kconfig 6
  9. 一滴血、15分钟!钟南山指导研制试剂盒有望快速检测出结果
  10. maven 加入第三方库_添加第三方库到Maven资源库
  11. 夏至日计算公式及“三伏”的日期算法问题
  12. 【js】querySelectorAll和getElemensByTagName的区别
  13. 数学建模之lingo使用
  14. 51单片机流水灯程序
  15. Apple Pencil 一代和二代有什么区别
  16. 华为2012实验室(北京)工作机会
  17. 企业为什么会遭到DDoS攻击?被DDoS攻击该怎么办?
  18. MD5的使用(计算指定目录下文件的md5值)
  19. 我的网名--荡涤心灵
  20. 使用云服务器上线网站流程

热门文章

  1. 160 - 45 Dope2112.2
  2. leetcode 344. 反转字符串 541. 反转字符串 II 双指针解
  3. java 根据类名示例化类_Java类类getEnclosingClass()方法及示例
  4. python掷骰子_用于掷骰子的Python程序(2人骰子游戏)
  5. 信息安全主动攻击和被动攻击_信息安全中的主动和被动攻击 网络安全
  6. Java——设计模式(工厂方法模式)
  7. 安卓手机突然很卡_你的安卓手机越来越卡?教你4招轻松解决问题!
  8. 指针数组和数组指针和函数指针
  9. ubuntu上有个小项目 ,需要调用xx.sh脚本, 出现无法识别 某些环境变量的解决办法,仅供参考
  10. html 微博下拉菜单,jQuery实现模仿微博下拉滚动条加载数据效果