JSP分页技术实现

时间:2022-09-20 20:58:38
 目前比较广泛使用的分页方式是将查询结果缓存在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条记录应该这么写:
[pre] 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
[/pre]
  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.  
  2. //DAO查询数据部分代码:
  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.  
  11.  
  12. //Servlet处理查询请求部分代码:
  13.  
  14. int pageNo;
  15. try{
  16. //可以通过参数pageno获得用户选择的页码
  17. pageNo = Integer.parseInt(request.getParameter("pageno") );
  18. }catch(Exception ex){
  19. //默认为第一页
  20. pageNo=1;
  21. }
  22. String gender = request.getParameter("gender" );
  23. request.setAttribute("empPage", myBean.getEmployee(gender, pageNo) );
  24.  
  25. //JSP显示部分代码
  26. <%@ page import = "page.RowSetPage"%>
  27. <script language="javascript">
  28. function doQuery(){
  29. form1.actionType.value="doQuery";
  30. form1.submit();
  31. }
  32. </script>
  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. <table cellspacing="0" width="90%">
  43. <tr> <td>ID</td> <td>代码</td> <td>用户名</td> <td>姓名</td> </tr>
  44. <%
  45. javax.sql.RowSet empRS = (javax.sql.RowSet) empPage.getRowSet();
  46. if (empRS!=null) while (empRS.next() ) {
  47. %>
  48. <tr>
  49. <td><%= empRS.getString("EMP_ID")%></td>
  50. <td><%= empRS.getString("EMP_CODE")%></td>
  51. <td><%= empRS.getString("USER_NAME")%></td>
  52. <td><%= empRS.getString("REAL_NAME")%></td>
  53. </tr>
  54. <%
  55. }// end while
  56. %>
  57. <tr>
  58. <%
  59. //显示总页数和当前页数(pageno)以及分页代码。
  60. //此处doQuery为页面上提交查询动作的javascript函数名, pageno为标识当前页码的参数名
  61. %>
  62. <td colspan=4><%= empPage .getHTML("doQuery", "pageno")%></td>
  63. </tr>
  64. </table>
  65. </form>

  效果如图:
JSP分页技术实现

  因为分页显示一般都会伴有查询条件和查询动作,页面应已经有校验查询条件和提交查询的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. //
  4. // Page.java
  5. // author: evan_zhao@hotmail.com
  6. //
  7. ///////////////////////////////////
  8.  
  9. package page;
  10.  
  11. import java.util.List;
  12. import java.util.ArrayList;
  13. import java.util.Collection;
  14. import java.util.Collections;
  15.  
  16.  
  17. /**
  18. * Title: 分页对象<br>
  19. * Description: 用于包含数据及分页信息的对象<br>
  20. * Page类实现了用于显示分页信息的基本方法,但未指定所含数据的类型,
  21. * 可根据需要实现以特定方式组织数据的子类,<br>
  22. * 如RowSetPage以RowSet封装数据,ListPage以List封装数据<br>
  23. * Copyright: Copyright (c) 2002 <br>
  24. * @author evan_zhao@hotmail.com <br>
  25. * @version 1.0
  26. */
  27. public class Page implements java.io.Serializable {
  28. public static final Page EMPTY_PAGE = new Page();
  29. public static final int DEFAULT_PAGE_SIZE = 20;
  30. public static final int MAX_PAGE_SIZE = 9999;
  31.  
  32. private int myPageSize = DEFAULT_PAGE_SIZE;
  33.  
  34. private int start;
  35. private int avaCount,totalSize;
  36. private Object data;
  37.  
  38. private int currentPageno;
  39. private int totalPageCount;
  40.  
  41. /**
  42. * 默认构造方法,只构造空页
  43. */
  44. protected Page(){
  45. this.init(0,0,0,DEFAULT_PAGE_SIZE,new Object());
  46. }
  47.  
  48. /**
  49. * 分页数据初始方法,由子类调用
  50. * @param start 本页数据在数据库中的起始位置
  51. * @param avaCount 本页包含的数据条数
  52. * @param totalSize 数据库中总记录条数
  53. * @param pageSize 本页容量
  54. * @param data 本页包含的数据
  55. */
  56. protected void init(int start, int avaCount, int totalSize, int pageSize, Object data){
  57.  
  58. this.avaCount =avaCount;
  59. this.myPageSize = pageSize;
  60.  
  61. this.start = start;
  62. this.totalSize = totalSize;
  63.  
  64. this.data=data;
  65.  
  66. //System.out.println("avaCount:"+avaCount);
  67. //System.out.println("totalSize:"+totalSize);
  68. if (avaCount>totalSize) {
  69. //throw new RuntimeException("记录条数大于总条数?!");
  70. }
  71.  
  72. this.currentPageno = (start -1)/pageSize +1;
  73. this.totalPageCount = (totalSize + pageSize -1) / pageSize;
  74.  
  75. if (totalSize==0 && avaCount==0){
  76. this.currentPageno = 1;
  77. this.totalPageCount = 1;
  78. }
  79. //System.out.println("Start Index to Page No: " + start + "-" + currentPageno);
  80. }
  81.  
  82. public Object getData(){
  83. return this.data;
  84. }
  85.  
  86. /**
  87. * 取本页数据容量(本页能包含的记录数)
  88. * @return 本页能包含的记录数
  89. */
  90. public int getPageSize(){
  91. return this.myPageSize;
  92. }
  93.  
  94. /**
  95. * 是否有下一页
  96. * @return 是否有下一页
  97. */
  98. public boolean hasNextPage() {
  99. /*
  100. if (avaCount==0 && totalSize==0){
  101. return false;
  102. }
  103. return (start + avaCount -1) < totalSize;
  104. */
  105. return (this.getCurrentPageNo()<this.getTotalPageCount());
  106. }
  107.  
  108. /**
  109. * 是否有上一页
  110. * @return 是否有上一页
  111. */
  112. public boolean hasPreviousPage() {
  113. /*
  114. return start > 1;
  115. */
  116. return (this.getCurrentPageNo()>1);
  117. }
  118.  
  119. /**
  120. * 获取当前页第一条数据在数据库中的位置
  121. * @return
  122. */
  123. public int getStart(){
  124. return start;
  125. }
  126.  
  127. /**
  128. * 获取当前页最后一条数据在数据库中的位置
  129. * @return
  130. */
  131. public int getEnd(){
  132. int end = this.getStart() + this.getSize() -1;
  133. if (end<0) {
  134. end = 0;
  135. }
  136. return end;
  137. }
  138.  
  139. /**
  140. * 获取上一页第一条数据在数据库中的位置
  141. * @return 记录对应的rownum
  142. */
  143. public int getStartOfPreviousPage() {
  144. return Math.max(start-myPageSize, 1);
  145. }
  146.  
  147.  
  148. /**
  149. * 获取下一页第一条数据在数据库中的位置
  150. * @return 记录对应的rownum
  151. */
  152. public int getStartOfNextPage() {
  153. return start + avaCount;
  154. }
  155.  
  156. /**
  157. * 获取任一页第一条数据在数据库中的位置,每页条数使用默认值
  158. * @param pageNo 页号
  159. * @return 记录对应的rownum
  160. */
  161. public static int getStartOfAnyPage(int pageNo){
  162. return getStartOfAnyPage(pageNo, DEFAULT_PAGE_SIZE);
  163. }
  164.  
  165. /**
  166. * 获取任一页第一条数据在数据库中的位置
  167. * @param pageNo 页号
  168. * @param pageSize 每页包含的记录数
  169. * @return 记录对应的rownum
  170. */
  171. public static int getStartOfAnyPage(int pageNo, int pageSize){
  172. int startIndex = (pageNo-1) * pageSize + 1;
  173. if ( startIndex < 1) startIndex = 1;
  174. //System.out.println("Page No to Start Index: " + pageNo + "-" + startIndex);
  175. return startIndex;
  176. }
  177.  
  178. /**
  179. * 取本页包含的记录数
  180. * @return 本页包含的记录数
  181. */
  182. public int getSize() {
  183. return avaCount;
  184. }
  185.  
  186. /**
  187. * 取数据库中包含的总记录数
  188. * @return 数据库中包含的总记录数
  189. */
  190. public int getTotalSize() {
  191. return this.totalSize;
  192. }
  193.  
  194. /**
  195. * 取当前页码
  196. * @return 当前页码
  197. */
  198. public int getCurrentPageNo(){
  199. return this.currentPageno;
  200. }
  201.  
  202. /**
  203. * 取总页码
  204. * @return 总页码
  205. */
  206. public int getTotalPageCount(){
  207. return this.totalPageCount;
  208. }
  209.  
  210.  
  211. /**
  212. *
  213. * @param queryJSFunctionName 实现分页的JS脚本名字,页码变动时会自动回调该方法
  214. * @param pageNoParamName 页码参数名称
  215. * @return
  216. */
  217. public String getHTML(String queryJSFunctionName, String pageNoParamName){
  218. if (getTotalPageCount()<1){
  219. return "<input type='hidden' name='"+pageNoParamName+"' value='1' >";
  220. }
  221. if (queryJSFunctionName == null || queryJSFunctionName.trim().length()<1) {
  222. queryJSFunctionName = "gotoPage";
  223. }
  224. if (pageNoParamName == null || pageNoParamName.trim().length()<1){
  225. pageNoParamName = "pageno";
  226. }
  227.  
  228. String gotoPage = "_"+queryJSFunctionName;
  229.  
  230. StringBuffer html = new StringBuffer("/n");
  231. html.append("<script language=/"Javascript1.2/">/n")
  232. .append("function ").append(gotoPage).append("(pageNo){ /n")
  233. .append( " var curPage=1; /n")
  234. .append( " try{ curPage = document.all[/"")
  235. .append(pageNoParamName).append("/"].value; /n")
  236. .append( " document.all[/"").append(pageNoParamName)
  237. .append("/"].value = pageNo; /n")
  238. .append( " ").append(queryJSFunctionName).append("(pageNo); /n")
  239. .append( " return true; /n")
  240. .append( " }catch(e){ /n")
  241. // .append( " try{ /n")
  242. // .append( " document.forms[0].submit(); /n")
  243. // .append( " }catch(e){ /n")
  244. .append( " alert('尚未定义查询方法:function ")
  245. .append(queryJSFunctionName).append("()'); /n")
  246. .append( " document.all[/"").append(pageNoParamName)
  247. .append("/"].value = curPage; /n")
  248. .append( " return false; /n")
  249. // .append( " } /n")
  250. .append( " } /n")
  251. .append( "}")
  252. .append( "</script> /n")
  253. .append( "");
  254. html.append( "<table border=0 cellspacing=0 cellpadding=0 align=center width=80%> /n")
  255. .append( " <tr> /n")
  256. .append( " <td align=left><br> /n");
  257. html.append( " 共" ).append( getTotalPageCount() ).append( "页")
  258. .append( " [") .append(getStart()).append("..").append(getEnd())
  259. .append("/").append(this.getTotalSize()).append("] /n")
  260. .append( " </td> /n")
  261. .append( " <td align=right> /n");
  262. if (hasPreviousPage()){
  263. html.append( "[<a href='javascript:").append(gotoPage)
  264. .append("(") .append(getCurrentPageNo()-1)
  265. .append( ")'>上一页</a>] /n");
  266. }
  267. html.append( " 第")
  268. .append( " <select name='")
  269. .append(pageNoParamName).append("' onChange='javascript:")
  270. .append(gotoPage).append("(this.value)'>/n");
  271. String selected = "selected";
  272. for(int i=1;i<=getTotalPageCount();i++){
  273. if( i == getCurrentPageNo() )
  274. selected = "selected";
  275. else selected = "";
  276. html.append( " <option value='").append(i).append("' ")
  277. .append(selected).append(">").append(i).append("</option> /n");
  278. }
  279. if (getCurrentPageNo()>getTotalPageCount()){
  280. html.append( " <option value='").append(getCurrentPageNo())
  281. .append("' selected>").append(getCurrentPageNo())
  282. .append("</option> /n");
  283. }
  284. html.append( " </select>页 /n");
  285. if (hasNextPage()){
  286. html.append( " [<a href='javascript:").append(gotoPage)
  287. .append("(").append((getCurrentPageNo()+1))
  288. .append( ")'>下一页</a>] /n");
  289. }
  290. html.append( "</td></tr></table> /n");
  291.  
  292. return html.toString();
  293.  
  294. }
  295. }
  296.  
  297.  
  298.  
  299.  
  300. ///////////////////////////////////
  301. //
  302. // RowSetPage.java
  303. // author: evan_zhao@hotmail.com
  304. //
  305. ///////////////////////////////////
  306. package page;
  307.  
  308. import javax.sql.RowSet;
  309.  
  310.  
  311. /**
  312. * <p>Title: RowSetPage</p>
  313. * <p>Description: 使用RowSet封装数据的分页对象</p>
  314. * <p>Copyright: Copyright (c) 2003</p>
  315. * @author evan_zhao@hotmail.com
  316. * @version 1.0
  317. */
  318.  
  319. public class RowSetPage extends Page {
  320. private javax.sql.RowSet rs;
  321.  
  322. /**
  323. *空页
  324. */
  325. public static final RowSetPage EMPTY_PAGE = new RowSetPage();
  326.  
  327. /**
  328. *默认构造方法,创建空页
  329. */
  330. public RowSetPage(){
  331. this(null, 0,0);
  332. }
  333.  
  334. /**
  335. *构造分页对象
  336. *@param crs 包含一页数据的OracleCachedRowSet
  337. *@param start 该页数据在数据库中的起始位置
  338. *@param totalSize 数据库中包含的记录总数
  339. */
  340. public RowSetPage(RowSet crs, int start, int totalSize) {
  341. this(crs,start,totalSize,Page.DEFAULT_PAGE_SIZE);
  342. }
  343.  
  344. /**
  345. *构造分页对象
  346. *@param crs 包含一页数据的OracleCachedRowSet
  347. *@param start 该页数据在数据库中的起始位置
  348. *@param totalSize 数据库中包含的记录总数
  349. *@pageSize 本页能容纳的记录数
  350. */
  351. public RowSetPage(RowSet crs, int start, int totalSize, int pageSize) {
  352. try{
  353. int avaCount=0;
  354. if (crs!=null) {
  355. crs.beforeFirst();
  356. if (crs.next()){
  357. crs.last();
  358. avaCount = crs.getRow();
  359. }
  360. crs.beforeFirst();
  361. }
  362. rs = crs;
  363. super.init(start,avaCount,totalSize,pageSize,rs);
  364. }catch(java.sql.SQLException sqle){
  365. throw new RuntimeException(sqle.toString());
  366. }
  367. }
  368.  
  369. /**
  370. *取分页对象中的记录数据
  371. */
  372. public javax.sql.RowSet getRowSet(){
  373. return rs;
  374. }
  375.  
  376.  
  377. }
  378.  
  379.  
  380.  
  381.  
  382. ///////////////////////////////////
  383. //
  384. // PagedStatement.java
  385. // author: evan_zhao@hotmail.com
  386. //
  387. ///////////////////////////////////
  388.  
  389. package page;
  390.  
  391. import foo.DBUtil;
  392.  
  393. import java.math.BigDecimal;
  394. import java.util.List;
  395. import java.util.Iterator;
  396. import java.util.Collections;
  397.  
  398. import java.sql.Connection;
  399. import java.sql.SQLException;
  400. import java.sql.ResultSet;
  401. import java.sql.Statement;
  402. import java.sql.PreparedStatement;
  403. import java.sql.Timestamp;
  404. import javax.sql.RowSet;
  405.  
  406. /**
  407. * <p>Title: 分页查询</p>
  408. * <p>Description: 根据查询语句和页码查询出当页数据</p>
  409. * <p>Copyright: Copyright (c) 2002</p>
  410. * @author evan_zhao@hotmail.com
  411. * @version 1.0
  412. */
  413. public abstract class PagedStatement {
  414. public final static int MAX_PAGE_SIZE = Page.MAX_PAGE_SIZE;
  415.  
  416. protected String countSQL, querySQL;
  417. protected int pageNo,pageSize,startIndex,totalCount;
  418. protected javax.sql.RowSet rowSet;
  419. protected RowSetPage rowSetPage;
  420.  
  421. private List boundParams;
  422.  
  423. /**
  424. * 构造一查询出所有数据的PageStatement
  425. * @param sql query sql
  426. */
  427. public PagedStatement(String sql){
  428. this(sql,1,MAX_PAGE_SIZE);
  429. }
  430.  
  431.  
  432. /**
  433. * 构造一查询出当页数据的PageStatement
  434. * @param sql query sql
  435. * @param pageNo 页码
  436. */
  437. public PagedStatement(String sql, int pageNo){
  438. this(sql, pageNo, Page.DEFAULT_PAGE_SIZE);
  439. }
  440.  
  441. /**
  442. * 构造一查询出当页数据的PageStatement,并指定每页显示记录条数
  443. * @param sql query sql
  444. * @param pageNo 页码
  445. * @param pageSize 每页容量
  446. */
  447. public PagedStatement(String sql, int pageNo, int pageSize){
  448. this.pageNo = pageNo;
  449. this.pageSize = pageSize;
  450. this.startIndex = Page.getStartOfAnyPage(pageNo, pageSize);
  451. this.boundParams = Collections.synchronizedList(new java.util.LinkedList());
  452.  
  453. this.countSQL = "select count(*) from ( " + sql +") ";
  454. this.querySQL = intiQuerySQL(sql, this.startIndex, pageSize);
  455. }
  456.  
  457.  
  458. /**
  459. *生成查询一页数据的sql语句
  460. *@param sql 原查询语句
  461. *@startIndex 开始记录位置
  462. *@size 需要获取的记录数
  463. */
  464. protected abstract String intiQuerySQL(String sql, int startIndex, int size);
  465.  
  466.  
  467. /**
  468. *使用给出的对象设置指定参数的值
  469. *@param index 第一个参数为1,第二个为2,。。。
  470. *@param obj 包含参数值的对象
  471. */
  472. public void setObject(int index, Object obj) throws SQLException{
  473. BoundParam bp = new BoundParam(index, obj);
  474. boundParams.remove(bp);
  475. boundParams.add( bp);
  476. }
  477.  
  478. /**
  479. *使用给出的对象设置指定参数的值
  480. *@param index 第一个参数为1,第二个为2,。。。
  481. *@param obj 包含参数值的对象
  482. *@param targetSqlType 参数的数据库类型
  483. */
  484. public void setObject(int index, Object obj, int targetSqlType) throws SQLException{
  485. BoundParam bp = new BoundParam(index, obj, targetSqlType);
  486. boundParams.remove(bp);
  487. boundParams.add(bp );
  488. }
  489.  
  490. /**
  491. *使用给出的对象设置指定参数的值
  492. *@param index 第一个参数为1,第二个为2,。。。
  493. *@param obj 包含参数值的对象
  494. *@param targetSqlType 参数的数据库类型(常量定义在java.sql.Types中)
  495. *@param scale 精度,小数点后的位数
  496. * (只对targetSqlType是Types.NUMBER或Types.DECIMAL有效,其它类型则忽略)
  497. */
  498. public void setObject(int index, Object obj, int targetSqlType, int scale) throws SQLException{
  499. BoundParam bp = new BoundParam(index, obj, targetSqlType, scale) ;
  500. boundParams.remove(bp);
  501. boundParams.add(bp);
  502. }
  503.  
  504. /**
  505. *使用给出的字符串设置指定参数的值
  506. *@param index 第一个参数为1,第二个为2,。。。
  507. *@param str 包含参数值的字符串
  508. */
  509. public void setString(int index, String str)throws SQLException{
  510. BoundParam bp = new BoundParam(index, str) ;
  511. boundParams.remove(bp);
  512. boundParams.add(bp);
  513. }
  514.  
  515. /**
  516. *使用给出的字符串设置指定参数的值
  517. *@param index 第一个参数为1,第二个为2,。。。
  518. *@param timestamp 包含参数值的时间戳
  519. */
  520. public void setTimestamp(int index, Timestamp timestamp)throws SQLException{
  521. BoundParam bp = new BoundParam(index, timestamp) ;
  522. boundParams.remove(bp);
  523. boundParams.add( bp );
  524. }
  525.  
  526. /**
  527. *使用给出的整数设置指定参数的值
  528. *@param index 第一个参数为1,第二个为2,。。。
  529. *@param value 包含参数值的整数
  530. */
  531. public void setInt(int index, int value)throws SQLException{
  532. BoundParam bp = new BoundParam(index, new Integer(value)) ;
  533. boundParams.remove(bp);
  534. boundParams.add( bp );
  535. }
  536.  
  537. /**
  538. *使用给出的长整数设置指定参数的值
  539. *@param index 第一个参数为1,第二个为2,。。。
  540. *@param value 包含参数值的长整数
  541. */
  542. public void setLong(int index, long value)throws SQLException{
  543. BoundParam bp = new BoundParam(index, new Long(value)) ;
  544. boundParams.remove(bp);
  545. boundParams.add( bp );
  546. }
  547.  
  548. /**
  549. *使用给出的双精度浮点数设置指定参数的值
  550. *@param index 第一个参数为1,第二个为2,。。。
  551. *@param value 包含参数值的双精度浮点数
  552. */
  553. public void setDouble(int index, double value)throws SQLException{
  554. BoundParam bp = new BoundParam(index, new Double(value)) ;
  555. boundParams.remove(bp);
  556. boundParams.add( bp);
  557. }
  558.  
  559. /**
  560. *使用给出的BigDecimal设置指定参数的值
  561. *@param index 第一个参数为1,第二个为2,。。。
  562. *@param bd 包含参数值的BigDecimal
  563. */
  564. public void setBigDecimal(int index, BigDecimal bd)throws SQLException{
  565. BoundParam bp = new BoundParam(index, bd ) ;
  566. boundParams.remove(bp);
  567. boundParams.add( bp);
  568. }
  569.  
  570. private void setParams(PreparedStatement pst) throws SQLException{
  571. if (pst==null || this.boundParams==null || this.boundParams.size()==0 ) return ;
  572. BoundParam param;
  573. for (Iterator itr = this.boundParams.iterator();itr.hasNext();){
  574. param = (BoundParam) itr.next();
  575. if (param==null) continue;
  576. if (param.sqlType == java.sql.Types.OTHER){
  577. pst.setObject(param.index, param.value);
  578. }else{
  579. pst.setObject(param.index, param.value, param.sqlType, param.scale);
  580. }
  581. }
  582. }
  583.  
  584.  
  585.  
  586. /**
  587. * 执行查询取得一页数据,执行结束后关闭数据库连接
  588. * @return RowSetPage
  589. * @throws SQLException
  590. */
  591. public RowSetPage executeQuery() throws SQLException{
  592. System.out.println("executeQueryUsingPreparedStatement");
  593. Connection conn = DBUtil.getConnection();
  594. PreparedStatement pst = null;
  595. ResultSet rs = null;
  596. try{
  597. pst = conn.prepareStatement(this.countSQL);
  598. setParams(pst);
  599. rs =pst.executeQuery();
  600. if (rs.next()){
  601. totalCount = rs.getInt(1);
  602. } else {
  603. totalCount = 0;
  604. }
  605.  
  606. rs.close();
  607. pst.close();
  608.  
  609. if (totalCount < 1 ) return RowSetPage.EMPTY_PAGE;
  610.  
  611. pst = conn.prepareStatement(this.querySQL);
  612. System.out.println(querySQL);
  613. pst.setFetchSize(this.pageSize);
  614. setParams(pst);
  615. rs =pst.executeQuery();
  616. //rs.setFetchSize(pageSize);
  617.  
  618. this.rowSet = populate(rs);
  619.  
  620. rs.close();
  621. rs = null;
  622. pst.close();
  623. pst = null;
  624.  
  625. this.rowSetPage = new RowSetPage(this.rowSet,startIndex,totalCount,pageSize);
  626. return this.rowSetPage;
  627. }catch(SQLException sqle){
  628. //System.out.println("executeQuery SQLException");
  629. sqle.printStackTrace();
  630. throw sqle;
  631. }catch(Exception e){
  632. e.printStackTrace();
  633. throw new RuntimeException(e.toString());
  634. }finally{
  635. //System.out.println("executeQuery finally");
  636. DBUtil.close(rs, pst, conn);
  637. }
  638. }
  639.  
  640. /**
  641. *将ResultSet数据填充进CachedRowSet
  642. */
  643. protected abstract RowSet populate(ResultSet rs) throws SQLException;
  644.  
  645. /**
  646. *取封装成RowSet查询结果
  647. *@return RowSet
  648. */
  649. public javax.sql.RowSet getRowSet(){
  650. return this.rowSet;
  651. }
  652.  
  653.  
  654. /**
  655. *取封装成RowSetPage的查询结果
  656. *@return RowSetPage
  657. */
  658. public RowSetPage getRowSetPage() {
  659. return this.rowSetPage;
  660. }
  661.  
  662.  
  663.  
  664. /**
  665. *关闭数据库连接
  666. */
  667. public void close(){
  668. //因为数据库连接在查询结束或发生异常时即关闭,此处不做任何事情
  669. //留待扩充。
  670. }
  671.  
  672.  
  673.  
  674. private class BoundParam {
  675. int index;
  676. Object value;
  677. int sqlType;
  678. int scale;
  679.  
  680. public BoundParam(int index, Object value) {
  681. this(index, value, java.sql.Types.OTHER);
  682. }
  683.  
  684. public BoundParam(int index, Object value, int sqlType) {
  685. this(index, value, sqlType, 0);
  686. }
  687.  
  688. public BoundParam(int index, Object value, int sqlType, int scale) {
  689. this.index = index;
  690. this.value = value;
  691. this.sqlType = sqlType;
  692. this.scale = scale;
  693. }
  694.  
  695. public boolean equals(Object obj){
  696. if (obj!=null && this.getClass().isInstance(obj)){
  697. BoundParam bp = (BoundParam)obj;
  698. if (this.index==bp.index) return true;
  699. }
  700. return false;
  701. }
  702. }
  703.  
  704. }
  705.  
  706.  
  707. ///////////////////////////////////
  708. //
  709. // PagedStatementOracleImpl.java
  710. // author: evan_zhao@hotmail.com
  711. //
  712. ///////////////////////////////////
  713. package page;
  714. import java.sql.ResultSet;
  715. import java.sql.SQLException;
  716. import javax.sql.RowSet;
  717. import oracle.jdbc.rowset.OracleCachedRowSet;
  718.  
  719. /**
  720. * <p>Title: 分页查询Oracle数据库实现</p>
  721. * <p>Copyright: Copyright (c) 2002</p>
  722. * @author evan_zhao@hotmail.com
  723. * @version 1.0
  724. */
  725. public class PagedStatementOracleImpl extends PagedStatement {
  726.  
  727. /**
  728. * 构造一查询出所有数据的PageStatement
  729. * @param sql query sql
  730. */
  731. public PagedStatementOracleImpl(String sql){
  732. super(sql);
  733. }
  734.  
  735.  
  736. /**
  737. * 构造一查询出当页数据的PageStatement
  738. * @param sql query sql
  739. * @param pageNo 页码
  740. */
  741. public PagedStatementOracleImpl(String sql, int pageNo){
  742. super(sql, pageNo);
  743. }
  744.  
  745. /**
  746. * 构造一查询出当页数据的PageStatement,并指定每页显示记录条数
  747. * @param sql query sql
  748. * @param pageNo 页码
  749. * @param pageSize 每页容量
  750. */
  751. public PagedStatementOracleImpl(String sql, int pageNo, int pageSize){
  752. super(sql, pageNo, pageSize);
  753. }
  754.  
  755.  
  756. /**
  757. *生成查询一页数据的sql语句
  758. *@param sql 原查询语句
  759. *@startIndex 开始记录位置
  760. *@size 需要获取的记录数
  761. */
  762. protected String intiQuerySQL(String sql, int startIndex, int size){
  763. StringBuffer querySQL = new StringBuffer();
  764. if (size != super.MAX_PAGE_SIZE) {
  765. querySQL.append("select * from (select my_table.*,rownum as my_rownum from(")
  766. .append( sql)
  767. .append(") my_table where rownum<").append(startIndex + size)
  768. .append(") where my_rownum>=").append(startIndex);
  769. } else {
  770. querySQL.append("select * from (select my_table.*,rownum as my_rownum from(")
  771. .append(sql)
  772. .append(") my_table ")
  773. .append(") where my_rownum>=").append(startIndex);
  774. }
  775. return querySQL.toString();
  776. }
  777.  
  778. /**
  779. *将ResultSet数据填充进CachedRowSet
  780. */
  781. protected RowSet populate(ResultSet rs) throws SQLException{
  782. OracleCachedRowSet ocrs = new OracleCachedRowSet();
  783. ocrs.populate(rs);
  784. return ocrs;
  785. }
  786.  
  787. } ***********************************************************************************************************************
  788. 下面是用javabean和jsp页面来实现数据的分页显示,例子中所使用的数据库是Mysql.
  789.          类名:

    databaseBean.java:

    以下为databaseBean.java的代码:

    package loan;
    import java.sql.*;
    import java.util.*;
  790. public class databaseBean
    {
    //这是默认的数据库连接方式
    private String DBLocation="jdbc:mysql://localhost/loan?user=root&password=password&useUnicode=true&characterEncoding=GB2312";
    private String DBDriver="com.mysql.jdbc.Driver";
    private Connection conn=null;
    public databaseBean(){}
  791. //通过set方法可以灵活设置数据库的连接
    public void setDBLocation(String location){DBLocation=location;}
    public void setDBDriver(String driver){DBDriver=driver;}
    public void setconn(Connection conn){this.conn=conn;}
  792. public String getDBLocation(){return(DBLocation);}
    public String getDBDriver(){return(DBDriver);}
    public Connection getconn(){return(conn);}
  793. /*public String DBConnect(){}
    public String DBDisconnect(){}
    public ResultSet query(String sql){}
    public int getTotalPage(String sql,int pageSize){}
    public ResultSet getPagedRs(String sql,int pageSize,int pageNumber){}
    public String execute_sql(String sql){}*/
  794. //建立连接
    public String DBConnect()
    {
    String strExc="Success!";//strExc默认为Success,如果有例外抛出,即数据库连接不成功,则下面几个catch中被赋予其他抛出信息
    try
    {
    Class.forName(DBDriver);
    conn=DriverManager.getConnection(DBLocation);
    }
    catch(ClassNotFoundException e)
    {
    strExc="数据库驱动没有找到,错误提示:<br>" +e.toString();
    }
    catch(SQLException e)
    {
  795. strExc="sql语句错误,错误提示<br>" +e.toString();
    }
    catch(Exception e)
    {
    strExc="错误提示:<br>" +e.toString();
    }
  796. return (strExc);
    }//then end of DBConnect
  797. //断开连接
    public String DBDisconnect()
    {
    String strExc="Success!";//strExc默认为Success,如果有例外抛出,即数据库断开连接不成功,则下面几个catch中被赋予其他抛出信息
  798. try
    {
    if(conn!=null)conn.close();
    }
    catch(SQLException e)
    {
    strExc=e.toString();
    }
    return (strExc);
  799. }
  800.  
  801. //通过传入sql语句来返回一个结果集
    public ResultSet query(String sql) throws SQLException,Exception
    {
    ResultSet rs=null;
    if (conn==null)
    {
    DBConnect();
    }
  802. if (conn==null)
    {
    rs=null;
    }
    else
    {
    try
    {
    Statement s=conn.createStatement();
    rs=s.executeQuery(sql);
    }
    catch(SQLException e){throw new SQLException("Cound not execute query.");}
    catch(Exception e){throw new Exception("Cound not execute query.");}
    }//then end of if
    return(rs);
    }//then end of the function executeQuery
  803. //通过传入sql语句和pageSize(每页所显示的结果数目)计算并返回总共的页数
    public int getTotalPage(String sql,int pageSize)
    {
    ResultSet rs=null;
    int totalRows=0;
    if (conn==null)
    {
    DBConnect();
    }
  804. if (conn==null)
    {
    rs=null;
    }
    else
    try
    {
    Statement s=conn.createStatement();
  805. rs=s.executeQuery(sql);//通过传入的sql得到结果集
  806. while(rs.next())
    totalRows++;//让rs一个个数,数完一遍,通过totalRows++也就计算出了返回结果集中总的条目数
    }
    catch(SQLException e){}
  807. rs=null;
    //由这个算法得出总页数(totalRows-1)/pageSize+1,并返回结果。totalRows是指返回结果集中的总的条目数,pageSize是指每页显示的条目数
    return((totalRows-1)/pageSize+1);
    }

  808. //通过传入sql语句,每页显示的条目数(pageSize)和页码,得到一个结果集
    public ResultSet getPagedRs(String sql,int pageSize,int pageNumber)
    {
    ResultSet rs=null;
    int absoluteLocation;
    if (conn==null)
    {
    DBConnect();
    }
  809. if (conn==null)
    {
    rs=null;
    }
    else
    try
    {
    Statement s=conn.createStatement();
  810. //pageSize*pageNumber每页显示的条目数乘以页码,计算出最后一行结果的编号,任何编号大于这个maxrows的结果都会被drop
    s.setMaxRows(pageSize*pageNumber);
  811. rs=s.executeQuery(sql);
    }
    catch(SQLException e){}
  812. //absoluteLocation=pageSize*(pageNumber-1)这个表达式计算出上一页最后一个结果的编号(如果有本页的话,上一页的显示的结果条目数肯定是pageSize)
    absoluteLocation=pageSize*(pageNumber-1);

  813. try
    {
  814. //这个for循环的作用是让结果集rs定位到本页之前的最后一个结果处
    for(int i=0;i<absoluteLocation;i++)
    {
    rs.next();
    }
    }
    catch(SQLException e) { }
    //此时返回的结果集被两头一夹,就是该页(pageNumber)要显示的结果
    return(rs);
  815. }

  816. public String execute_sql(String sql){
    String strExc;
    strExc="Success!";
  817. if(conn!=null)
    {
    try{
    PreparedStatement update;
    update=conn.prepareStatement(sql);
    update.execute();
    }
    catch(SQLException e)
    {
    strExc=e.toString();
    }
    catch(Exception e)
    {
    strExc=e.toString();
    }
    }
    else
    {
    strExc="Connection Lost!";
    }
    return(strExc);
  818. }}//execute_sql
  819.   ********************************************************************************************************
  820.    分析jsp页面

    页面名称:

    fenye.jsp
  821. 页面代码:

<%@ page errorPage="error1.jsp"%>           
<%@ page contentType="text/html;charset=gb2312"%>
<%@ page import="java.util.*"%>
<%@ page import="java.sql.*"%>
<!--//导入database_basic包下面的databaseBean类,别名是basicDB -->
<jsp:useBean id="basicDB" class="loan.databaseBean" scope="page"/>
<html>
<%
String sql;
ResultSet rs;
int id;
String reply,Exc;
Exc=basicDB.DBConnect();//建立连接,若成功,则返回Success!若失败,则返回相应出错信息

if(!Exc.equals("Success!"))
{
//basicDB.DBDisconnect();
throw new Exception(Exc);
}

int pageSize=3; //定义每页显示的数据条数
int currentPage=1; //当前页(第一次显示的肯定是第一页啦!~),以后的“当前页”由下面出现的页面中的pages参数传入
int allPage=-1;
String pages=request.getParameter("pages");//取得页面中pages参数,此参数代表的页面的就是要显示的“当前页面”

if(pages!=null) currentPage=Integer.valueOf(pages).intValue();//这是一个Integer型转int型的例子,第一次执行这个页面的时候,pages这个参数是null,currentPage=1;当再次执行这个页面的时候,参数pages将被赋值,currentPage=pages的int值

sql="select * from teacher order by id desc";//这样返回的结果集会采用desc降序排列,好处是,显示在前面的是最新的信息

allPage=basicDB.getTotalPage(sql,pageSize);//得到总页码数

rs=basicDB.getPagedRs(sql,pageSize,currentPage);//得到当前页面要显示的结果集

%><body background="images/789.gif">

<table border="0" cellspacing="1" cellpadding="3" width="700" bgcolor="#ffffff">
<%
while(rs.next()){
id=rs.getInt("id");//得到数据库(结果集)中id编号
%>
<tr bgcolor="#FF6600" style="color:white">
<td >姓名:<%=rs.getString("name")%></td>
<td >院系:<%=rs.getString("department")%></td>
<td >专业:<%=rs.getString("zhuanye")%></td>
<td >性别:<%=rs.getString("sex")%></td>
<td >电话:<%=rs.getString("tel")%></td>
</tr>
<tr bgcolor="#FFE3B9">
<td colspan="5"><FONT COLOR="#FF6600">备注:</FONT><BR><BR><%=rs.getString("remark")%> </td>
</tr>
<%}%>

<tr><td height="1"></td></tr>
<tr>
<td colspan="5"align=right bgcolor="#FF6600" style="color:white;">
现在是第<%=currentPage%>页,
<%if(currentPage>1){%>
<!--如果不在第一页,则显示出“首页”链接-->
<A HREF="fenye.jsp?pages=1">首页</A>
<%}
for(int i=1;i<=allPage;i++)
{
//显示出1、2、3、4……到最后一页的链接
out.println("<a href=fenye.jsp?pages="+i+">"+i+"</a>");

}

%>
<%if(currentPage<allPage){%>
<!--如果不在最后一页,则显示出“末页”链接-->
<A HREF="fenye.jsp?pages=<%=(allPage)%>">末页</A>
<%}%></td>
</tr>
</table>

</body>
</html>

效果如图:

  JSP分页技术实现