Java 分页技术实现

  • Post author:
  • Post category:java


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


    RowSetPage getEmployee(




    String




    gender,


    int


    pageNo)


    throws






    Exception




    {




  3. String




    sql=

    “select emp_id, emp_code,  user_name, real_name from employee where gender =?”

    ;


  4. //使用Oracle数据库的分页查询实现,每页显示5条

  5. PagedStatement pst =


    new


    PagedStatementOracleImpl(sql,  pageNo, 5);
  6. pst.setString(1, gender);


  7. return


    pst.executeQuery();
  8. }


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



  10. int


    pageNo;


  11. try


    {


  12. //可以通过参数pageno获得用户选择的页码

  13. pageNo =




    Integer




    .parseInt(request.getParameter(

    “pageno”

    ) );
  14. }


    catch


    (




    Exception




    ex){


  15. //默认为第一页

  16. pageNo=1;
  17. }




  18. String




    gender = request.getParameter(

    “gender”

    );
  19. request.setAttribute(

    “empPage”

    , myBean.getEmployee(gender, pageNo) );


  20. //JSP显示部分代码

  21. <%@ page


    import


    =

    “page.RowSetPage”

    %>
  22. <script language=

    “javascript”

    >
  23. function doQuery(){
  24. form1.actionType.value=

    “doQuery”

    ;
  25. form1.submit();
  26. }
  27. </script>
  28. <form name=form1 method=get>
  29. <input type=hidden name=actionType>
  30. 性别:
  31. <input type=text name=gender size=1 value=

    “<%=request.getParameter(”

    gender

    “)%>”

    >
  32. <input type=button value=

    ” 查询 ”

    οnclick=

    “doQuery()”

    >
  33. <%
  34. RowSetPage empPage = (RowSetPage)request.getAttribute(

    “empPage”

    );


  35. if


    (empPage ==


    null


    ) empPage = RowSetPage.EMPTY_PAGE;
  36. %>
  37. <table  cellspacing=

    “0”

    width=

    “90%”

    >
  38. <tr> <td>ID</td> <td>代码</td> <td>用户名</td> <td>姓名</td>  </tr>
  39. <%
  40. javax.sql.




    RowSet




    empRS = (javax.sql.




    RowSet




    ) empPage.getRowSet();


  41. if


    (empRS!=


    null


    )


    while


    (empRS.next() ) {
  42. %>
  43. <tr>
  44. <td><%= empRS.getString(

    “EMP_ID”

    )%></td>
  45. <td><%= empRS.getString(

    “EMP_CODE”

    )%></td>
  46. <td><%= empRS.getString(

    “USER_NAME”

    )%></td>
  47. <td><%= empRS.getString(

    “REAL_NAME”

    )%></td>
  48. </tr>
  49. <%
  50. }


    // end while

  51. %>
  52. <tr>
  53. <%


  54. //显示总页数和当前页数(pageno)以及分页代码。



  55. //此处doQuery为页面上提交查询动作的javascript函数名, pageno为标识当前页码的参数名

  56. %>
  57. <td colspan=4><%= empPage .getHTML(

    “doQuery”

    ,

    “pageno”

    )%></td>
  58. </tr>
  59. </table>
  60. </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==


    null


    )


    continue


    ;


  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. }