分页
- 分页的优点
- 只查询一页,不用查询所有页!有利于提高效率.
2. 分页数据
- 页面的数据都是由 Servlet 传递过来的. Servlet 需要给页面提供
- 当前页: pageCode, 简写 pc
- 如果页面没有传递当前页码, 那么 Servlet 默认是第一页, 否则,以页面传递的为准!
- 总页数: totalPages, 简写 tp
- tp = 总记录数/每页记录数;
- 总记录数: totalRecord, tr. 由 DAO 来获取,
SELECT COUNT(*) FROM t_customer
; - 每页记录数: 也称为业务数据或系统数据, 由我们自己来定义, 此处为 10 行;
- 当前页数据: beanList
- url : 用于保存查询条件
3. 数据传递
- 我们需要把这些分页数据封装到一个 javaBean 中, 它就叫 PageBean.
public class PageBean<T>{
private int pc; // 当前页码,page Code
//private int tp; 总页数, total page, 通过计算得到
private int tr; // 总记录数, total record
private int ps; // 每页记录数, page size
private List<T> beanList; //当前页的记录, T 是为了方便将来复用
private String url; // 表示 url 后面的条件!
public int getPc(){
return pc;
}
public void setPc(int pc){
this.pc = pc;
}
// tp 是计算出来的
public void getTp(){
int tp = tr/ps;
// 如果有余数, 总页数+1
return tr/ps==0 ? tp : tp+1;
}
public int getTr(){
return tr;
}
public void setTr(int tr){
this.tr = tr;
}
public int getPs(){
return ps;
}
public void setPs(int ps){
this.ps = ps;
}
public List<T> getBeanList(){
return beanList;
}
public void setBeanList(List<T> beanList){
this.beanList = beanList;
}
public String getUrl(){
return url;
}
public void setTr(String url){
this.url = url;
}
}
4. 分页数据在各层的处理
- 页面: 显示分页相关的链接们!
- 页面需要给 Servlet 传递当前页码
- Servlet: 创建 PageBean 对象, 给 PageBean 所有的属性赋值, 然后传递给页面.
- Servlet 需要给 DAO 传递当前页码,以及每页记录数.
- Service: 中转站
- DAO
- 获取总记录数 tr:
SELECT COUNT(*) t_customer
; - 获取当前页数据 BeanList:
SELECT * FROM t_customer LIMIT x,y
; limit 方言
其中, x 表示从第几行开始查询, y 表示查询多少行.
- 获取总记录数 tr:
// CustomerServlet
// 查询所有
public String findAll(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException{
// 使用 getPc 方法, 得到 pc
int pc = getPc(request);
// 给定 ps 的值, 每页10行
int ps = 10;
// 传递 pc, ps 给 service, 得到 PageBean
PageBean<Customer> pb = customerService.findAll(pc,ps);
// 保存到 request 域
request.setAttribute("pb",pb);
// 转发到 list.jsp
return "f:/list.jsp";
}
// 从 request 域得到的 pc 为字符串, 需要转换成 int 类型
private int getPc(HttpServletRequest request){
String value = request.getParameter("pc");
// 如果 pc 参数不存在, 说明 pc=1;
// 如果 pc 参数存在, 转换成 int 类型
if(value == null || value.trim().isEmpty()){
return 1;
}
return Interger.parseInt(value);
}
// CustomerService
public PageBean<Customer> findAll(int pc, int ps){
return customerDao.findAll(pc,ps);
}
// CustomerDao
public PageBean<Customer> findAll(int pc, int ps){
try{
PageBean<Customer> pb = new PageBean<Customer>();
// 设置 pb 的 pc 和 ps的值
pb.setPc(pc);
pb.setPs(ps);
// 得到 tr
String sql = "SELECT COUNT(*) FROM t_customer";
Number num = (Number)qr.query(sql, new ScalarHandler());
int tr = num.intValue();
pb.setTr(tr);
// 得到 BeanList
sql = "SELECT * FROM t_customer order by cname LIMIT ?,?";
List<Customer> beanList = qr.query(sql,
new BeanListHandler<Customer>(Customer.class),
(pc-1)*ps,ps);
pb.setBeanList(beanList);
return pb;
}catch(SQLException e){
throw new RuntimeException(e);
}
}
// list.jsp
<body>
<h3 align="center">客户列表</h3>
<table border="1" width="70" align="center">
<tr>
<th>姓名</th>
<th>性别</th>
<th>生日</th>
<th>手机</th>
<th>邮箱</th>
<th>邮箱</th>
<th>操作</th>
</tr>
<c:forEach items="${pb.beanList }" var = "cstm">
<tr>
<td>${cstm.cname }</td>
<td>${cstm.gender }</td>
<td>${cstm.birthday }</td>
<td>${cstm.cellphone }</td>
<td>${cstm.email }</td>
<td>${cstm.description }</td>
<td>
<a href="<c:url value='/CustomerServlet?method=preEdit&cid=${cstm.cid }'/>">编辑</a>
<a href="<c:url value='/CustomerServlet?method=delete&cid=${cstm.cid }'/>">删除</a>
</td>
</tr>
</c:froEach>
</table>
<br/>
<%--给出分页相关的链接 --%>
<center>
第${pb.pc}页/共${pb.tp}页
<a href="<c:url value='/CustomerSevlet?method=findAll&pc=1'/>">首页</a>
<c:if test="${pb.pc > 1}">
<a href="<c:url value='/CustomerServlet?method=findAll?pc=${pb.pc-1}'/>">上一页</a>
</c:if>
<c:if test="${pb.pc < pb.tp}">
<a href="<c:url value='/CustomerServlet?method=findAll?pc=${pb.pc+1}'/>">下一页</a>
</c:if>
<a href="<c:url value='/CustomerServlet?method=findAll?pc=${pb.tp}'/>">尾页</a>
</center>
</body>
5. 分页的页码列表
5.1 需要关注的参数
- 最多显示多少个页码, 定为 10 页;
- 当前页, 在页码列表中的位置, 即居中显示, 定位 6;
5.2 如何来确定页码列表
- 需要当前页码, begin, end 三样数据.
5.3 计算公式
- 如果总页数 <= 10(列表长度), 那么 begin = 1, end = 总页数;
- 使用公式计算: begin = pc -5, end = pc + 4;
- 头溢出: 当 begin < 1 时, 让 begin = 1;
- 尾溢出: 当 end > ${tp} 时, 让 end = ${tp};
// list.jsp
<body>
<br/>
<center>
第${pb.pc}页/共${pb.tp}页
<a href="<c:url value='/CustomerSevlet?method=findAll&pc=1'/>">首页</a>
<c:if test="${pb.pc > 1}">
<a href="<c:url value='/CustomerServlet?method=findAll?pc=${pb.pc-1}'/>">上一页</a>
</c:if>
<%-- 计算 begin 和 end --%>
<c:choose>
<%-- 如果总页数不足 10 页, 那么把所有的页数都显示出来! --%>
<c:when test="${pb.tp <= 10}">
<c:set var="begin" value="1"/>
<c:set var="end" value="${pb.tp}"/>
</c:when>
<c:otherwise>
<%-- 当总页数 > 10 时, 通过公式计算出 begin 和 end --%>
<c:set var = "begin" value="${pb.pc - 5}"/>
<c:set var = "end" value="{pb.pc + 4}"/>
<%-- 头溢出 --%>
<c:if test="${begin < 1}">
<c:set var = "begin" value = "1"/>
<c:set var = "end" value="10"/>
</c:if>
<%-- 尾溢出 --%>
<c:if test="${end > pb.tp}">
<c:set var="begin" value="${pb.tp-9}"/>
<c:set var="end" value="${pb.tp }"/>
</c:if>
</c:otherwise>
</c:choose>
<%-- 循环遍历页码列表 --%>
<c:forEach var="i" begin="${begin}" end="${end}">
<c:choose>
<c:when test="${i eq pb.pc}">
[${i }]
</c:when>
<c:otherwise>
<a href="<c:url value='/CustomerServlet?method=findAll&pc=${i}'/>">[${i}]</a>
</c:otherwise>
</c:choose>
</c:forEach>
<c:if test="${pb.pc < pb.tp}">
<a href="<c:url value='/CustomerServlet?method=findAll?pc=${pb.pc+1}'/>">下一页</a>
</c:if>
<a href="<c:url value='/CustomerServlet?method=findAll?pc=${pb.tp}'/>">尾页</a>
</center>
</body>
6. 在超链接中保留参数
- 当使用多条件查询时, 在点击第 2 页时, 这个第 2 页超链接没有条件了, 所以会丢失条件,
我们需要在页码上的所有链接都要保留条件!! - 要保留的条件以一个字符串的形式保存到 PageBean 的 url 中, 这个操作交给 Servlet 处理.
// CutomerServlet
public String query(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException{
// 把条件封装到 Customer 对象中
Customer criteria = CommonUtils.toBean(request.getParameterMap(),Customer.class);
// 得到 pc
int pc = getPc(request);
int ps = 10;
// 使用 pc, ps 以及条件对象, 调用 service 方法得到 PageBean
PageBean<Customer> pb = customerService.query(criteria, pc, ps);
// 保存 url 到 pb
pb.setUrl(gerUrl(request));
// 保存到 request 域
request.setAttribute("pb",pb);
// 转发到 list.jsp
return "f:/list.jsp";
}
// 截取 url
// request 域中的 url : /项目名/Servlet 路径?参数字符串
// 请求方式更改为 get 请求
private String getUrl(HttpServletRequest request){
String contextPath = request.getContextPath();
String servletPath = request.getServletPath();
String queryString = request.getQueryString();
// 判断是否存在 pc, 存在的话, 需要去掉
if(queryString.contains("&pc=")){
int index = queryString.lastIndexOf("&pc=");
queryString = queryString.substring(0,index);
}
return contextPath + servletPath +"?"+ queryString;
}
// CustomerService
public PageBean<Customer> query(Customer criteria, int pc, int ps){
return customerDao.query(criteria, pc, ps);
}
// customerDao
public PageBean<Customer> query(Customer criteria, int pc, int ps){
// 创建 pageBean 对象
PageBean<Customer> pb = new PageBean<Customer>();
// 设置 pc 和 ps
pb.setPc(pc);
pb.setPs(ps);
// 得到 tr
// 查询 tr 的 sql 语句前缀
StringBuilder cntSql = new StringBuilder("SELECT COUNT(*) FROM t_customer");
StringBuilder whereSql = new StringBuilder(" WHERE 1=1");
// 创建一个 ArrayList, 用来装载参数
List<Object> params = new ArrayList<Object>();
String cname = criteria.getCanem();
if(cname != null && !cname.trim().isEmpty()){
whereSql.append(" AND cname like ?");
params.add("%" + cname + "%");
}
String gender = criteria.getGender();
if(gender != null && !gender.trim().isEmpty()){
whereSql.append(" AND gender=?");
params.add(gender);
}
String cellphone = criteria.getCellphone();
if(cellphone != null && !cellphone.trim().isEmpty()){
whereSql.append(" AND cellphone like ?");
params.add("%" + cellphone + "%");
}
String email = criteria.getEmail();
if(email != null && !email.trim().isEmpty()){
whereSql.append(" AND email like ?");
params.add("%"+ email + "%");
}
Number num = (Number)qr.query(cntSql.append(whereSql).toString(),
new ScalarHandler(),params.toArray());
int tr = num.intValue();
pb.setTr(tr);
// 得到 beanList
StringBuilder sql = new StringBuilder("SELECT * FROM t_customer");
StringBuilder limitSql = new StringBuilder(" LIMIT ?,?");
params.add((pc-1)*ps);
params.add(ps);
List<Customer> beanList =
qr.query(sql.append(whereSql).append(limitSql).toString(),
new BeanListHandler<Customer>(Customer.class),
params.toArray());
pb.setBeanList(beanList);
return pb;
}
// list.jsp
<body>
<center>
第${pb.pc}页/共${pb.tp}页
<a href="${pb.url }&pc=1">首页</a>
<c:if test="${pb.pc > 1}">
<a href="${pb.url }&pc=${pb.pc-1}">上一页</a>
</c:if>
<c:if test="${pb.pc < pb.tp}">
<a href="${pb.url}&pc=${pb.pc+1}">下一页</a>
</c:if>
<a href="${pb.url }&pc=${pb.tp}">尾页</a>
</center>
</body>
参考资料: