在《MySQL(SQL入门)》一文中介绍过数据库的基本查询语句(单表查询),在《MySQL(JDBC)》一文中介绍过多表查询技术,本文是DQL的补充。当查询的结果集过多时,不应一次请求查询所有结果,而是采用数据库分页技术,每次查询部分结果集并显示。
1、分页查询语句
不同数据库的分页查询语句是不同的,MySQL中的分页查询语句为:
select * from 表名 limit M,N;
M:开始记录的索引(第一条记录的索引为0)N:一次查询几条记录。
所以从Customers表中分页查询的语句为:
第一页:select * from customers limit 0,10;
第二页:select * from customers limit 10,10;
……
第n页:select * from customers limit (n-1)*10,10;
本文以Customers数据库为例,主要实现下图所示的两种分页效果。Customers表下载
2、第一种分页的实现
1)设计一个Page类(核心),封装所有与分页有关的数据。
分析第一节的分页效果图片,可知展示一页需要的数据有:
- 每页展示记录条数(pageSize):预先设定
- 一页记录本身(records):从数据库中查找时需要向Dao层提供开始记录的索引(startIndex)以及查询条数(pageSize)
- 当前页码(currentPageNum)、总页数(totalPageNum)、上页页码(prePageNum)、下页页码(nextPageNum):总页数要需根据数据库中查询总结果条数(totalRecordsNum)计算。
这些数据的关系如下图所示,箭头表示依赖关系,如查找records时,需要startIndex。(没有列出pageSize,因为pageSize需要自己预先指定)
由上图可以看出,实例化一个Page类时,需要传入currentPageNum和totalRecordsNum,其他数据都可以计算出来。
package org.flyne.common; public class Page { //每页显示条数(预先定义) private int pageSize = 10; //页面上显示的记录 private List records; //与页面有关 private int currentPageNum; private int totalPageNum; private int prePageNum; private int nextPageNum; //与数据库有关 private int totalRecordsNum; private int startIndex; //实例一个页面时,需要拿到当前页码和总记录条数 public Page(int currentPageNum,int totalRecordsNum){ this.currentPageNum = currentPageNum; this.totalRecordsNum = totalRecordsNum; //计算totalPageNum totalPageNum = totalRecordsNum%pageSize==0?totalRecordsNum/pageSize:(totalRecordsNum/pageSize+1); //计算startIndex startIndex = (currentPageNum - 1)*pageSize; } …………………………各种setter、getter方法………………………… ……………getPrePageNum、getNextPageNum返回前需要计算………… public int getPrePageNum() { prePageNum = currentPageNum - 1; if(prePageNum < 1){ prePageNum = 1; } return prePageNum; } public int getNextPageNum() { nextPageNum = currentPageNum + 1; if(nextPageNum > totalPageNum){ nextPageNum = totalPageNum; } return nextPageNum; } }
2)在service层加入findOneCustomerPage方法
public class CustomerServiceImpl implements CustomerService { private CustomerDao dao = new CustomerDaoImpl(); public Page findOneCustomerPage(String currentPageNum) { int num = 1;//没有指定页码,默认查询第一页 if(currentPageNum!=null && "".equals(currentPageNum.trim())){ num = Integer.parseInt(currentPageNum); } int totalRecordsNums = dao.getTotalRecordsNum(); //创建Page对象 Page page = new Page(num,totalRecordsNums); page.setRecords(dao.findByPage(page.getStartIndex(),page.getPageSize())); return page; } }
3)在DAO层int getTotalRecordsNum、List findByPage方法,略。
4)Servlet中的相关代码:
private void showCustomersList(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String pageNum = request.getParameter("pageNum"); Page page = service.findOneCustomerPage(pageNum); request.setAttribute("page", page); request.getRequestDispatcher("/customerlist.jsp").forward(request, response); }
5)customerlist.jsp分页相关代码:
<!-- 分页开始 --> 第${page.currentPageNum }页/共${page.totalPageNum }页 <a href="${pageContext.request.contextPath }/servlet/CustomerController?op=showCustomersList&pageNum=1">首页</a> <c:if test="${page.currentPageNum>1 }"> <a href="${pageContext.request.contextPath }/servlet/CustomerController?op=showCustomersList&pageNum=${page.prePageNum}">上一页</a> </c:if> <c:if test="${page.currentPageNum<page.totalPageNum }"> <a href="${pageContext.request.contextPath }/servlet/CustomerController?op=showCustomersList&pageNum=${page.nextPageNum}">下一页</a> </c:if> <a href="${pageContext.request.contextPath }/servlet/CustomerController?op=showCustomersList&pageNum=${page.totalPageNum}">尾页</a> <input type='text' id='pageNum' size=1 /> <input type='button' value="跳转" onclick="jumpTo()"/> <!-- 分页结束 --> <script type="text/javascript"> function jumpTo(){ var num = document.getElementById("pageNum").value; if(/^[1-9][0-9]*$/.test(num)){ if(num>=1 && num<${page.totalPageNum}){ window.location.href = "${pageContext.request.contextPath }/servlet/CustomerController?op=showCustomersList&pageNum="+num; }else{ alert("页码超过范围了"); } } } </script>
3、第二种分页的实现
1)改造Page类
在Page类中加入两个字段,表示起始页码和结束页码:
//类似1 2 3 4 5 6 7 8 9的页码 private int startPageNum; private int endPageNum;
在Page类的构造方法中计算startPageNum、endPageNum,如下:
//计算startPageNum、endPageNum if(totalPageNum > 9){ startPageNum = currentPageNum - 4; endPageNum = currentPageNum + 4; if(startPageNum<1){ startPageNum = 1; endPageNum = 9; } if(endPageNum > totalPageNum ){ endPageNum = totalPageNum; startPageNum = endPageNum - 8; } }else{ //没有9页 startPageNum =1; endPageNum = totalPageNum; }
2)Service、Dao、Servlet中代码不变
3)在customerlist.jsp上一页、下一页之间加入如下代码即可:
<c:forEach begin="${page.startPageNum}" end="${page.endPageNum}" var="pageNum"> <c:if test="${pageNum==page.currentPageNum}"> ${pageNum} </c:if> <c:if test="${pageNum!=page.currentPageNum}"> <a href="${pageContext.request.contextPath }/servlet/CustomerController?op=showCustomersList&pageNum=${pageNum}">${pageNum}</a> </c:if> </c:forEach>
4、抽取customerlist.jsp中的分页代码
如果很多网页都需要用到分页,就可以考虑将customerlist.jsp中的分页代码抽取出来放入common/page.jsp,在需要分页的地方静态引入即可。
1) 改造Page类
增加一个url字段,用于存放分页需要请求的Servlet,并生成其getter和setter方法即可。
// 抽取分页 private String url;
2) Service、Dao层代码不变
3) Servlet中代码微调如下(同第2节中的Servlet):
Page page = service.findPageRecords(num); // page.url有具体Servlet赋值 page.setUrl("/servlet/CustomerController?op=showCustomersList"); request.setAttribute("page", page);
4)抽取/common/page.jsp页面
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <!-- 分页开始 --> 第${page.currentPageNum }页/共${page.totalPageNum }页 <a href="${pageContext.request.contextPath }${page.url}&pageNum=1">首页</a> <c:if test="${page.currentPageNum>1 }"> <a href="${pageContext.request.contextPath }${page.url}&pageNum=${page.prePageNum}">上一页</a> </c:if> <c:forEach begin="${page.startPageNum}" end="${page.endPageNum}" var="pageNum"> <c:if test="${pageNum==page.currentPageNum}"> ${pageNum} </c:if> <c:if test="${pageNum!=page.currentPageNum}"> <a href="${pageContext.request.contextPath }${page.url}&pageNum=${pageNum}">${pageNum}</a> </c:if> </c:forEach> <c:if test="${page.currentPageNum<page.totalPageNum }"> <a href="${pageContext.request.contextPath }${page.url}&pageNum=${page.nextPageNum}">下一页</a> </c:if> <a href="${pageContext.request.contextPath }${page.url}&pageNum=${page.totalPageNum}">尾页</a> <input type='text' id='pageNum' size=1 /> <input type='button' value="跳转" onclick="jumpTo()" /> <!-- 分页结束 -->
5)在customerlist.jsp中静态引入/common/page.jsp
<%@ include file="/common/page.jsp" %>
- 本文固定链接: http://www.flyne.org/article/617
- 转载请注明: 东风化宇 2014年08月27日 于 Flyne 发表