JDBC使用數據庫來完成分頁功能


  本篇講訴如何在頁面中通過操作數據庫來完成數據顯示的分頁功能。當一個操作數據庫進行查詢的語句返回的結果集內容如果過多,那么內存極有可能溢出,所以在大數據的情況下分頁是必須的。當然分頁能通過很多種方式來實現,而這里我們采用的是操作數據庫的方式,而且在這種方式中,最重要的是帶限制條件的查詢SQL語句:

select name from user limit m,n

  其中m與n為數字。n代表需要獲取多少行的數據項,而m代表從哪開始(以0為起始),例如我們想從user表中先獲取前五行數據項(1-5)的name列數據,則SQL為:

select name from user limit 0,5;

  那么如果要繼續往下看一頁五行的數據項(6-10)則下一步的SQL應該為:

select name from user limit 5,5;

  再下一頁五行的數據項(11-15)的SQL就為:

select name from user limit 15,5;

。。。

  如果對上面的SQL語句不熟悉的話,請先查詢相關文檔再來看本篇內容。

  我們先來看看“百度貼吧”中的分頁效果:

  

  

  

  我選取了首頁、次頁和尾頁三種情況的顯示效果,可以看到這個分頁顯示的效果是比較靈活多變的,開發者可以依據自己的愛好進行展示,但是實質是不變的。

 

  那么接下來我們將做出如下效果的頁面顯示:

  

  在這個頁面中,其實按面向對象的思考方式,這個頁面就是一個對象,稍后我們會說到,先來看看在該頁面下方的頁碼分頁顯示,探究當用戶點擊之后,分頁請求是如何一步步到底層的。

基本流程就如上圖所示,那么我們來分析這個過程:

  第一步:當用戶在頁面上點擊某一頁,或者下一頁、上一頁等等這些超鏈接,根據MVC設計模式,這些請求都是交給Servlet處理。

  第二步:在Servlet中,首先應該將請求對象帶來的信息封裝到對象中,由於我們是要查詢數據庫,因此必須封裝成一個查詢的條件對象,這里舉例為“QueryInfo”自定義對象,在剛對象中包含當前頁、每頁多少條數據、等會查詢數據庫從哪開始等信息,只有擁有了這些信息,才能在數據庫查詢的時候能根據順序往下翻頁。

  第三步、第四步:根據web工程的三層設計模式,業務從service層一步步到dao層。

  第五步、第六步:通過上層傳下來的QueryInfo對象,根據里面封裝的信息開始對數據庫進行操作,使用select name from user limit startIndex,pageSize 這樣的SQL命令,將查詢到的結果集返回給dao層。

  第七步:dao層根據從數據庫返回的結果集,提取出用戶想看的頁面數據,這里我們將頁面數據都封裝到一個集合中,除此之外為了之后在頁面上能顯示頁碼之類的數據,還必須要獲取到查詢的總記錄數。前面這兩個信息數據我們以“QueryResult”自定義對象來封裝。

  第八步、第九步:在service層,通過dao傳遞上來的查詢結果“QueryResult”對象,和一開始的查詢信息“QueryInfo”對象,來構建頁面顯示信息,例如頁面數據、總記錄數、總頁數、當前頁、上一頁、下一頁、頁碼條等等,我們將其都封裝進“PageBean”這個JavaBean中,對於JSP中要顯示的動態數據,我們只需要提取PageBean對象中的屬性即可。其實PageBean的對象需要哪些屬性,只要看在JSP頁面中我們想顯示什么數據就行了,設計還是很簡單。

  第十步、第十一步:service層將頁面所需要的信息封裝進PageBean對象后,將其傳給web層的Servlet,由MVC模式,Servlet再將PageBean對象封裝進請求交給JSP來顯示。

 

  了解完一個分頁功能的實現流程之后,下面我將開始進行分頁的實現。

  上面的步驟涉及到三個實體對象,分別是QueryInfo,QueryResult、PageBean,而我們在工程中先構建這三個實體,在這三個實體中,有些屬性是可以根據別的屬性計算出來的,我們沒必要提供setter方法。

實體QueryInfo對象:

 1 public class QueryInfo {  2     private int currentPage = 1;   //用戶當前看的頁數
 3     private int pageSize = 10;        //每頁多少條顯示數據
 4     private int startIndex;            //記住用戶想看的頁的數據在數據庫的起始位置
 5     
 6     。。。  //此處省略currentPage和pageSize兩個屬性的set和get方法
 7 
 8     public int getStartIndex() {  9         this.startIndex = (this.currentPage-1)*this.pageSize; 10         return startIndex; 11  } 12 }
View Code

  注:在查詢信息對象QueryInfo中,currentPage和pageSize屬性都設置了默認值,如果用戶沒有特意設置每頁顯示多少條數據,則根據默認值進行計算。另外由於startIndex屬性可以由另外兩個屬性計算出,因此無需set方法。

實體PageBean對象:

 1 public class PageBean {  2     private List contentData;    //保存頁面數據
 3     private int totalRecords;    //查詢到的總記錄數
 4     private int currentPage;       //用戶當前看的頁數
 5     private int pageSize;        //每頁多少條顯示數據
 6     private int totalPages;        //總頁數
 7     private int previousPage;    //上一頁
 8     private int nextPage;        //下一頁
 9     private int[] pageBar;        //頁碼條 10 
11 //1,contentData可以從QueryResult對象中獲取
12     。。。//此處省略contentData屬性的set和get方法 13     
14     //2,totalRecords可以從QueryResult對象中獲取
15     。。。//此處省略contentData屬性的set和get方法 16 
17     //3,currentPage可以從QueryInfo對象中獲取
18     。。。//此處省略contentData屬性的set和get方法 19 
20     //4,pageSize可以從QueryInfo對象中獲取
21 。。。//此處省略contentData屬性的set和get方法 22 
23 //5,總頁數可以由總頁數和頁面數據大小這兩個屬性計算,因此無需set方法
24     public int getTotalPages() { 25         if(totalRecords % pageSize ==0){ 26             totalPages = totalRecords / pageSize; 27         }else{ 28             totalPages = totalRecords / pageSize + 1; 29  } 30         return totalPages; 31  } 32 
33 //6,上一頁可以根據當前頁計算,因此無需set方法
34     public int getPreviousPage() { 35         if(currentPage == 1) { 36             previousPage = 1; 37         }else { 38             previousPage = currentPage - 1; 39  } 40         return previousPage; 41  } 42 
43     //7,下一頁可以根據當前頁計算,因此無需set方法
44     public int getNextPage() { 45         if(currentPage == totalPages) { 46             nextPage = totalPages; 47         }else { 48             nextPage = currentPage + 1; 49  } 50         return nextPage; 51  } 52 
53     //8,頁碼條可以由總頁數來計算顯示,因此無需set方法
54     public int[] getPageBar() { 55         pageBar = null; 56         int startIndex ; 57         int endIndex ; 58         if(totalPages<10) { 59             pageBar = new int[totalPages]; 60             startIndex = 1; 61             endIndex = totalPages; 62             
63         }else{ 64             pageBar = new int[10]; 65             startIndex = currentPage-5; 66             endIndex = currentPage+4; 67             if(startIndex<1) { 68                 startIndex = 1; 69                 endIndex = 10; 70  } 71             if(endIndex>totalPages) { 72                 startIndex = totalPages-10+1; 73                 endIndex = totalPages; 74  } 75  } 76         int index = 0; 77         for(int i=startIndex;i<=endIndex;i++) { 78             pageBar[index] = i; 79             index++; 80  } 81         return pageBar; 82     }}
View Code

注:PageBean對象屬性會比較多,因為這些屬性都是要在頁面上顯示的內容。雖然屬性多,但是由很多屬性值可以通過別的屬性計算得到,另外的屬性可以通過別的對象屬性得到。

  尤其是頁碼條pageBar這個屬性,這里我的設計是,如果總頁數不超過10頁的話,那么頁碼條顯示的個數就為總頁數;如果總頁數超過10頁,那么頁碼條固定顯示10個頁碼,同時如果當前頁在最前6個頁則頁碼條保持不變,在中間部分的當前頁會保持在頁碼條的中間位置(前面5個頁碼,后面4個頁碼,當前頁在第6個位置)。如果當前頁到最后部分也是同理。

 

  上面三個對象設計完成后,我們就要來考慮在分頁流程中不同層對查詢信息的處理方式。

  按從下到上的開發流程,首先是dao層,該層必須通過請求發來的查詢信息來對數據庫進行操作,也就是本文最開始講解的SQL語句的兩個參數是執行數據庫操作的關鍵,本文以顯示User用戶為分頁案例,在數據庫中為user表。因此在處理User對象的dao層實現類UserDaoImpl中,查詢方法為pageQuery,返回上面剛剛定義的QueryResult對象。

  注:該工程是博客《JDBC操作數據庫的學習(2)》和《在JDBC中使用PreparedStatement代替Statement,同時預防SQL注入》中工程的擴展,下面使用到JDBC的工具類JdbcUtils即是在《JDBC操作數據庫的學習(2)》中的定義。

  下面的代碼對應流程圖中的第五、六、七步驟:

 1 package com.fjdingsd.dao.impl;  2 public class UserDaoImpl implements UserDao {  3     public QueryResult pageQuery(int startIndex,int pageSize) {  4         Connection conn = null;  5         PreparedStatement st = null;  6         ResultSet rs = null;  7         QueryResult result = new QueryResult();  8         try{  9             conn = JdbcUtils.getConnection(); 10             String sql = "select * from user limit ?,?"; 11             st = conn.prepareStatement(sql); 12             st.setInt(1, startIndex); 13             st.setInt(2, pageSize); 14             rs = st.executeQuery(); 15             List contentList = new ArrayList(); 16             while(rs.next()) { 17                 User user = new User(); 18                 user.setId(rs.getInt("id")); 19                 user.setName(rs.getString("name")); 20                 user.setAge(rs.getInt("age")); 21  contentList.add(user); 22  } 23  result.setContentData(contentList); 24             //獲取了頁面數據后還沒結束,還得獲取總記錄數
25             sql = "select count(*) from user"; 26             st = conn.prepareStatement(sql); 27             rs = st.executeQuery(); 28             if(rs.next()) { 29                 int totalRecords = rs.getInt(1);  //rs.getInt("count(*)")也是可以的
30  result.setTotalRecords(totalRecords); 31  } 32             return result; 33         }catch (Exception e) { 34             throw new RuntimeException(e); 35         }finally{ 36  JdbcUtils.release(conn, st, rs); 37  } 38  } 39 }
View Code

  上面在dao層對User對象處理的實現類UserDaoImpl已經處理好了分頁查詢,該pageQuery方法返回的QueryResult對象正是在service層中處理User對象的業務的方法所需要的參數。在service層中,我們需要根據查詢得到的結果QueryResult對象,來獲取頁面顯示所需要的對象PageBean。

  下面的代碼對應流程圖的第三、四和第八、九步驟:

 1 package com.fjdingsd.service;  2 public class UserServiceImpl {  3     private UserDao userDao = new UserDaoImpl(); //通常使用工程模式獲取實現類對象,這里為了簡便直接采用實現類的構造器
 4     
 5     public PageBean pageQuery(QueryInfo info) {  6         //獲取對應dao的實現類中的查詢到的結果數據
 7         QueryResult result = userDao.pageQuery(info.getStartIndex(), info.getPageSize());  8         
 9         //根據dao的查詢結果,生成頁面顯示需要的PageBean
10         PageBean page = new PageBean(); 11  page.setContentData(result.getContentData()); 12  page.setTotalRecords(result.getTotalRecords()); 13  page.setCurrentPage(info.getCurrentPage()); 14  page.setPageSize(info.getPageSize()); 15         
16         return page; 17  } 18 }
View Code

  上面在service層將查詢到的結果對象封裝成頁面顯示所需要的對象PageBean,service層需要將這個對象交給web層的Servlet來處理,其實這個Servlet也是最開始處理請求對象的Servlet,因為最開始要想生成查詢信息QueryInfo對象就必須要從請求中提取數據封裝。

    注:下面代碼中使用到了工具類的靜態方法WebUtils.request2Bean,是將請求對象中的參數值轉移到一個Bean對象中,該方法的實現具體請看《在WEB工程的web層中的編程技巧》。即使Request對象中沒有我們需要的參數,那么創建出來的QueryInfo對象中的currentPage和pageSize屬性我們在最開始創建時已經設置了默認值,所以無需擔心空指針異常。

    下面的代碼對應流程圖中的第一,二和第十、十一步驟:

 1 package com.fjdingsd.web.controller;  2 public class UserListServlet extends HttpServlet {  3 
 4     public void doGet(HttpServletRequest request, HttpServletResponse response)  5             throws ServletException, IOException {  6         try{  7             QueryInfo info = WebUtils.request2Bean(request, QueryInfo.class);  8             UserService userService = new UserServiceImpl();//通常使用工程模式獲取實現類對象,這里為了簡便直接采用實現類的構造器
 9 
10             PageBean page = userService.pageQuery(info); 11             request.setAttribute("pagebean", page); 12             request.getRequestDispatcher("/WEB-INF/jsp/userlist.jsp").forward(request, response); 13         }catch (Exception e) { 14  e.printStackTrace(); 15             request.setAttribute("message", "查看用戶失敗"); 16             request.getRequestDispatcher("/message.jsp").forward(request, response); 17  } 18  } 19 }
View Code

  上面在web層中已經使用Servlet將頁面需要顯示的信息全部封裝進PageBean對象中,通過請求對象Request存儲,最后轉發進相應的JSP頁面,這里例子為userlist.jsp頁面,最后只要在這個頁面中將請求對象中保存的PageBean對象提取出來,再將該對象中的每個屬性的內容在頁面相應的地方顯示即可。

  在JSP頁面中,我們以表格的形式將頁面數據顯示出來,除了用戶想看的頁面數據以外,其他的就是與頁碼相關的,因為在Servlet中我們將PageBean對象封裝進請求Request對象中,所以在JSP頁面中我們就可以通過EL表達式將其取出,而是會是大量地使用到EL表達式和JSP標簽。

 1 <body>
 2         <a href="${pageContext.request.contextPath}/servlet/UserListServlet">顯示用戶</a><br>       
 3         <table>
 4             <tr>
 5                 <td>用戶id</td>
 6                 <td>用戶姓名</td>
 7                 <td>用戶年齡</td>
 8             </tr>
 9             <c:forEach var="user" items="${requestScope.pagebean.contentData }">
10                 <tr>
11                 <td>${user.id}</td>
12                 <td>${user.name}</td>
13                    <td>${user.age}</td>
14             </tr>
15             </c:forEach>        
16         </table>
17         <br>
18   <%-- 
19  共${pagebean.totalRecords} 條記錄,每頁${pagebean.pageSize}條,共${pagebean.totalPages}頁, 20  當前第${pagebean.currentPage}頁 &nbsp;&nbsp; 21    <a href="${pageContext.request.contextPath}/servlet/UserListServlet?currentPage=${pagebean.currentPage}">上一頁</a>
22            <c:forEach var="bar" items="${requestScope.pagebean.pageBar}">
23                <a href="${pageContext.request.contextPath}/servlet/UserListServlet?currentPage=${pagebean.currentPage}">${bar}</a>
24            </c:forEach>
25    <a href="${pageContext.request.contextPath}/servlet/UserListServlet?currentPage=${pagebean.currentPage}">下一頁</a>
26    --%>    
27    
28  共 ${pagebean.totalRecords} 條記錄, 29     每頁<input type="text" id="pagesize" value="${pagebean.pageSize }" onchange="gotopage(1)" style="width: 30px" maxlength="3">條, 30  共${pagebean.totalPages}頁, 31     當前第${pagebean.currentPage}頁  &nbsp;&nbsp;
32    <a href="javascript:void(0)" onclick="gotopage(${pagebean.previousPage})" >上一頁</a> &nbsp;
33            <c:forEach var="bar" items="${requestScope.pagebean.pageBar}">
34                <a href="javascript:void(0)" onclick="gotopage(${bar})" >${bar}</a>&nbsp;
35            </c:forEach>
36    <a href="javascript:void(0)" onclick="gotopage(${pagebean.nextPage})" >下一頁</a> &nbsp;
37    
38    跳轉<input type="text" id="forwardPage" value="${pagebean.currentPage}" style="width: 30px;" onchange="gotopage(this.value)">39    
40   </body>
41   
42   <script type="text/javascript">
43       function gotopage(wantedPage) { 44           var pagesize = document.getElementById("pagesize").value; 45  window.location.href = "${pageContext.request.contextPath}/servlet/UserListServlet?currentPage="+wantedPage+"&pageSize="+pagesize; 46           
47  } 48 
49   </script>
View Code

  在上面的代碼中,我們使用了JSTL標簽庫的<c:forEach>標簽來迭代頁面數據內容,也就是PageBean中的contentData集合。中間有一段的代碼雖然被注釋掉了,這里是用URL地址的方法給每個<a>標簽中的href屬性賦值超鏈接,在后面的代碼中我使用的是JavaScript的方式。

  在Servlet中跳轉到JSP頁面的請求對象中設置了PageBean對象的關鍵字:request.setAttribute("pagebean", page); 因此在JSP中,使用EL表達式將以“pagebean”為關鍵字,而后面跟着PageBean對象的屬性取出對應的值。

  在JavaScript中,上面無論是改變頁面大小、上一頁、下一頁,某個特定頁,跳轉某頁,都是根據gotopage方法來講請求超鏈接發送給Servlet,再一步步發送到數據庫查詢。在gotopage方法中,傳入參數是“wantedPage”,是用戶想要去的頁數,同時每次該方法調用還會獲取頁面大小“pagesize”,將這兩個數放置在URL地址后作為請求參數給Servlet。

  最終效果如下:

  

  

 

 

 

 

 

 

           

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM