Java Web -【分页功能】详解
分頁簡介
分頁功能在網頁中是非常常見的一個功能,其作用也就是將數據分割成多個頁面來進行顯示。
- 使用場景: 當取到的數據量達到一定的時候,就需要使用分頁來進行數據分割。
當我們不使用分頁功能的時候,會面臨許多的問題:
- 客戶端的問題: 如果數據量太多,都顯示在同一個頁面的話,會因為頁面太長嚴重影響到用戶的體驗,也不便于操作,也會出現(xiàn)加載太慢的問題。
- 服務端的問題: 如果數據量太多,可能會造成內存溢出,而且一次請求攜帶的數據太多,對服務器的性能也是一個考驗。
分頁的分類
分頁的實現(xiàn)分為真分頁和假分頁兩種,也就是物理分頁和邏輯分頁。
1.真分頁(物理分頁):
- 實現(xiàn)原理: SELECT * FROM xxx [WHERE...] LIMIT #{param1}, #{param2}
第一個參數是開始數據的索引位置
第二個參數是要查詢多少條數據 - 優(yōu)點: 不會造成內存溢出
- 缺點: 翻頁的速度比較慢
2.假分頁(邏輯分頁):
- 實現(xiàn)原理: 一次性將所有的數據查詢出來放在內存之中,每次需要查詢的時候就直接從內存之中去取出相應索引區(qū)間的數據
- 優(yōu)點: 分頁的速度比較快
- 缺點: 可能造成內存溢出
傳統(tǒng)的分頁方式
對于假分頁的實現(xiàn)方式很簡單,只需要準備一個集合保存從數據庫中取出的所有數據,然后根據當前頁面的碼數,取出對應范圍的數據顯示就好了,我們這里基于物理分頁來實現(xiàn)。
分頁的原理
- 頁面中的數據有:
結果集:通過 SQL 語句查詢得來的——List<Student> - 分頁條中的數據有:
當前頁:用戶傳遞到后臺——currentPage
總頁數:計算的來——totalPage
上一頁:計算的來——prePage
下一頁:計算的來——nextPage
尾頁:計算的來(總頁數)——lastPage
頁面大小(即每一頁顯示的條數):用戶傳遞到后臺——count
總條數:通過 SQL 語句查詢得來的——totalCount
可以發(fā)現(xiàn)頁面功能中需要用到的數據有兩個是需要通過 SQL 語句查詢得來的:一個是頁面中顯示的數據 List<Student> ,另一個是數據的總條數 totalCount,分別對應以下兩條 SQL 語句:
- SELECT * FROM student LIMIT #{param1}, #{param2}
- SELECT COUNT(*) FROM student
通過計算得到的數據有:
- 總頁數:totalPage
總頁數 = 總條數 % 頁面大小 == 0 ? 總條數 / 頁面大小 : 總條數 / 頁面大小 + 1 - 上一頁:prePage
上一頁 = 當前頁 - 1 > = 1 ? 當前頁 - 1 : 1 - 下一頁:nextPage
下一頁 = 當前頁 + 1 <= totalPage ? 當前頁 + 1 : totalPage - 尾頁:lastPage
尾頁 = 總條數 % 頁面大小 == 0 ? 總條數 - 頁面大小 : 總條數 - 總條數 % 頁面大小
用戶傳遞的數據:
- 當前頁:currentPage
- 頁面大小:count
所有我們可以創(chuàng)建一個 Page 工具類備用:
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頁elsetotalPage = total / count + 1;if(0==totalPage)totalPage = 1;return totalPage;}/*** 計算得到尾頁* @return*/public int getLast(){int last;// 假設總數是50,是能夠被5整除的,那么最后一頁的開始就是45if (0 == total % count)last = total - count;// 假設總數是51,不能夠被5整除的,那么最后一頁的開始就是50elselast = total - total % count;last = last<0?0:last;return last;}/* getter and setter */ }前臺實現(xiàn)分頁設計
首先我們在前臺需要完成我們分頁條的設計,這里可以直接引入 Bootstrap 來完成:
上面是使用 Bootstrap 實現(xiàn)一個分頁條的簡單例子,如果不熟悉的童鞋可以去菜鳥教程中查看:點這里
簡單版本的分頁條
為了便于理解,我們先來實現(xiàn)一個簡單版本的分頁條吧:
- 首頁超鏈:指向了 start 為 0 的首頁
- 上一頁超鏈:
- 下一頁超鏈:
- 最后一頁超鏈:指向了最后一頁
- 中間頁:
- 所以寫完看起來會是這樣子的:
- 存在的問題:
① 沒有邊界判斷,即在首頁仍然可以點擊前一頁,不符合邏輯也影響用戶體驗
② 會顯示完所有的分頁,即如果 totalPage 有50頁,那么分頁欄將會顯得特別長,影響體驗
改良版本的分頁條
1.寫好頭和尾
<nav class="pageDIV"><ul class="pagination">.....</ul> </nav>2.寫好? ?這兩個功能按鈕
使用 <c:if>標簽來增加邊界判斷,如果沒有前面的頁碼了則設置為disable狀態(tài)
再通過 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>><ahref="?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 循環(huán)到 page.totalPage - 1 ,varStatus 相當于是循環(huán)變量
- status.count 是從1開始遍歷
- status.index 是從0開始遍歷
- 要求:顯示當前頁碼的前兩個和后兩個就可,例如當前頁碼為3的時候,就顯示 1 2 3(當前頁) 4 5 的頁碼
- 理解測試條件:
-10 <= 當前頁*每一頁顯示的數目 - 當前頁開始的數據編號 <= 30 - 只要理解了這個判斷條件,其他的就都好理解了
- 注意: 測試條件是需要根據項目的需求動態(tài)改變的,不是萬能的!
后臺中的分頁
首頁在項目中引入上面提到的 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 的版本,在之前寫過的項目(學生管理系統(tǒng)(簡易版))中實際的使用了這種方法,感興趣的可以去看一下。
SSM 中的分頁
在 SSM 項目中,我們可以使用 MyBatis 的一款分頁插件: PageHelper 來幫助我們更加簡單的完成分頁的需求,官網在這里: PageHelper
在這里,我們演示一下如何使用上面的工具重構我們之前寫過的 SSM 項目 —— 學生管理系統(tǒng)-SSM 版
第一步:添加相關 jar 依賴包
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>第二步:配置相關環(huán)境
在 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"; }重啟服務器,能看到也能夠正確的使用分頁功能。
總結
其實我自己對于這個工具比較無感..因為只是弱化了少一部分的功能,并沒有我想象中的那樣 “智能” ,也沒有看到什么好的博文能夠點通我的認知,希望了解的大大們能無私分享一下,謝謝!
歡迎轉載,轉載請注明出處!
簡書ID:@我沒有三顆心臟
作者:我沒有三顆心臟
鏈接:https://www.jianshu.com/p/d108d0cd9acf
來源:簡書
著作權歸作者所有。商業(yè)轉載請聯(lián)系作者獲得授權,非商業(yè)轉載請注明出處。
總結
以上是生活随笔為你收集整理的Java Web -【分页功能】详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 《Effective-Ruby》读书笔记
- 下一篇: 狼羊菜过河问题深入学习分析——Java语