0%

JavaWeb书城项目(六)———图书分页

整个项目源代码可以到我的github上下载。前五个部分我们已经完成了用户的注册与登录模块,以及后台的图书管理,第六部分我们完成图书的分页部分,分页的原因就是一页显示全部信息太繁杂了,所以需要需要分页来解决这个问题。

分页模块的分析

分页模块的分析


分页模型Page的抽取

由分页的视图分析出分页的对象模型Page类有如下属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 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<Book> items;

分页的初步实现

BookDao代码

1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
public Integer queryForPageTotalCount() {
String sql = "select count(*) from t_book";
Number count = (Number) queryForSingleValue(sql);
return count.intValue();
}

@Override
public List<Book> queryForPageItems(int begin, int pageSize) {
String sql = "select `id`,`name`,`author`,`price`,`sales`,`stock`,`img_path` imgPath" +
" from t_book limit ?,?";
return queryForList(Book.class, sql, begin, pageSize);
}

BookService代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Override
public Page<Book> page(int pageNo, int pageSize) {
Page<Book> page = new Page<Book>();
// 设置当前页码
page.setPageNo(pageNo);
// 设置每页显示的数量
page.setPageSize(pageSize);
// 求总记录数
Integer pageTotalCount = bookDao.queryForPageTotalCount();
// 设置总记录数
page.setPageTotalCount(pageTotalCount);
// 求总页码
Integer pageTotal = pageTotalCount / pageSize;
if (pageTotalCount % pageSize > 0){
pageTotal+=1;
}
// 设置总页码
page.setPageTotal(pageTotal);
// 求当前页数据的开始索引
int begin = (page.getPageNo() - 1) * pageSize;
// 求当前页数据
List<Book> items = bookDao.queryForPageItems(begin, pageSize);
// 设置当前页数据
page.setItems(items);

return page;
}

BookServlet程序的代码

1
2
3
4
5
6
7
8
9
10
11
protected void page(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1 获取请求的参数 pageNo 和 pageSize
int pageNo = WebUtils.parseInt(req.getParameter("pageNo"), 1);
int pageSize = WebUtils.parseInt(req.getParameter("pageSize"), Page.PAGE_SIZE);
//2 调用BookService.page(pageNo, pageSize): Page对象
Page<Book> page = bookService.page(pageNo, pageSize);
//3 保存 Page 对象到 Request 域中
req.setAttribute("page", page);
//4 请求转发到pages/manager/book_manager.jsp页面
req.getRequestDispatcher("/pages/manager/book_manager.jsp").forward(req,resp);
}

manager_menu.jsp中图书管理请求地址的修改
修改图书管理请求地址
book_manager.jsp修改
book_manager.jsp修改(一)
book_manager.jsp修改(二)


首页,上一页,下一页,末页实现

修改book_manager.jsp
修改book_manager.jsp


实现跳到指定页数

修改book_manager.jsp,通过绑定单击事件实现跳到指定页数
修改book_manager.jsp
Page对象的修改,完成数据边界的有效检查,使其不会跳到没有的页数

1
2
3
4
5
6
7
8
9
10
public void setPageNo(Integer pageNo) {
/* 数据边界的有效检查 */
if (pageNo < 1) {
pageNo = 1;
}
if (pageNo > pageTotal) {
pageNo = pageTotal;
}
this.pageNo = pageNo;
}

与之对应,要修改 BookServicepage方法,因为设置当前页码时,需要 pageTotal 来进行数据边界的有效检查,所以设置当前页码要放在设置总页码之后。
修改BookService中page方法


页码的显示

一般来说,显示页码的时候,不仅会显示当前页的页码,还会显示前几页的页码,以及后几页的页码,然后点击这些页码就可以跳转到指定页。
这里实现一次显示5个页码,下面分情况讨论:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
情况1:
如果总页码小于等于5,页码的范围是:1~总页码
11
212
3123
41234
512345
情况2
总页码大于5的情况。假设一共10
小情况1:当前页码为前面2个,页码的范围是:1~5
[1],2,3,4,5
1,[2],3,4,5
小情况2:当前页码为最后2个,页码的范围是:总页码-4~总页码
6,7,8,[9],10
6,7,8,9,[10]
小情况3:其它情况,页码的范围是:当前页码-2~当前页码+2
2,3,[4],5,6
3,4,[5],6,7

按照上面的情况修改 book_manager.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<c:choose>
<%--情况1:如果总页码小于等于5--%>
<c:when test="${ requestScope.page.pageTotal <=5 }">
<c:set var="begin" value="1"/>
<c:set var="end" value="${requestScope.page.pageTotal}"/>
</c:when>
<%--情况2:总页码大于5的情况--%>
<c:when test="${requestScope.page.pageTotal > 5}">
<c:choose>
<%--小情况1:当前页码为前面2个--%>
<c:when test="${requestScope.page.pageNo < 3}">
<c:set var="begin" value="1"/>
<c:set var="end" value="5"/>
</c:when>
<%--小情况2:当前页码为最后2个--%>
<c:when test="${requestScope.page.pageNo > requestScope.page.pageTotal-2}">
<c:set var="begin" value="${requestScope.page.pageTotal-4}"/>
<c:set var="end" value="${requestScope.page.pageTotal}"/>
</c:when>
<%--小情况3:其他情况--%>
<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>
1
2
3
4
5
6
7
8
<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>

修改分页后,增加,删除,修改图书信息的回显页面

以修改图书为例
1.在修改的请求地址上追加当前页码参数
修改book_manager,jsp
2. 在book_edit.jsp页面中使用隐藏域记录下pageNo参数
使用隐藏域记录下pageNo参数

  1. 在服务器重定向时,获取当前页码追加上进行跳转
    追加当前页码进行跳转

首页index.jsp的跳转

因为首页也需要分页,所以我们访问首页的时候需要让其通过 ClientBookServlet程序让其跳转到 web目录/pages/client目录的/index.jsp
首页index.jsp的跳转
创建ClientBookServlet程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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 {
//System.out.println("经过了前台程序");
//1 获取请求的参数 pageNo 和 pageSize
int pageNo = WebUtils.parseInt(req.getParameter("pageNo"), 1);
int pageSize = WebUtils.parseInt(req.getParameter("pageSize"), Page.PAGE_SIZE);
//2 调用BookService.page(pageNo, pageSize): Page对象
Page<Book> page = bookService.page(pageNo, pageSize);
page.setUrl("client/bookServlet?action=page");
//3 保存 Page 对象到 Request 域中
req.setAttribute("page", page);
//4 请求转发到pages/client/index.jsp页面
req.getRequestDispatcher("/pages/client/index.jsp").forward(req,resp);
}

}

配置web.xml,增加ClientBookServlet的映射

1
2
3
4
5
6
7
8
<servlet>
<servlet-name>ClientBookServlet</servlet-name>
<servlet-class>com.atguigu.web.ClientBookServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ClientBookServlet</servlet-name>
<url-pattern>/client/bookServlet</url-pattern>
</servlet-mapping>

创建client/index.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
<%@ 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>
<link type="text/css" rel="stylesheet" href="static/css/style.css" >
</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: #ff0000">时间简史</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">
<a href="#">首页</a>
<a href="#">上一页</a>
<a href="#">3</a>
【4】
<a href="#">5</a>
<a href="#">下一页</a>
<a href="#">末页</a>
共10页,30条记录 到第<input value="4" name="pn" id="pn_input"/>页
<input type="button" value="确定">
</div>

</div>

<div id="bottom">
<span>
尚硅谷书城.Copyright &copy;2015
</span>
</div>
</body>
</html>

修改web/index.jsp为请求转发到Servlet

1
2
3
4
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--只负责请求转发--%>
<jsp:forward page="/client/bookServlet?action=page"></jsp:forward>


分页条的抽取

前台页面也需要分页条,因为对于分页条而言,只是请求的 url 不同,我们可以给 page 添加 url 属性,再把分页条抽取出来,就可以简单的调用分页条。

  1. page 对象中添加 url 属性
    page对象中添加url属性

  2. Servlet 程序中的 page 分页方法中设置 url 的分页请求地址
    设置url的分页请求地址

  3. 修改分页条中请求地址为url变量输出,并抽取一个单独的page_nav.jsp页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<div id="page_nav">
<c:choose>
<%--情况1:如果总页码小于等于5--%>
<c:when test="${ requestScope.page.pageTotal <=5 }">
<c:set var="begin" value="1"/>
<c:set var="end" value="${requestScope.page.pageTotal}"/>
</c:when>
<%--情况2:总页码大于5的情况--%>
<c:when test="${requestScope.page.pageTotal > 5}">
<c:choose>
<%--小情况1:当前页码为前面2个--%>
<c:when test="${requestScope.page.pageNo < 3}">
<c:set var="begin" value="1"/>
<c:set var="end" value="5"/>
</c:when>
<%--小情况2:当前页码为最后2个--%>
<c:when test="${requestScope.page.pageNo > requestScope.page.pageTotal-2}">
<c:set var="begin" value="${requestScope.page.pageTotal-4}"/>
<c:set var="end" value="${requestScope.page.pageTotal}"/>
</c:when>
<%--小情况3:其他情况--%>
<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:if test="${requestScope.page.pageNo > 1}">
<a href="${requestScope.page.url}&pageNo=1">首页</a>
<a href="${requestScope.page.url}&pageNo=${requestScope.page.pageNo-1}">上一页</a>
</c:if>

<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="${requestScope.page.url}&pageNo=${i}">${i}</a>
</c:if>
</c:forEach>
<%--如果已经是最后一页,则不显示下一页,末页--%>
<c:if test="${requestScope.page.pageNo < requestScope.page.pageTotal}">
<a href="${requestScope.page.url}&pageNo=${requestScope.page.pageNo+1}">下一页</a>
<a href="${requestScope.page.url}&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();
<%--var pageTotal = ${requestScope.page.pageTotal};--%>
// alert(pageTotal)
// js语言中提供了一个 location 地址栏对象
// 它有一个属性叫 href. 它可以获取浏览器中地址栏中的地址
// href 属性可读可写
location.href = "${pageScope.basePath}${requestScope.page.url}&pageNo="
+ pageNo;
});
});
</script>
</div>
  1. 静态包含page_nav.jsp
    静态包含page_nav.jsp

首页价格搜索

首页价格搜索
BookDao程序添加如下方法

1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
public Integer queryForPageTotalCountByPrice(int min, int max) {
String sql = "select count(*) from t_book where price between ? and ?";
Number count = (Number) queryForSingleValue(sql, min, max);
return count.intValue();
}

@Override
public List<Book> queryForPageItemsByPrice(int begin, int pageSize, int min, int max) {
String sql = "select `id`,`name`,`author`,`price`,`sales`,`stock`,`img_path` imgPath" +
" from t_book where price between ? and ? limit ?,?";
return queryForList(Book.class, sql, min, max, begin, pageSize);
}

BookService程序添加如下方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Override
public Page<Book> pageByPrice(int pageNo, int pageSize, int min, int max) {
Page<Book> page = new Page<Book>();
// 设置每页显示的数量
page.setPageSize(pageSize);
// 求总记录数
Integer pageTotalCount = bookDao.queryForPageTotalCountByPrice(min,max);
// 设置总记录数
page.setPageTotalCount(pageTotalCount);
// 求总页码
Integer pageTotal = pageTotalCount / pageSize;
if (pageTotalCount % pageSize > 0){
pageTotal+=1;
}
// 设置总页码
page.setPageTotal(pageTotal);
// 设置当前页码
page.setPageNo(pageNo);
// 求当前页数据的开始索引
int begin = (page.getPageNo() - 1) * pageSize;
// 求当前页数据
List<Book> items = bookDao.queryForPageItemsByPrice(begin, pageSize, min, max);
// 设置当前页数据
page.setItems(items);

return page;
}

ClientBookServlet程序添加如下方法,需要注意的是设置 url 需要加上 minmax参数,这样之后点击下一页之类的,才是按价格查询的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
protected void pageByPrice(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//System.out.println("经过了前台程序");
//1 获取请求的参数 pageNo 和 pageSize, min 和 max
int pageNo = WebUtils.parseInt(req.getParameter("pageNo"), 1);
int pageSize = WebUtils.parseInt(req.getParameter("pageSize"), Page.PAGE_SIZE);
int min = WebUtils.parseInt(req.getParameter("min"), 0);
int max = WebUtils.parseInt(req.getParameter("max"), Integer.MAX_VALUE);
//2 调用BookService.pageByPrice(pageNo, pageSize, min, max): Page对象
Page<Book> page = bookService.pageByPrice(pageNo, pageSize, min, max);
StringBuilder sb = new StringBuilder("client/bookServlet?action=pageByPrice");
// 如果有最小价格的参数,追加到请求参数中
if (req.getParameter("min") != null) {
sb.append("&min=").append(req.getParameter("min"));
}
// 如果有最大价格的参数,追加到请求参数中
if (req.getParameter("max") != null) {
sb.append("&max=").append(req.getParameter("max"));
}
page.setUrl(sb.toString());
//3 保存 Page 对象到 Request 域中
req.setAttribute("page", page);
//4 请求转发到pages/client/index.jsp页面
req.getRequestDispatcher("/pages/client/index.jsp").forward(req,resp);
}
-------------本文结束感谢您的阅读-------------