posts - 23,comments - 66,trackbacks - 0
title: JSP分页技术实现
summary:使用工具类实现通用分页处理
author: evan_zhao
email: evan_zhao@hotmail.com
from:http://www.javaresearch.org/article/showarticle.jsp?column=106&thread=8893

  目前比较广泛使用的分页方式是将查询结果缓存在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。

  下面是简单的使用示例

  1.     //DAO查询数据部分代码:
  2.     …
  3.     public RowSetPage getEmployee(String gender, int pageNo) throws Exception{
  4.         String sql="select emp_id, emp_code,  user_name, real_name from employee where gender =?";
  5.        //使用Oracle数据库的分页查询实现,每页显示5条
  6.         PagedStatement pst =new PagedStatementOracleImpl(sql,  pageNo, 5);
  7.         pst.setString(1, gender);
  8.         return pst.executeQuery();
  9.     }


  10.     //Servlet处理查询请求部分代码:

  11.     …
  12.     int pageNo;
  13.     try{
  14.         //可以通过参数pageno获得用户选择的页码
  15.         pageNo = Integer.parseInt(request.getParameter("pageno") );
  16.     }catch(Exception ex){
  17.         //默认为第一页
  18.         pageNo=1;
  19.     }
  20.     String gender = request.getParameter("gender" );
  21.     request.setAttribute("empPage", myBean.getEmployee(gender, pageNo) );
  22.     …

  23.     //JSP显示部分代码
  24. <%@ page import = "page.RowSetPage"%>
  25.     …
  26.     <script language="javascript">
  27.         function doQuery(){
  28.             form1.actionType.value="doQuery";
  29.             form1.submit();
  30.     }
  31.     </script>
  32.     …
  33.     <form name=form1 method=get>
  34.       <input type=hidden name=actionType>
  35.       性别:
  36.       <input type=text name=gender size=1 value="<%=request.getParameter("gender")%>">
  37.       <input type=button value=" 查询 " onclick="doQuery()">
  38. <%
  39.     RowSetPage empPage = (RowSetPage)request.getAttribute("empPage");
  40.     if (empPage == null ) empPage = RowSetPage.EMPTY_PAGE;
  41. %>
  42.     …
  43.     <table  cellspacing="0" width="90%">
  44.         <tr>  <td>ID</td> <td>代码</td> <td>用户名 </td> <td>姓名</td>  </tr>
  45. <%
  46.     javax.sql.RowSet empRS = (javax.sql.RowSet) empPage.getRowSet();
  47.     if (empRS!=nullwhile (empRS.next() ) {
  48. %>
  49.         <tr>  
  50.             <td><%= empRS.getString("EMP_ID")%></td> 
  51.             <td><%= empRS.getString("EMP_CODE")%></td>  
  52.             <td><%= empRS.getString("USER_NAME")%></td> 
  53.             <td><%= empRS.getString("REAL_NAME")%></td>  
  54.         </tr>
  55. <%
  56.     }// end while
  57. %>
  58.         <tr>
  59. <%
  60.     //显示总页数和当前页数(pageno)以及分页代码。
  61.     //此处doQuery为页面上提交查询动作的javascript函数名, pageno为标识当前页码的参数名
  62. %>
  63.             <td colspan=4><%= empPage .getHTML("doQuery""pageno")%></td>
  64.         </tr>
  65.     </table>
  66.     </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字样

  1. ///////////////////////////////////
  2. //
  3. //  Page.java
  4. //  author: evan_zhao@hotmail.com
  5. //
  6. ///////////////////////////////////

  7. package page;

  8. import java.util.List;
  9. import java.util.ArrayList;
  10. import java.util.Collection;
  11. import java.util.Collections;


  12. /**
  13.  * Title: 分页对象<br>
  14.  * Description:  用于包含数据及分页信息的对象<br>
  15.  *                Page类实现了用于显示分页信息的基本方法,但未指定所含数据的类型,
  16.  *               可根据需要实现以特定方式组织数据的子类,<br>
  17.  *                如RowSetPage以RowSet 封装数据,ListPage以List封装数据<br>
  18.  * Copyright:    Copyright (c) 2002 <br>
  19.  * @author evan_zhao@hotmail.com <br>
  20.  * @version 1.0
  21.  */
  22. public  class Page implements java.io.Serializable {
  23.     public static final Page EMPTY_PAGE = new Page();
  24.     public static final int  DEFAULT_PAGE_SIZE = 20;
  25.     public static final  int MAX_PAGE_SIZE = 9999;

  26.     private int myPageSize = DEFAULT_PAGE_SIZE;

  27.     private int start;
  28.     private int avaCount,totalSize;
  29.     private Object data;

  30.     private int currentPageno;
  31.     private int totalPageCount;

  32.     /**
  33.      * 默认构造方法,只构造空页
  34.      */
  35.     protected Page(){
  36.         this.init(0,0,0,DEFAULT_PAGE_SIZE,new Object());
  37.     }

  38.     /**
  39.      * 分页数据初始方法,由子类调用
  40.      * @param start 本页数据在数据库中的起始位置
  41.      * @param avaCount 本页包含的数据条数
  42.      * @param totalSize 数据库中总记录条数
  43.      * @param pageSize 本页容量
  44.      * @param data 本页包含的数据
  45.      */
  46.     protected void init(int start, int avaCount, int totalSize, int pageSize, Object data){

  47.         this.avaCount =avaCount;
  48.         this.myPageSize = pageSize;

  49.         this.start = start;
  50.         this.totalSize = totalSize;

  51.         this.data=data;

  52.         //System.out.println("avaCount:"+avaCount);
  53.         //System.out.println("totalSize:"+totalSize);
  54.         if (avaCount>totalSize) {
  55.             //throw new RuntimeException("记录条数大于总条数?!");
  56.         }

  57.         this.currentPageno = (start -1)/pageSize +1;
  58.         this.totalPageCount = (totalSize + pageSize -1) / pageSize;

  59.         if (totalSize==0 && avaCount==0){
  60.             this.currentPageno = 1;
  61.             this.totalPageCount = 1;
  62.         }
  63.         //System.out.println("Start Index to Page No: " + start + "-" + currentPageno);
  64.     }

  65.     public  Object getData(){
  66.         return this.data;
  67.     }

  68.     /**
  69.      * 取本页数据容量(本页能包含的记录数)
  70.      * @return 本页能包含的记录数
  71.      */
  72.     public int getPageSize(){
  73.         return this.myPageSize;
  74.     }

  75.     /**
  76.      * 是否有下一页
  77.      * @return 是否有下一页
  78.      */
  79.     public boolean hasNextPage() {
  80.       /*
  81.         if (avaCount==0 && totalSize==0){
  82.             return false;
  83.         }
  84.         return (start + avaCount -1) < totalSize;
  85.        */
  86.       return (this.getCurrentPageNo()<this.getTotalPageCount());
  87.     }

  88.     /**
  89.      * 是否有上一页
  90.      * @return  是否有上一页
  91.      */
  92.     public boolean hasPreviousPage() {
  93.       /*
  94.         return start > 1;
  95.        */
  96.       return (this.getCurrentPageNo()>1);
  97.     }

  98.     /**
  99.      * 获取当前页第一条数据在数据库中的位置
  100.      * @return
  101.      */
  102.     public int getStart(){
  103.         return start;
  104.     }

  105.     /**
  106.      * 获取当前页最后一条数据在数据库中的位置
  107.      * @return
  108.      */
  109.     public int getEnd(){
  110.         int end = this.getStart() + this.getSize() -1;
  111.         if (end<0) {
  112.             end = 0;
  113.         }
  114.         return end;
  115.     }

  116.     /**
  117.      * 获取上一页第一条数据在数据库中的位置
  118.      * @return 记录对应的rownum
  119.      */
  120.     public int getStartOfPreviousPage() {
  121.         return Math.max(start-myPageSize, 1);
  122.     }


  123.     /**
  124.      * 获取下一页第一条数据在数据库中的位置
  125.      * @return 记录对应的rownum
  126.      */
  127.     public int getStartOfNextPage() {
  128.         return start + avaCount;
  129.     }

  130.     /**
  131.      * 获取任一页第一条数据在数据库中的位置,每页条数使用默认值
  132.      * @param pageNo 页号
  133.      * @return 记录对应的rownum
  134.      */
  135.     public static int getStartOfAnyPage(int pageNo){
  136.         return getStartOfAnyPage(pageNo, DEFAULT_PAGE_SIZE);
  137.     }

  138.     /**
  139.      * 获取任一页第一条数据在数据库中的位置
  140.      * @param pageNo 页号
  141.      * @param pageSize 每页包含的记录数
  142.      * @return 记录对应的rownum
  143.      */
  144.     public static int getStartOfAnyPage(int pageNo, int pageSize){
  145.         int startIndex = (pageNo-1) * pageSize + 1;
  146.         if ( startIndex < 1) startIndex = 1;
  147.         //System.out.println("Page No to Start Index: " + pageNo + "-" + startIndex);
  148.         return startIndex;
  149.     }

  150.     /**
  151.      * 取本页包含的记录数
  152.      * @return 本页包含的记录数
  153.      */
  154.     public int getSize() {
  155.         return avaCount;
  156.     }

  157.     /**
  158.      * 取数据库中包含的总记录数
  159.      * @return 数据库中包含的总记录数
  160.      */
  161.     public int getTotalSize() {
  162.         return this.totalSize;
  163.     }

  164.     /**
  165.      * 取当前页码
  166.      * @return 当前页码
  167.      */
  168.     public int getCurrentPageNo(){
  169.         return  this.currentPageno;
  170.     }

  171.     /**
  172.      * 取总页码
  173.      * @return 总页码
  174.      */
  175.     public int getTotalPageCount(){
  176.         return this.totalPageCount;
  177.     }


  178.     /**
  179.      *
  180.      * @param queryJSFunctionName 实现分页的JS脚本名字,页码变动时会自动回调该方法
  181.      * @param pageNoParamName 页码参数名称
  182.      * @return
  183.      */
  184.     public String getHTML(String queryJSFunctionName, String pageNoParamName){
  185.         if (getTotalPageCount()<1){
  186.             return "<input type='hidden' name='"+pageNoParamName+"' value='1' >";
  187.         }
  188.         if (queryJSFunctionName == null || queryJSFunctionName.trim().length()<1) {
  189.             queryJSFunctionName = "gotoPage";
  190.         }
  191.         if (pageNoParamName == null || pageNoParamName.trim().length()<1){
  192.             pageNoParamName = "pageno";
  193.         }

  194.         String gotoPage = "_"+queryJSFunctionName;

  195.         StringBuffer html = new StringBuffer("\n");
  196.         html.append("<script language=\"Javascript1.2\">\n")
  197.              .append("function ").append(gotoPage).append("(pageNo){  \n")
  198.              .append(  "   var curPage=1;  \n")
  199.              .append(  "   try{ curPage = document.all[\"")
  200.              .append(pageNoParamName).append("\"].value;  \n")
  201.              .append(  "        document.all[\"").append(pageNoParamName)
  202.              .append("\"].value = pageNo;  \n")
  203.              .append(  "        ").append(queryJSFunctionName).append("(pageNo); \n")
  204.              .append(  "        return true;  \n")
  205.              .append(  "   }catch(e){ \n")
  206. //             .append(  "      try{ \n")
  207. //             .append(  "           document.forms[0].submit();  \n")
  208. //             .append(  "      }catch(e){   \n")
  209.              .append(  "          alert('尚未定义查询方法:function ")
  210.              .append(queryJSFunctionName).append("()'); \n")
  211.              .append(  "          document.all[\"").append(pageNoParamName)
  212.              .append("\"].value = curPage;  \n")
  213.              .append(  "          return false;  \n")
  214. //             .append(  "      }  \n")
  215.              .append(  "   }  \n")
  216.              .append(  "}")
  217.              .append(  "</script>  \n")
  218.              .append(  "");
  219.         html.append( "<table  border=0 cellspacing=0 cellpadding=0 align=center width=80%>  \n")
  220.              .append( "  <tr>  \n")
  221.              .append( "    <td align=left><br>  \n");
  222.         html.append(  "       共" ).append( getTotalPageCount() ).append( "页")
  223.              .append(  "       [") .append(getStart()).append("..").append(getEnd())
  224.              .append("/").append(this.getTotalSize()).append("]  \n")
  225.              .append( "    </td>  \n")
  226.              .append( "    <td align=right>  \n");
  227.         if (hasPreviousPage()){
  228.              html.append( "[<a href='javascript:").append(gotoPage)
  229.              .append("(") .append(getCurrentPageNo()-1) 
  230.              .append( ")'>上一页</a>]   \n");
  231.         }
  232.         html.append(  "       第")
  233.              .append(   "        <select name='")
  234.              .append(pageNoParamName).append("' onChange='javascript:")
  235.              .append(gotoPage).append("(this.value)'>\n");
  236.         String selected = "selected";
  237.         for(int i=1;i<=getTotalPageCount();i++){
  238.             if( i == getCurrentPageNo() )
  239.                  selected = "selected";
  240.             else selected = "";
  241.             html.append( "      <option value='").append(i).append("' ")
  242.               .append(selected).append(">").append(i).append("</option>  \n");
  243.         }
  244.         if (getCurrentPageNo()>getTotalPageCount()){
  245.             html.append( "      <option value='").append(getCurrentPageNo())
  246.             .append("' selected>").append(getCurrentPageNo())
  247.             .append("</option>  \n");
  248.         }
  249.         html.append( "    </select>页  \n");
  250.         if (hasNextPage()){
  251.              html.append( "    [<a href='javascript:").append(gotoPage)
  252.                .append("(").append((getCurrentPageNo()+1)) 
  253.                .append( ")'>下一页</a>]   \n");
  254.         }
  255.         html.append( "</td></tr></table>  \n");

  256.         return html.toString();

  257.     }
  258. }




  259. ///////////////////////////////////
  260. //
  261. //  RowSetPage.java
  262. //  author: evan_zhao@hotmail.com
  263. //
  264. ///////////////////////////////////
  265. package page;

  266. import javax.sql.RowSet;


  267. /**
  268.  * <p>Title: RowSetPage</p>
  269.  * <p>Description: 使用RowSet封装数据的分页对象</p>
  270.  * <p>Copyright: Copyright (c) 2003</p>
  271.  * @author evan_zhao@hotmail.com
  272.  * @version 1.0
  273.  */

  274. public class RowSetPage extends Page {
  275.     private javax.sql.RowSet rs;

  276.     /**
  277.      *空页
  278.      */
  279.     public static final RowSetPage EMPTY_PAGE = new RowSetPage();

  280.     /**
  281.      *默认构造方法,创建空页
  282.      */
  283.     public RowSetPage(){
  284.       this(null, 0,0);
  285.     }

  286.     /**
  287.      *构造分页对象
  288.      *@param crs 包含一页数据的OracleCachedRowSet
  289.      *@param start 该页数据在数据库中的起始位置
  290.      *@param totalSize 数据库中包含的记录总数
  291.      */
  292.     public RowSetPage(RowSet crs, int start, int totalSize) {
  293.         this(crs,start,totalSize,Page.DEFAULT_PAGE_SIZE);
  294.     }

  295.     /**
  296.      *构造分页对象
  297.      *@param crs 包含一页数据的OracleCachedRowSet
  298.      *@param start 该页数据在数据库中的起始位置
  299.      *@param totalSize 数据库中包含的记录总数
  300.      *@pageSize 本页能容纳的记录数
  301.      */
  302.     public RowSetPage(RowSet crs, int start, int totalSize, int pageSize) {
  303.         try{
  304.             int avaCount=0;
  305.             if (crs!=null) {
  306.                 crs.beforeFirst();
  307.                 if (crs.next()){
  308.                     crs.last();
  309.                     avaCount = crs.getRow();
  310.                 }
  311.                 crs.beforeFirst();
  312.             }
  313.             rs = crs;
  314.             super.init(start,avaCount,totalSize,pageSize,rs);
  315.         }catch(java.sql.SQLException sqle){
  316.             throw new RuntimeException(sqle.toString());
  317.         }
  318.     }

  319.     /**
  320.      *取分页对象中的记录数据
  321.      */
  322.     public javax.sql.RowSet getRowSet(){
  323.         return rs;
  324.     }


  325. }




  326. ///////////////////////////////////
  327. //
  328. //  PagedStatement.java
  329. //  author: evan_zhao@hotmail.com
  330. //
  331. ///////////////////////////////////

  332. package page;

  333. import foo.DBUtil;

  334. import java.math.BigDecimal;
  335. import java.util.List;
  336. import java.util.Iterator;
  337. import java.util.Collections;

  338. import java.sql.Connection;
  339. import java.sql.SQLException;
  340. import java.sql.ResultSet;
  341. import java.sql.Statement;
  342. import java.sql.PreparedStatement;
  343. import java.sql.Timestamp;
  344. import javax.sql.RowSet;

  345. /**
  346.  * <p>Title: 分页查询</p>
  347.  * <p>Description: 根据查询语句和页码查询出当页数据</p>
  348.  * <p>Copyright: Copyright (c) 2002</p>
  349.  * @author evan_zhao@hotmail.com
  350.  * @version 1.0
  351.  */
  352. public abstract class PagedStatement {
  353.     public final static int MAX_PAGE_SIZE = Page.MAX_PAGE_SIZE;

  354.     protected String countSQL, querySQL;
  355.     protected int pageNo,pageSize,startIndex,totalCount;
  356.     protected javax.sql.RowSet rowSet;
  357.     protected RowSetPage rowSetPage;

  358.     private List boundParams;

  359.     /**
  360.      * 构造一查询出所有数据的PageStatement
  361.      * @param sql  query sql
  362.      */
  363.     public PagedStatement(String sql){
  364.         this(sql,1,MAX_PAGE_SIZE);
  365.     }


  366.     /**
  367.      * 构造一查询出当页数据的PageStatement
  368.      * @param sql  query sql
  369.      * @param pageNo  页码
  370.      */
  371.     public PagedStatement(String sql, int pageNo){
  372.         this(sql, pageNo, Page.DEFAULT_PAGE_SIZE);
  373.     }

  374.     /**
  375.      * 构造一查询出当页数据的PageStatement,并指定每页显示记录条数
  376.      * @param sql query sql
  377.      * @param pageNo 页码
  378.      * @param pageSize 每页容量
  379.      */
  380.     public PagedStatement(String sql, int pageNo, int pageSize){
  381.         this.pageNo = pageNo;
  382.         this.pageSize = pageSize;
  383.         this.startIndex = Page.getStartOfAnyPage(pageNo, pageSize);
  384.         this.boundParams = Collections.synchronizedList(new java.util.LinkedList());

  385.         this.countSQL = "select count(*) from ( " + sql +") ";
  386.         this.querySQL = intiQuerySQL(sql, this.startIndex, pageSize);
  387.     }


  388.     /**
  389.      *生成查询一页数据的sql语句
  390.      *@param sql 原查询语句
  391.      *@startIndex 开始记录位置
  392.      *@size 需要获取的记录数
  393.      */
  394.     protected abstract  String intiQuerySQL(String sql, int startIndex, int size);


  395.     /**
  396.      *使用给出的对象设置指定参数的值
  397.      *@param index 第一个参数为1,第二个为2,。。。
  398.      *@param obj 包含参数值的对象
  399.      */
  400.     public void setObject(int index, Object obj) throws SQLException{
  401.         BoundParam bp = new BoundParam(index, obj);
  402.         boundParams.remove(bp);
  403.         boundParams.add( bp);
  404.     }

  405.     /**
  406.      *使用给出的对象设置指定参数的值
  407.      *@param index 第一个参数为1,第二个为2,。。。
  408.      *@param obj 包含参数值的对象
  409.      *@param targetSqlType 参数的数据库类型
  410.      */
  411.     public void setObject(int index, Object obj, int targetSqlType) throws SQLException{
  412.         BoundParam bp = new BoundParam(index, obj, targetSqlType);
  413.         boundParams.remove(bp);
  414.         boundParams.add(bp );
  415.     }

  416.     /**
  417.      *使用给出的对象设置指定参数的值
  418.      *@param index 第一个参数为1,第二个为2,。。。
  419.      *@param obj 包含参数值的对象
  420.      *@param targetSqlType 参数的数据库类型(常量定义在java.sql.Types中)
  421.      *@param scale 精度,小数点后的位数
  422.      * (只对targetSqlType是Types.NUMBER或Types.DECIMAL有效,其它类型则忽略)
  423.      */
  424.     public void setObject(int index, Object obj, int targetSqlType, int scale) throws SQLException{
  425.         BoundParam bp = new BoundParam(index, obj, targetSqlType, scale) ;
  426.         boundParams.remove(bp);
  427.         boundParams.add(bp);
  428.     }

  429.     /**
  430.      *使用给出的字符串设置指定参数的值
  431.      *@param index 第一个参数为1,第二个为2,。。。
  432.      *@param str 包含参数值的字符串
  433.      */
  434.     public void setString(int index, String str)throws SQLException{
  435.         BoundParam bp = new BoundParam(index, str)  ;
  436.         boundParams.remove(bp);
  437.         boundParams.add(bp);
  438.     }

  439.     /**
  440.      *使用给出的字符串设置指定参数的值
  441.      *@param index 第一个参数为1,第二个为2,。。。
  442.      *@param timestamp 包含参数值的时间戳
  443.      */
  444.     public void setTimestamp(int index, Timestamp timestamp)throws SQLException{
  445.         BoundParam bp = new BoundParam(index, timestamp)  ;
  446.         boundParams.remove(bp);
  447.         boundParams.add( bp );
  448.     }

  449.     /**
  450.      *使用给出的整数设置指定参数的值
  451.      *@param index 第一个参数为1,第二个为2,。。。
  452.      *@param value 包含参数值的整数
  453.      */
  454.     public void setInt(int index, int value)throws SQLException{
  455.         BoundParam bp =  new BoundParam(index, new Integer(value))  ;
  456.         boundParams.remove(bp);
  457.         boundParams.add( bp );
  458.     }

  459.     /**
  460.      *使用给出的长整数设置指定参数的值
  461.      *@param index 第一个参数为1,第二个为2,。。。
  462.      *@param value 包含参数值的长整数
  463.      */
  464.     public void setLong(int index, long value)throws SQLException{
  465.         BoundParam bp =  new BoundParam(index, new Long(value))  ;
  466.         boundParams.remove(bp);
  467.         boundParams.add( bp );
  468.     }

  469.     /**
  470.      *使用给出的双精度浮点数设置指定参数的值
  471.      *@param index 第一个参数为1,第二个为2,。。。
  472.      *@param value 包含参数值的双精度浮点数
  473.      */
  474.     public void setDouble(int index, double value)throws SQLException{
  475.         BoundParam bp =  new BoundParam(index, new Double(value))   ;
  476.         boundParams.remove(bp);
  477.         boundParams.add( bp);
  478.     }

  479.     /**
  480.      *使用给出的BigDecimal设置指定参数的值
  481.      *@param index 第一个参数为1,第二个为2,。。。
  482.      *@param bd 包含参数值的BigDecimal
  483.      */
  484.     public void setBigDecimal(int index, BigDecimal bd)throws SQLException{
  485.         BoundParam bp =   new BoundParam(index, bd )   ;
  486.         boundParams.remove(bp);
  487.         boundParams.add( bp);
  488.     }

  489.     private  void setParams(PreparedStatement pst) throws SQLException{
  490.         if (pst==null || this.boundParams==null || this.boundParams.size()==0 ) return ;
  491.         BoundParam param;
  492.         for (Iterator itr = this.boundParams.iterator();itr.hasNext();){
  493.             param = (BoundParam) itr.next();
  494.             if  (param==nullcontinue;
  495.             if (param.sqlType == java.sql.Types.OTHER){
  496.                 pst.setObject(param.index, param.value);
  497.             }else{
  498.                 pst.setObject(param.index, param.value, param.sqlType, param.scale);
  499.             }
  500.         }
  501.     }



  502.     /**
  503.      * 执行查询取得一页数据,执行结束后关闭数据库连接
  504.      * @return RowSetPage
  505.      * @throws SQLException
  506.      */
  507.     public  RowSetPage executeQuery() throws SQLException{
  508.         System.out.println("executeQueryUsingPreparedStatement");
  509.         Connection conn = DBUtil.getConnection();
  510.         PreparedStatement pst = null;
  511.         ResultSet rs = null;
  512.         try{
  513.             pst = conn.prepareStatement(this.countSQL);
  514.             setParams(pst);
  515.             rs =pst.executeQuery();
  516.             if (rs.next()){
  517.                 totalCount = rs.getInt(1);
  518.             } else {
  519.                 totalCount = 0;
  520.             }

  521.             rs.close();
  522.             pst.close();

  523.             if (totalCount < 1 ) return RowSetPage.EMPTY_PAGE;

  524.             pst = conn.prepareStatement(this.querySQL);
  525.             System.out.println(querySQL);
  526.             pst.setFetchSize(this.pageSize);
  527.             setParams(pst);
  528.             rs =pst.executeQuery();
  529.             //rs.setFetchSize(pageSize);

  530.             this.rowSet = populate(rs);

  531.             rs.close();
  532.             rs = null;
  533.             pst.close();
  534.             pst = null;

  535.             this.rowSetPage = new RowSetPage(this.rowSet,startIndex,totalCount,pageSize);
  536.             return this.rowSetPage;
  537.         }catch(SQLException sqle){
  538.             //System.out.println("executeQuery SQLException");
  539.             sqle.printStackTrace();
  540.             throw sqle;
  541.         }catch(Exception e){
  542.             e.printStackTrace();
  543.             throw new RuntimeException(e.toString());
  544.         }finally{
  545.             //System.out.println("executeQuery finally");
  546.             DBUtil.close(rs, pst, conn);
  547.         }
  548.     }

  549.     /**
  550.      *将ResultSet数据填充进CachedRowSet
  551.      */
  552.     protected abstract RowSet populate(ResultSet rs) throws SQLException;

  553.     /**
  554.      *取封装成RowSet查询结果
  555.      *@return RowSet
  556.      */
  557.     public javax.sql.RowSet getRowSet(){
  558.         return this.rowSet;
  559.     }


  560.     /**
  561.      *取封装成RowSetPage的查询结果
  562.      *@return RowSetPage
  563.      */
  564.     public RowSetPage getRowSetPage() {
  565.         return this.rowSetPage;
  566.     }



  567.     /**
  568.      *关闭数据库连接
  569.      */
  570.     public void close(){
  571.         //因为数据库连接在查询结束或发生异常时即关闭,此处不做任何事情
  572.         //留待扩充。
  573.     }



  574.     private class BoundParam {
  575.         int index;
  576.         Object value;
  577.         int sqlType;
  578.         int scale;

  579.         public BoundParam(int index, Object value) {
  580.             this(index, value, java.sql.Types.OTHER);
  581.         }

  582.         public BoundParam(int index, Object value, int sqlType) {
  583.             this(index, value, sqlType, 0);
  584.         }

  585.         public BoundParam(int index, Object value, int sqlType, int scale) {
  586.             this.index = index;
  587.             this.value = value;
  588.             this.sqlType = sqlType;
  589.             this.scale = scale;
  590.         }

  591.         public boolean equals(Object obj){
  592.             if (obj!=null && this.getClass().isInstance(obj)){
  593.                 BoundParam bp = (BoundParam)obj;
  594.                 if (this.index==bp.index) return true;
  595.             }
  596.             return false;
  597.         }
  598.     }

  599. }


  600. ///////////////////////////////////
  601. //
  602. //  PagedStatementOracleImpl.java
  603. //  author: evan_zhao@hotmail.com
  604. //
  605. ///////////////////////////////////
  606. package page;
  607. import java.sql.ResultSet;
  608. import java.sql.SQLException;
  609. import javax.sql.RowSet;
  610. import oracle.jdbc.rowset.OracleCachedRowSet;

  611. /**
  612.  * <p>Title: 分页查询Oracle数据库实现</p>
  613.  * <p>Copyright: Copyright (c) 2002</p>
  614.  * @author evan_zhao@hotmail.com
  615.  * @version 1.0
  616.  */
  617. public class PagedStatementOracleImpl extends PagedStatement {

  618.     /**
  619.      * 构造一查询出所有数据的PageStatement
  620.      * @param sql  query sql
  621.      */
  622.     public PagedStatementOracleImpl(String sql){
  623.         super(sql);
  624.     }


  625.     /**
  626.      * 构造一查询出当页数据的PageStatement
  627.      * @param sql  query sql
  628.      * @param pageNo  页码
  629.      */
  630.     public PagedStatementOracleImpl(String sql, int pageNo){
  631.         super(sql, pageNo);
  632.     }

  633.     /**
  634.      * 构造一查询出当页数据的PageStatement,并指定每页显示记录条数
  635.      * @param sql query sql
  636.      * @param pageNo 页码
  637.      * @param pageSize 每页容量
  638.      */
  639.     public PagedStatementOracleImpl(String sql, int pageNo, int pageSize){
  640.         super(sql, pageNo, pageSize);
  641.     }


  642.     /**
  643.      *生成查询一页数据的sql语句
  644.      *@param sql 原查询语句
  645.      *@startIndex 开始记录位置
  646.      *@size 需要获取的记录数
  647.      */
  648.     protected String intiQuerySQL(String sql, int startIndex, int size){
  649.         StringBuffer querySQL = new StringBuffer();
  650.         if (size != super.MAX_PAGE_SIZE) {
  651.             querySQL.append("select * from (select my_table.*,rownum as my_rownum from(")
  652.                     .append(  sql)
  653.                     .append(") my_table where rownum<").append(startIndex + size)
  654.                     .append(") where my_rownum>=").append(startIndex);
  655.         } else {
  656.             querySQL.append("select * from (select my_table.*,rownum as my_rownum from(")
  657.                     .append(sql)
  658.                     .append(") my_table ")
  659.                     .append(") where my_rownum>=").append(startIndex);
  660.         }
  661.         return querySQL.toString();
  662.     }

  663.     /**
  664.      *将ResultSet数据填充进CachedRowSet
  665.      */
  666.     protected  RowSet populate(ResultSet rs) throws SQLException{
  667.         OracleCachedRowSet ocrs = new OracleCachedRowSet();
  668.         ocrs.populate(rs);
  669.         return ocrs;
  670.     }

  671. }


相关连接
  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设计模式的一个示例程序。
posted on 2006-03-13 22:27 rd2pm 阅读(954) 评论(0)  编辑  收藏

只有注册用户登录后才能发表评论。


网站导航: