分页功能在网页中是很是常见的一个功能,其做用也就是将数据分割成多个页面来进行显示。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"; }
重启服务器,能看到也可以正确的使用分页功能。
其实我本身对于这个工具比较无感..由于只是弱化了少一部分的功能,并无我想象中的那样 “智能” ,也没有看到什么好的博文可以点通个人认知,但愿了解的大大们能无私分享一下,谢谢!
欢迎转载,转载请注明出处!转载自@我没有三颗心脏