分頁功能在網頁中是很是常見的一個功能,其做用也就是將數據分割成多個頁面來進行顯示。javascript
- 使用場景: 當取到的數據量達到必定的時候,就須要使用分頁來進行數據分割。
當咱們不使用分頁功能的時候,會面臨許多的問題:php
- 客戶端的問題: 若是數據量太多,都顯示在同一個頁面的話,會由於頁面太長嚴重影響到用戶的體驗,也不便於操做,也會出現加載太慢的問題。
- 服務端的問題: 若是數據量太多,可能會形成內存溢出,並且一次請求攜帶的數據太多,對服務器的性能也是一個考驗。
分頁的實現分爲真分頁和假分頁兩種,也就是物理分頁和邏輯分頁。html
1.真分頁(物理分頁):java
- 實現原理:
SELECT * FROM xxx [WHERE...] LIMIT #{param1}, #{param2}
第一個參數是開始數據的索引位置
第二個參數是要查詢多少條數據- 優勢: 不會形成內存溢出
- 缺點: 翻頁的速度比較慢
2.假分頁(邏輯分頁):git
- 實現原理: 一次性將全部的數據查詢出來放在內存之中,每次須要查詢的時候就直接從內存之中去取出相應索引區間的數據
- 優勢: 分頁的速度比較快
- 缺點: 可能形成內存溢出
對於假分頁的實現方式很簡單,只須要準備一個集合保存從數據庫中取出的全部數據,而後根據當前頁面的碼數,取出對應範圍的數據顯示就行了,咱們這裏基於物理分頁來實現。github
- 頁面中的數據有:
結果集:經過 SQL 語句查詢得來的——List<Student>- 分頁條中的數據有:
當前頁:用戶傳遞到後臺——currentPage
總頁數:計算的來——totalPage
上一頁:計算的來——prePage
下一頁:計算的來——nextPage
尾頁:計算的來(總頁數)——lastPage
頁面大小(即每一頁顯示的條數):用戶傳遞到後臺——count
總條數:經過 SQL 語句查詢得來的——totalCount
能夠發現頁面功能中須要用到的數據有兩個是須要經過 SQL 語句查詢得來的:一個是頁面中顯示的數據 List<Student> ,另外一個是數據的總條數 totalCount,分別對應如下兩條 SQL 語句:spring
SELECT * FROM student LIMIT #{param1}, #{param2}
SELECT COUNT(*) FROM student
經過計算獲得的數據有:sql
- 總頁數:totalPage
總頁數 = 總條數 % 頁面大小 == 0 ? 總條數 / 頁面大小 : 總條數 / 頁面大小 + 1- 上一頁:prePage
上一頁 = 當前頁 - 1 > = 1 ? 當前頁 - 1 : 1- 下一頁:nextPage
下一頁 = 當前頁 + 1 <= totalPage ? 當前頁 + 1 : totalPage- 尾頁:lastPage
尾頁 = 總條數 % 頁面大小 == 0 ? 總條數 - 頁面大小 : 總條數 - 總條數 % 頁面大小
用戶傳遞的數據:數據庫
- 當前頁:currentPage
- 頁面大小:count
全部咱們能夠建立一個 Page 工具類備用:bootstrap
public class Page { int start; // 開始數據的索引 int count; // 每一頁的數量 int total; // 總共的數據量 /** * 提供一個構造方法 * @param start * @param count */ public Page(int start, int count) { super(); this.start = start; this.count = count; } /** * 判斷是否有上一頁 * @return */ public boolean isHasPreviouse(){ if(start==0) return false; return true; } /** * 判斷是否有下一頁 * @return */ public boolean isHasNext(){ if(start==getLast()) return false; return true; } /** * 計算獲得總頁數 * @return */ public int getTotalPage(){ int totalPage; // 假設總數是50,是可以被5整除的,那麼就有10頁 if (0 == total % count) totalPage = total /count; // 假設總數是51,不可以被5整除的,那麼就有11頁 else totalPage = total / count + 1; if(0==totalPage) totalPage = 1; return totalPage; } /** * 計算獲得尾頁 * @return */ public int getLast(){ int last; // 假設總數是50,是可以被5整除的,那麼最後一頁的開始就是45 if (0 == total % count) last = total - count; // 假設總數是51,不可以被5整除的,那麼最後一頁的開始就是50 else last = total - total % count; last = last<0?0:last; return last; } /* getter and setter */ }
首先咱們在前臺須要完成咱們分頁條的設計,這裏能夠直接引入 Bootstrap 來完成:
上面是使用 Bootstrap 實現一個分頁條的簡單例子,若是不熟悉的童鞋能夠去菜鳥教程中查看:點這裏
爲了便於理解,咱們先來實現一個簡單版本的分頁條吧:
- 首頁超鏈:指向了 start 爲 0 的首頁
<li> <a href="?page.start=0"> <span>«</span> </a> </li>
- 上一頁超鏈:
<li > <a href="?page.start=${page.start-page.count}"> <span>‹</span> </a> </li>
- 下一頁超鏈:
<li > <a href="?page.start=${page.start+page.count}"> <span>›</span> </a> </li>
- 最後一頁超鏈:指向了最後一頁
<li > <a href="?page.start=${page.last}"> <span>»</span> </a> </li>
- 中間頁:
<c:forEach begin="0" end="${page.totalPage-1}" varStatus="status"> <li> <a href="?page.start=${status.index*page.count}" class="current">${status.count}</a> </li> </c:forEach>
- 因此寫完看起來會是這樣子的:
<nav>
<ul class="pagination"> <li> <a href="?page.start=0"> <span>«</span> </a> </li> <li > <a href="?page.start=${page.start-page.count}"> <span>‹</span> </a> </li> <c:forEach begin="0" end="${page.totalPage-1}" varStatus="status"> <li> <a href="?page.start=${status.index*page.count}" class="current">${status.count}</a> </li> </c:forEach> <li > <a href="?page.start=${page.start+page.count}"> <span>›</span> </a> </li> <li > <a href="?page.start=${page.last}"> <span>»</span> </a> </li> </ul> </nav>
1.寫好頭和尾
<nav class="pageDIV"> <ul class="pagination"> ..... </ul> </nav>
2.寫好«
‹
這兩個功能按鈕
使用 <c:if>
標籤來增長邊界判斷,若是沒有前面的頁碼了則設置爲disable狀態
<li <c:if test="${!page.hasPreviouse}">class="disabled"</c:if>> <a href="?page.start=0"> <span>«</span> </a> </li> <li <c:if test="${!page.hasPreviouse}">class="disabled"</c:if>> <a href="?page.start=${page.start-page.count}"> <span>‹</span> </a> </li>
再經過 JavaScrip 代碼來完成禁用功能:
<script> $(function () { $("ul.pagination li.disabled a").click(function () { return false; }); }); </script>
3.完成中間頁碼的編寫
<c:forEach begin="0" end="${page.totalPage-1}" varStatus="status"> <c:if test="${status.count*page.count-page.start<=30 && status.count*page.count-page.start>=-10}"> <li <c:if test="${status.index*page.count==page.start}">class="disabled"</c:if>> <a href="?page.start=${status.index*page.count}" <c:if test="${status.index*page.count==page.start}">class="current"</c:if> >${status.count}</a> </li> </c:if> </c:forEach>
從 0
循環到 page.totalPage - 1
,varStatus
至關因而循環變量
- 注意: 測試條件是須要根據項目的需求動態改變的,不是萬能的!
首頁在項目中引入上面提到的 Page 工具類,而後咱們在 DAO 類中使用 LIMIT 關鍵字來查詢數據庫中的信息:
public List<Student> list() { return list(0, Short.MAX_VALUE); } public List<Student> list(int start, int count) { List<Student> students = new ArrayList<>(); String sql = "SELECT * FROM student ORDER BY student_id desc limit ?,?"; try (Connection c = DBUtil.getConnection(); PreparedStatement ps = c.prepareStatement(sql)) { ps.setInt(1, start); ps.setInt(2, count); // 獲取結果集... } catch (SQLException e) { e.printStackTrace(); } return students; }
在 Servlet 中獲取分頁參數並使首頁顯示的 StudentList 用 page 的參數來獲取:
// 獲取分頁參數 int start = 0; int count = 10; try { start = Integer.parseInt(req.getParameter("page.start")); count = Integer.parseInt(req.getParameter("page.count")); } catch (Exception e) { } Page page = new Page(start, count); List<Student> students = studentDAO.list(page.getStart(), page.getCount()); .... // 共享數據 req.setAttribute("page", page); req.setAttribute("students", students);
以上便可完成分頁功能,但這是基於 Servlet 的版本,在以前寫過的項目(學生管理系統(簡易版))中實際的使用了這種方法,感興趣的能夠去看一下。
在 SSM 項目中,咱們能夠使用 MyBatis 的一款分頁插件: PageHelper 來幫助咱們更加簡單的完成分頁的需求,官網在這裏: PageHelper
在這裏,咱們演示一下如何使用上面的工具重構咱們以前寫過的 SSM 項目 —— 學生管理系統-SSM 版
PageHelper 須要依賴兩個 jar 包,咱們直接在 pom.xml 中增長兩個 jar 包依賴:
<!-- pageHelper --> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.1.2-beta</version> </dependency> <!--jsqlparser--> <dependency> <groupId>com.github.jsqlparser</groupId> <artifactId>jsqlparser</artifactId> <version>1.0</version> </dependency>
在 MyBatis 的 SessionFactory 配置中新增長一個屬性名 plugins 的配置:
<!-- 配置SqlSessionFactory對象 --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 注入數據庫鏈接池 --> <property name="dataSource" ref="dataSource"/> <!-- 掃描entity包 使用別名 --> <property name="typeAliasesPackage" value="cn.wmyskxz.entity"/> <!-- 掃描sql配置文件:mapper須要的xml文件 --> <property name="mapperLocations" value="classpath:mapper/*.xml"/> <!-- 讓MyBatis支持PageHelper插件 --> <property name="plugins"> <array> <bean class="com.github.pagehelper.PageInterceptor"> <property name="properties"> <!--使用下面的方式配置參數,一行配置一個 --> <value> </value> </property> </bean> </array> </property> </bean>
首先咱們把 LIMIT 關鍵字從映射文件中幹掉:
<!-- 查詢從start位置開始的count條數據--> <select id="list" resultMap="student"> SELECT * FROM student ORDER BY student_id desc </select>
而後註釋掉查詢數據總條數的 SQL 語句:
<!--<!– 查詢數據條目 –>--> <!--<select id="getTotal" resultType="int">--> <!--SELECT COUNT(*) FROM student--> <!--</select>-->
在 Dao 類和 Service 類中修改相應的地方:
而後修改掉 StudentController 中的方法:
@RequestMapping("/listStudent") public String listStudent(HttpServletRequest request, HttpServletResponse response) { // 獲取分頁參數 int start = 0; int count = 10; try { start = Integer.parseInt(request.getParameter("page.start")); count = Integer.parseInt(request.getParameter("page.count")); } catch (Exception e) { } Page page = new Page(start, count); // 使用 PageHelper 來設置分頁 PageHelper.offsetPage(page.getStart(),page.getCount()); List<Student> students = studentService.list(); // 使用 PageHelper 來獲取總數 int total = (int) new PageInfo<>(students).getTotal(); page.setTotal(total); request.setAttribute("students", students); request.setAttribute("page", page); return "listStudent"; }
重啓服務器,能看到也可以正確的使用分頁功能。
其實我本身對於這個工具比較無感..由於只是弱化了少一部分的功能,並無我想象中的那樣 「智能」 ,也沒有看到什麼好的博文可以點通個人認知,但願瞭解的大大們能無私分享一下,謝謝!
歡迎轉載,轉載請註明出處!轉載自@我沒有三顆心臟