1. 为什么要使用分页?
a) 当数据量比较大的时候, 一次性查找所有数据会带来一些问题
b) 数据库压力会比较大
c) 数据的显示会很慢, 造成用户体验度不高
d) 为了更方便的显示数据, 并且减小数据库的压力, 就会使用分页的技术
2. 如何实现分页查询
a) MySql中分页查询的实现方式
-- limit用于分页查询, 后面有两个数字 -- 第一个数字表示查询起始位置的索引(从0开始) -- 第二个数字表示每次查询数据的个数 select * from emp limit 10,10; -- 必须知道当前页数(page), 每页显示的条数(size) -- select * from emp limit (page-1)*size,size; |
3. 分页的代码实现
a) 定义分页实体类, 用于封装分页相关属性, 方便后续的分页实现, 代码如下(省略Getter和Setter方法)
package com.bjsxt.pojo; import java.util.List; public class Pagination<T> { // 当前页数 private int page; // 每页显示多少条数据 private int size; // 总记录数 private int totalCount; // 总页数 private int totalPage; // 每次查询的起始位置 private int start; // 是否有上一页 private boolean hasPrev; // 上一页 private int prevPage; // 是否有下一页 private boolean hasNext; // 下一页 private int nextPage; // 首页 private int firstPage; // 尾页 private int lastPage; // 分页导航块的个数 private int navCount; // 导航的起始位置 private int navBegin; // 导航的结束位置 private int navEnd; // 查询结果对应的list集合 private List<T> list; public Pagination(String page, String size, int totalCount) { // 当前页数 this.page = null != page ? Integer.parseInt(page) : 1; // 每页显示多少条数据 this.size = null != size ? Integer.parseInt(size) : 10; // 总记录数 this.totalCount = totalCount; // 总页数 this.totalPage = (int) Math.ceil(this.totalCount * 1.0 / this.size); // 当前页数不能大于总页数 if(this.page > this.totalPage && this.totalPage > 0) { this.page = this.totalPage; } // 每次查询的起始位置 this.start = (this.page - 1) * this.size; if (this.page > 1) { // 是否有上一页 this.hasPrev = true; // 上一页 this.prevPage = this.page - 1; } if (this.page < this.totalPage) { // 是否有下一页 this.hasNext = true; // 下一页 this.nextPage = this.page + 1; } // 首页 this.firstPage = 1; // 尾页 this.lastPage = this.totalPage; // 分页导航块的个数 this.navCount = 10; // 导航的起始位置 // if (this.page > this.totalPage - this.navCount) { // this.navBegin = this.totalPage - this.navCount + 1; // } else { this.navBegin = this.page - this.navCount / 2 < 1 ? 1 : this.page - this.navCount / 2; // } // 导航的结束位置 this.navEnd = this.navBegin + this.navCount - 1 > this.totalPage ? this.totalPage : this.navBegin + this.navCount - 1; } // 省略getter和setter方法 } |
b) 在实体类中, 提供了一个构造器, 有三个参数, 分别为当前页page, 每页显示条数size和总记录数totalCount, 提供这三个参数, 是因为只要知道了这三个数据, 分页相关的其他数据都可以确定
c) 分页实体类的使用, 在Servlet中接收相关参数, 封装为实体类对象, 之后调用service, service再调用dao即可, 代码如下:
public void queryAll(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String page = req.getParameter("page"); String size = req.getParameter("size"); int totalCount = es.queryCount(); // 创建分页对象 Pagination<Emp> p = new Pagination<Emp>(page, size, totalCount); // 调用service中的方法, 完成后查询的结果自动设置到p对象中 es.queryAll(p); // 将p对象存储到作用域中, 方便jsp中使用 req.setAttribute("p", p); // 请求转发 req.getRequestDispatcher("/emplist.jsp").forward(req, resp); } |
@Override public void queryAll(Pagination<Emp> p) { // 调用dao dao.queryAll(p); } |
@Override public void queryAll(Pagination<Emp> p) { p.setList(query(Emp.class, "select * from emp limit ?,?", p.getStart(), p.getSize())); } |
d) jsp中分页的实现代码
为您查询到 <font color="blue">${p.totalCount }</font> 条记录, 当前第 <font color="blue">${p.page }</font> 页, 共有 <font color="blue">${p.totalPage }</font> 页 <table> <tr> <td>编号</td> <td>姓名</td> <td>薪资</td> <td>入职日期</td> <td>操作</td> </tr> <c:forEach items="${p.list }" var="e"> <tr> <td>${e.empno }</td> <td>${e.ename }</td> <td>${e.sal }</td> <td>${e.hiredate }</td> <td><a href="query.sxt?op=queryByEmpno&empno=${e.empno }&page=${p.page}&size=${p.size}">修改</a> / <a href="query.sxt?op=deleteEmp&empno=${e.empno }&page=${p.page}&size=${p.size}" onclick="return confirm('确定删除吗?');">删除</a></td> </tr> </c:forEach> </table> <a href="query.sxt?op=queryAll&page=${p.firstPage }&size=${p.size}">首页</a> | <a href="query.sxt?op=queryAll&page=${p.prevPage }&size=${p.size}" onclick="return ${p.hasPrev?true:false}">上一页</a> | <c:forEach begin="${p.navBegin }" end="${p.navEnd }" var="each"> <c:choose> <c:when test="${p.page==each }"> <a href="query.sxt?op=queryAll&page=${each }&size=${p.size}"><font color="red" size="5">${each }</font></a> </c:when> <c:otherwise> <a href="query.sxt?op=queryAll&page=${each }&size=${p.size}">${each }</a> </c:otherwise> </c:choose> </c:forEach> | <a href="query.sxt?op=queryAll&page=${p.nextPage }&size=${p.size}" onclick="return ${p.hasNext?true:false}">下一页</a> | <a href="query.sxt?op=queryAll&page=${p.lastPage }&size=${p.size}">尾页</a> |
4. 在删除中, 分页遇到的问题
a) 删除成功后, 如何继续停留在当前页面. 可以在删除时携带当前页数和每页显示记录数, 代码如下
<a href="query.sxt?op=deleteEmp&empno=${e.empno }&page=${p.page}&size=${p.size}" onclick="return confirm('确定删除吗?');">删除</a> |
b) 删除最后一页的最后一条数据后, 保证回到前一页. 其实就是要求当前页数不能大于总页数, 并且总页数要大于0.
// 当前页数不能大于总页数 if(this.page > this.totalPage && this.totalPage > 0) { this.page = this.totalPage; } |
5. 在修改中分页问题
a) 保证修改后留在当前页, 因为要跳转两个页面, 所以需要传值两次
<a href="query.sxt?op=queryByEmpno&empno=${e.empno }&page=${p.page}&size=${p.size}">修改</a> |
<input type="hidden" name="page" value="${param.page }" /> <input type="hidden" name="size" value="${param.size }" /> |