JSP分頁技術實現

 目前比較廣泛使用的分頁方式是將查詢結果緩存在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>

  效果如圖:


  因爲分頁顯示一般都會伴有查詢條件和查詢動作,頁面應已經有校驗查詢條件和提交查詢的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 [email protected]字樣
  1.  
  2. ///////////////////////////////////
  3. //
  4. // Page.java
  5. // author: [email protected]
  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 [email protected] <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: [email protected]
  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 [email protected]
  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: [email protected]
  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 [email protected]
  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: [email protected]
  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 [email protected]
  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>

效果如圖:

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章