图书分页的分析

16-书城项目(第五阶段)_数据

分页模型Page对象的创建

package com.atguigu.pojo;

import java.util.List;

/**
* Page是分页的模型对象
* @param <T> 具体模块的JavaBean类,图书模块、商品模块。。
*/
public class Page<T> {

public static final Integer PAGE_SIZE = 4;
private Integer pageNo;
private Integer pageTotal;
private Integer pageSize = PAGE_SIZE;
private Integer pageTotalCount;
private List<T> items;

public Integer getPageNo() {
return pageNo;
}

public void setPageNo(Integer pageNo) {
this.pageNo = pageNo;
}

public Integer getPageTotal() {
return pageTotal;
}

public void setPageTotal(Integer pageTotal) {
this.pageTotal = pageTotal;
}

public Integer getPageSize() {
return pageSize;
}

public void setPageSize(Integer pageSize) {
this.pageSize = pageSize;
}

public Integer getPageTotalCount() {
return pageTotalCount;
}

public void setPageTotalCount(Integer pageTotalCount) {
this.pageTotalCount = pageTotalCount;
}

public List<T> getItems() {
return items;
}

public void setItems(List<T> items) {
this.items = items;
}

@Override
public String toString() {
return "Page{" +
"pageNo=" + pageNo +
", pageTotal=" + pageTotal +
", pageSize=" + pageSize +
", pageTotalCount=" + pageTotalCount +
", items=" + items +
'}';
}
}

分页初步实现

web层

manager_menu.jsp,当点击图书管理时不再是显示全部图书信息,而是调用page()显示一页的图书信息

16-书城项目(第五阶段)_分页_02

BookServlet.java

16-书城项目(第五阶段)_java_03

在BookService.java接口中添加page()定义

16-书城项目(第五阶段)_分页_04

service层

BookServiceImpl.java实现接口中的page()

16-书城项目(第五阶段)_分页_05

bookDao.java中添加两个方法的定义

16-书城项目(第五阶段)_当前页_06

Dao层

BookDaoImpl.java中实现接口中的两个方法

16-书城项目(第五阶段)_java_07

测试

BookDaoTest.java

16-书城项目(第五阶段)_数据_08

BookServiceTest.java

16-书城项目(第五阶段)_分页_09

book_manager.jsp修改显示在页面的信息

16-书城项目(第五阶段)_java_10

将index.jsp中分页的代码复制过来,修改分页信息的代码

16-书城项目(第五阶段)_分页_11

16-书城项目(第五阶段)_html_12

首页、上一页、下一页、末页的实现

book_manager.jsp

16-书城项目(第五阶段)_数据_13

跳到指定页面功能的实现

16-书城项目(第五阶段)_html_14

head.jsp在pageContext域中保存basePath的值

16-书城项目(第五阶段)_分页_15

book_manager.jsp,修改跳转的路径,应该是服务器端的地址而非本机localhost

16-书城项目(第五阶段)_分页_16

数据有效边境检查

Page.java

16-书城项目(第五阶段)_html_17

BookServiceImpl.java

16-书城项目(第五阶段)_java_18

分页条页码的输出

显示5个连续的页码,当前页面在中间,除当前页码外,其余每个页码都可以跳转到指定页

当总页码数小于等于5时,将页码从1到最大页码都显示出来

当总页码数大于5,假设有10页(将Page.java中PAGE_SIZE的值改为2)

当前页是前三个的情况下,页码范围都是1-5,如1 【2】 3 4 5

当前页是最后3个的情况下,页码范围是总页码数减4-总页码数,如 6 7 【8】 9 10

当前页是其他页的情况下,页码范围是当前页码减2-当前页码加2,如3 4 【5】 6 7

book_manager.jsp

<%-- 页码输出的开始 --%>
<c:choose>
<%-- 情况一:总页码数小于等于5 --%>
<c:when test="${ requestScope.page.pageTotal <= 5 }">
<%-- 循环输出从1开始到最大页码数,如果不是当前页,就使用a标签进行跳转 --%>
<c:forEach begin="1" end="${ requestScope.page.pageTotal }" var="i">
<c:if test="${ i == requestScope.page.pageNo }">
【${i}】
</c:if>
<c:if test="${ i != requestScope.page.pageNo }">
<a href="manager/bookServlet?action=page&pageNo=${i}">${i}</a>
</c:if>
</c:forEach>
</c:when>

<%-- 情况二:总页码数大于5 --%>
<c:when test="${ requestScope.page.pageTotal > 5 }">
<c:choose>
<%-- 当前页是前三页的情况 --%>
<c:when test="${ requestScope.page.pageNo <=3 }">
<c:forEach begin="1" end="5" var="i">
<c:if test="${ i == requestScope.page.pageNo }">
【${i}】
</c:if>
<c:if test="${ i != requestScope.page.pageNo }">
<a href="manager/bookServlet?action=page&pageNo=${i}">${i}</a>
</c:if>
</c:forEach>
</c:when>

<%-- 当前页是后三页的情况 --%>
<c:when test="${ requestScope.page.pageNo > requestScope.page.pageTotal-3 }">
<c:forEach begin="${ requestScope.page.pageTotal-4 }" end="${ requestScope.page.pageTotal }" var="i">
<c:if test="${ i == requestScope.page.pageNo }">
【${i}】
</c:if>
<c:if test="${ i != requestScope.page.pageNo }">
<a href="manager/bookServlet?action=page&pageNo=${i}">${i}</a>
</c:if>
</c:forEach>
</c:when>

<%-- 当前页是其他页的情况 --%>
<c:otherwise>
<c:forEach begin="${ requestScope.page.pageNo-2 }" end="${ requestScope.page.pageNo+2 }" var="i">
<c:if test="${ i == requestScope.page.pageNo }">
【${i}】
</c:if>
<c:if test="${ i != requestScope.page.pageNo }">
<a href="manager/bookServlet?action=page&pageNo=${i}">${i}</a>
</c:if>
</c:forEach>
</c:otherwise>
</c:choose>
</c:when>
</c:choose>
<%-- 页码输出的结束 --%>

优化

<%-- 页码输出的开始 --%>
<c:choose>
<%-- 情况一:总页码数小于等于5 --%>
<c:when test="${ requestScope.page.pageTotal <= 5 }">
<%-- 循环输出从1开始到最大页码数,如果不是当前页,就使用a标签进行跳转 --%>
<c:set var="begin" value="1"/>
<c:set var="end" value="${ requestScope.page.pageTotal }"/>
</c:when>

<%-- 情况二:总页码数大于5 --%>
<c:when test="${ requestScope.page.pageTotal > 5 }">
<c:choose>
<%-- 当前页是前三页的情况 --%>
<c:when test="${ requestScope.page.pageNo <=3 }">
<c:set var="begin" value="1"/>
<c:set var="end" value="5"/>
</c:when>

<%-- 当前页是后三页的情况 --%>
<c:when test="${ requestScope.page.pageNo > requestScope.page.pageTotal-3 }">
<c:set var="begin" value="${ requestScope.page.pageTotal-4 }"/>
<c:set var="end" value="${ requestScope.page.pageTotal }"/>
</c:when>

<%-- 当前页是其他页的情况 --%>
<c:otherwise>
<c:set var="begin" value="${ requestScope.page.pageNo-2 }"/>
<c:set var="end" value="${ requestScope.page.pageNo+2 }"/>
</c:otherwise>
</c:choose>
</c:when>
</c:choose>
<c:forEach begin="${ begin }" end="${ end }" var="i">
<c:if test="${ i == requestScope.page.pageNo }">
【${i}】
</c:if>
<c:if test="${ i != requestScope.page.pageNo }">
<a href="manager/bookServlet?action=page&pageNo=${i}">${i}</a>
</c:if>
</c:forEach>
<%-- 页码输出的结束 --%>

修改分页对原来添加、删除、修改的影响

将BookServlet.java中add()、delete()、update()里重定向的地址由list改为page

16-书城项目(第五阶段)_分页_19

添加图书

book_manager.jsp,传递参数当前最大页码数

16-书城项目(第五阶段)_java_20

book_edit.jsp,继续将该参数传给add()

16-书城项目(第五阶段)_当前页_21

BookServlet.java,将最大页码数+1然后重定向到page(),用户最终看到的是添加图书后就显示最后一页新添加图书的内容(当添加图书时并没有增加页数,则还是跳转到最大页码数+1的页数,此时会经过数据有效边境检查,显示的还是最后一页而非最后一页的下一页)

16-书城项目(第五阶段)_html_22

删除

book_manager.jsp,传递参数当前页码数

16-书城项目(第五阶段)_java_23

BookServlet.java,用户看到的是在某一页删除图书就在这一页更新删除后的图书列表信息

16-书城项目(第五阶段)_html_24

修改

book_manager.jsp,传递参数当前页码数

16-书城项目(第五阶段)_分页_25

BookServlet.java,用户看到的是在某一页修改图书就在这一页更新修改后的图书列表信息

16-书城项目(第五阶段)_html_26

前台分页的初步实现

用户访问网站首先都是请求web目录下的index.jsp页面,而jsp页面不能直接获取用户需要的数据,需要经过Servlet程序,取到数据后再通过jsp页面展示出来

16-书城项目(第五阶段)_html_27

在web/pages下创建目录client,将index.jsp复制过来

web/index.jsp

16-书城项目(第五阶段)_当前页_28

ClientBookServlet.java

package com.atguigu.web;

import com.atguigu.pojo.Book;
import com.atguigu.pojo.Page;
import com.atguigu.service.BookService;
import com.atguigu.service.impl.BookServiceImpl;
import com.atguigu.utils.WebUtils;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class ClientBookServlet extends BaseServlet {

private BookService bookService = new BookServiceImpl();

protected void page(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1,获取请求参数,如果没有传参数,就使用默认值
int pageNo = WebUtils.parseInt(req.getParameter("pageNo"), 1);
int pageSize = WebUtils.parseInt(req.getParameter("pageSize"), Page.PAGE_SIZE);
// 2,获取Page对象
Page<Book> page = bookService.page(pageNo, pageSize);
// 3,将Page对象保存到Request域中
req.setAttribute("page", page);
// 4,请求转发,显示分页的图书信息
req.getRequestDispatcher("/pages/client/index.jsp").forward(req, resp);
}
}

web.xml

16-书城项目(第五阶段)_html_29

client/index.jsp

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>书城首页</title>
<%-- 静态包含,base标签、css样式、jQuery文件 --%>
<%@ include file="/pages/common/head.jsp"%>
</head>
<body>

<div id="header">
<img class="logo_img" alt="" src="static/img/logo.gif" >
<span class="wel_word">网上书城</span>
<div>
<a href="pages/user/login.jsp">登录</a> |
<a href="pages/user/regist.jsp">注册</a> &nbsp;&nbsp;
<a href="pages/cart/cart.jsp">购物车</a>
<a href="pages/manager/manager.jsp">后台管理</a>
</div>
</div>
<div id="main">
<div id="book">
<div class="book_cond">
<form action="" method="get">
价格:<input id="min" type="text" name="min" value=""> 元 -
<input id="max" type="text" name="max" value="">
<input type="submit" value="查询" />
</form>
</div>
<div style="text-align: center">
<span>您的购物车中有3件商品</span>
<div>
您刚刚将<span style="color: red">时间简史</span>加入到了购物车中
</div>
</div>

<c:forEach items="${ requestScope.page.items }" var="book">
<div class="b_list">
<div class="img_div">
<img class="book_img" alt="" src="${ book.imgPath }" />
</div>
<div class="book_info">
<div class="book_name">
<span class="sp1">书名:</span>
<span class="sp2">${ book.name }</span>
</div>
<div class="book_author">
<span class="sp1">作者:</span>
<span class="sp2">${ book.author }</span>
</div>
<div class="book_price">
<span class="sp1">价格:</span>
<span class="sp2">${ book.price }</span>
</div>
<div class="book_sales">
<span class="sp1">销量:</span>
<span class="sp2">${ book.sales }</span>
</div>
<div class="book_amount">
<span class="sp1">库存:</span>
<span class="sp2">${ book.stock }</span>
</div>
<div class="book_add">
<button>加入购物车</button>
</div>
</div>
</div>
</c:forEach>

</div>

<div id="page_nav">
<%-- 如果当前是第一页,就不用显示首页和上一页 --%>
<c:if test="${ requestScope.page.pageNo > 1}">
<%-- 当点击图书管理进入到这个页面,意味着page()中已经在Request域中保存了Page对象,
对于上一页,只需要将当前页-1然后传给page(),再经请请转发到这个页面显示出来 --%>
<a href="client/bookServlet?action=page&pageNo=1">首页</a>
<a href="client/bookServlet?action=page&pageNo=${ requestScope.page.pageNo-1 }">上一页</a>
</c:if>

<%-- 页码输出的开始 --%>
<c:choose>
<%-- 情况一:总页码数小于等于5 --%>
<c:when test="${ requestScope.page.pageTotal <= 5 }">
<%-- 循环输出从1开始到最大页码数,如果不是当前页,就使用a标签进行跳转 --%>
<c:set var="begin" value="1"/>
<c:set var="end" value="${ requestScope.page.pageTotal }"/>
</c:when>

<%-- 情况二:总页码数大于5 --%>
<c:when test="${ requestScope.page.pageTotal > 5 }">
<c:choose>
<%-- 当前页是前三页的情况 --%>
<c:when test="${ requestScope.page.pageNo <=3 }">
<c:set var="begin" value="1"/>
<c:set var="end" value="5"/>
</c:when>

<%-- 当前页是后三页的情况 --%>
<c:when test="${ requestScope.page.pageNo > requestScope.page.pageTotal-3 }">
<c:set var="begin" value="${ requestScope.page.pageTotal-4 }"/>
<c:set var="end" value="${ requestScope.page.pageTotal }"/>
</c:when>

<%-- 当前页是其他页的情况 --%>
<c:otherwise>
<c:set var="begin" value="${ requestScope.page.pageNo-2 }"/>
<c:set var="end" value="${ requestScope.page.pageNo+2 }"/>
</c:otherwise>
</c:choose>
</c:when>
</c:choose>
<c:forEach begin="${ begin }" end="${ end }" var="i">
<c:if test="${ i == requestScope.page.pageNo }">
【${i}】
</c:if>
<c:if test="${ i != requestScope.page.pageNo }">
<a href="client/bookServlet?action=page&pageNo=${i}">${i}</a>
</c:if>
</c:forEach>
<%-- 页码输出的结束 --%>

<c:if test="${ requestScope.page.pageNo < requestScope.page.pageTotal }">
<a href="client/bookServlet?action=page&pageNo=${ requestScope.page.pageNo+1 }">下一页</a>
<a href="client/bookServlet?action=page&pageNo=${ requestScope.page.pageTotal }">末页</a>
</c:if>

共${ requestScope.page.pageTotal }页,${ requestScope.page.pageTotalCount }条记录
到第<input value="${ param.pageNo }" name="pn" id="pn_input"/>
<input id="searchPageBtn" type="button" value="确定">
<script type="text/javascript">
$(function () {
$("#searchPageBtn").click(function () {
var pageNo = $("#pn_input").val();
// location是js提供的一个地址栏对象,它的href属性可以获取浏览器地址栏中的地址,该属性可读可写
// 给该属性赋值实际就是跳转页面
location.href = "${ pageScope.basePath }client/bookServlet?action=page&pageNo=" + pageNo;
});
});
</script>
</div>

</div>

<%-- 静态包含页脚内容 --%>
<%@include file="/pages/common/footer.jsp"%>
</body>
</html>

在上面jsp中,将book_manager.jsp中的分页代码复制过来,Ctrl+r全部替换。将page.jsp的PAGE_SIZE改为4

16-书城项目(第五阶段)_java_30

分页条的抽取

book_manager.jsp和client/index.jsp中分页的代码有大量重复,唯一不同就是点击页码时的请求地址,所以将重复代码抽取出来

在Page.java中新加属性url,表示分页条的请求地址,生成相应的get()、set()、toString()。替换掉两个页面中用到请求地址的地方,这样两个页面中分页的代码就完全一样

16-书城项目(第五阶段)_分页_31

16-书城项目(第五阶段)_数据_32

在BookServlet.java和ClientBookServlet.java中设置url。以访问首页为例,用户访问web/index.jsp,跳转到ClientBookServlet程序,设置相应的url后跳转到client/index.jsp中,页码就能获取到url值

16-书城项目(第五阶段)_当前页_33

16-书城项目(第五阶段)_java_34

将两个jsp页面中完全相同的分页代码抽取成page_nav.jsp放在common目录下

16-书城项目(第五阶段)_html_35

将book_manager.jsp和client/index.jsp中的重复代码用静态包含代替即可

16-书城项目(第五阶段)_数据_36

16-书城项目(第五阶段)_html_37

价格区间搜索并分页展示

16-书城项目(第五阶段)_当前页_38

client/index.jsp,修改查询提交的服务器地址,添加隐藏域

16-书城项目(第五阶段)_html_39

ClientBookServlet.java,添加pageByPrice()

16-书城项目(第五阶段)_当前页_40

BookService.java接口中添加pageByPrice()声明

16-书城项目(第五阶段)_分页_41

BookServiceImpl.java中实现接口中声明的方法

16-书城项目(第五阶段)_html_42

BookDao.java中添加上面两个方法的声明

16-书城项目(第五阶段)_数据_43

BookDaoImpl.java中实现接口中的两个方法

16-书城项目(第五阶段)_html_44

查询过程:用户输入价格区间点击查询,首先client/index.jsp将请求发送给ClientBookServlet程序,执行其中的pageByPrice(),获取四个请求参数,其中pageNo没有值就取默认值1,接着将参数传给BookServiceImpl.java的pageByPrice()用于返回page对象,该方法先执行BookDaoImpl.java中查找在价格区间内的数据的条数的方法,根据这个条数和pageSize计算应该用多少页来展示,即pageTotal的值,然后设置pageNo的值,结果边境检查后设置为默认值1,然后执行BookDaoImpl.java中返回指定价格区间、按价格排序后指定条数的数据的方法,即第一页的数据,BookServiceImpl.java的pageByPrice()执行完

接着拼接url值并设置到Page对象中,再转发到client/index.jsp,显示查询到的第一页数据,如果pageTotal大于1,则还有第二页及以后的数据页,用户若点击第二页,此时会带上参数pageNo=2,并重复上述过程,最终显示第二页的数据


搜索价格区间的回显

输入价格区间点击查询后保留用户输入的区间值

client/index.jsp

16-书城项目(第五阶段)_分页_45