开始上课了,下面我宣布,寒假正式结束😭
🤗1、MVC概念
1、MVC全称: Model 模型、View视图、Controller 控制器
2、MVC最早出现在JavaEE三层中的Web层,它可以有效的指导Web层的代码如何有效分离,单独工作
3、View视图:只负责数据和界面的显示,不接受任何与显示数据无关的代码,便于程序员和美工的分工合作---------JSP/HTML
4、Controller控制器:只负责接收请求,调用业务层的代码处理请求,然后派发页面,是一个“调度者”的角色一Servlet,转到某个页面。或者是重定向到某个页面
5、Model模型 :将与业务逻辑相关的数据封装为具体的JavaBean类,其中不掺杂任何与数据处理相关的代码一JavaBean/domain/entity
6、MVC是一种思想,MVC的理念是将软件代码拆分成为组件,单独开发,组合使用(目的还是为了解耦合)
概念图例
🤗2、图书模块
2.1、编写图书模块的数据库表
我们根据下图的内容,创建数据库
不难看出,我们需要 id、用户名、价格、作者、销量、库存、还有书本图片url🤔🤔
## 创建图书表
CREATE TABLE t_book(
`id` INT PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(100),
`price` DECIMAL(11,2),
`author` VARCHAR(100),
`sales` INT,
`stock` INT,
`img_path` VARCHAR(200)
);
现在,我们引入一些测试数据看看吧😄!!
## 插入初始化测试数据
INSERT INTO t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , 'java从入门到放弃' , '国哥' , 80 , 9999 , 9 , 'static/img/default.jpg');
INSERT INTO t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , '数据结构与算法' , '严敏君' , 78.5 , 6 , 13 , 'static/img/default.jpg');
INSERT INTO t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , '怎样拐跑别人的媳妇' , '龙伍' , 68, 99999 , 52 , 'static/img/default.jpg');
INSERT INTO t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , '木虚肉盖饭' , '小胖' , 16, 1000 , 50 , 'static/img/default.jpg');
INSERT INTO t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , 'C++编程思想' , '刚哥' , 45.5 , 14 , 95 , 'static/img/default.jpg');
INSERT INTO t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , '蛋炒饭' , '周星星' , 9.9, 12 , 53 , 'static/img/default.jpg');
INSERT INTO t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , '赌神' , '龙伍' , 66.5, 125 , 535 , 'static/img/default.jpg');
INSERT INTO t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , 'Java编程思想' , '阳哥' , 99.5 , 47 , 36 , 'static/img/default.jpg');
INSERT INTO t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , 'JavaScript从入门到精通' , '婷姐' , 9.9 , 85 , 95 , 'static/img/default.jpg');
INSERT INTO t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , 'cocos2d-x游戏编程入门' , '国哥' , 49, 52 , 62 , 'static/img/default.jpg');
INSERT INTO t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , 'C语言程序设计' , '谭浩强' , 28 , 52 , 74 , 'static/img/default.jpg');
INSERT INTO t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , 'Lua语言程序设计' , '雷丰阳' , 51.5 , 48 , 82 , 'static/img/default.jpg');
INSERT INTO t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , '西游记' , '罗贯中' , 12, 19 , 9999 , 'static/img/default.jpg');
INSERT INTO t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , '水浒传' , '华仔' , 33.05 , 22 , 88 , 'static/img/default.jpg');
INSERT INTO t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , '操作系统原理' , '刘优' , 133.05 , 122 , 188 , 'static/img/default.jpg');
INSERT INTO t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , '数据结构 java版' , '封大神' , 173.15 , 21 , 81 , 'static/img/default.jpg');
INSERT INTO t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , 'UNIX高级环境编程' , '乐天' , 99.15 , 210 , 810 , 'static/img/default.jpg');
INSERT INTO t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , 'javaScript高级编程' , '国哥' , 69.15 , 210 , 810 , 'static/img/default.jpg');
INSERT INTO t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , '大话设计模式' , '国哥' , 89.15 , 20 , 10 , 'static/img/default.jpg');
INSERT INTO t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , '人月神话' , '刚哥' , 88.15 , 20 , 80 , 'static/img/default.jpg');
## 查看表内容
SELECT id,NAME,author,price,sales,stock,img_path FROM t_book;
2.2、编写图书模块的JavaBean
我们写的javaBean和刚刚创建的t_book表是对应的
我们需要 id、用户名、价格、作者、销量、库存、还有书本图片url🤔🤔
public class Book {
private Integer id;
private String name;
private String author;
private BigDecimal price;
private Integer sales;
private Integer stock;
private String imgPath = "static/img/default.jpg"; // 给个默认值O(∩_∩)O
}
// Contruct、getXXX、setXXX 和 toString 省略
// 这里多说一下,我们希望图片路径如果为空,就不给它赋值,所以我们改改构造器和Set方法
if(imgPath != null && !"".equals(imgPath)) {
this.imgPath = imgPath;
}
2.3、编写图书模块的Dao和测试Dao
现在我们编写BookDao,我们发现,它需要增删改查的功能
2.3.1、编写BookDao
根据,我们之前在书城第二阶段的学习经验,先写个BookDao接口,然后再写个BookDaoImpl去实现它🐾
ublic interface BookDao {
public int addBook(Book book);
public int deleteBookById(Integer id);
public int updateBook(Book book);
public Book queryBookById(Integer id);
public List<Book> queryBooks();
}
public class BookDaoImpl extends BaseDao implements BookDao {
@Override
public int addBook(Book book) {
String sql = "insert into t_book(`name`,`author`,`price`,`sales`,`stock`,`img_path`) values(?,?,?,?,?,?)";
return update(sql,book.getName(),book.getAuthor(),book.getPrice(),book.getSales(),book.getStock(),book.getImgPath());
}
@Override
public int deleteBookById(Integer id) {
String sql = "delete from t_book where id = ?";
return update(sql,id);
}
@Override
public int updateBook(Book book) {
String sql = "update t_book set `name`=?,`author`=?,`price`=?,`price`=?,`stock`=?,`img_path`=? where id = ?";
return update(sql,book.getName(),book.getAuthor(),book.getPrice(),book.getSales(),book.getStock(),book.getImgPath(),book.getId());
}
@Override
public Book queryBookById(Integer id) {
String sql = "select `id`,`name`,`author`,`price`,`sales`,`stock`,`img_path` imgPath from t_book where id = ?";
return queryForOne(Book.class,sql,id);
}
@Override
public List<Book> queryBooks() {
String sql = "select `id`,`name`,`author`,`price`,`sales`,`stock`,`img_path` imgPath from t_book";
return queryForList(Book.class,sql);
}
}
2.3.2、测试BookDao
在BookDao的接口下,CTRL + SHIFT + T快捷生成测试
public int addBook(Book book)
@Test
public void addBook() {
bookDao.addBook(new Book(null,"BibleBlackⅠ","dark",new BigDecimal(9999),1,1,null));
}
public int updateBook(Book book)
@Test
public void updateBook() {
bookDao.updateBook(new Book(21,"BibleBlackⅡ","darkⅡ",new BigDecimal(9999),1,1,null));
}
public Book queryBookById(Integer id)
@Test
public void queryBookById() {
System.out.println(bookDao.queryBookById(22));
}
public List<Book> queryBooks()
@Test
public void queryBooks() {
for (Book queryBook: bookDao.queryBooks()) {
System.out.println(queryBook);
}
}
public int deleteBookById(Integer id)
@Test
public void deleteBookById() {
bookDao.deleteBookById(22);
}
2.4、编写图书模块的Service和测试Service
和Dao层一样
2.4.1、编写Service
public interface BookService {
public void addBook(Book book);
public void deleteBookById(Integer id);
public void updateBook(Book book);
public Book queryBookById(Integer id);
public List<Book> queryBooks();
}
public class BookServiceImpl implements BookService {
private BookDao bookDao = new BookDaoImpl();
@Override
public void addBook(Book book) {
bookDao.addBook(book);
}
@Override
public void deleteBookById(Integer id) {
bookDao.deleteBookById(id);
}
@Override
public void updateBook(Book book) {
bookDao.updateBook(book);
}
@Override
public Book queryBookById(Integer id) {
return bookDao.queryBookById(id);
}
@Override
public List<Book> queryBooks() {
return bookDao.queryBooks();
}
}
2.4.2、测试Service
CTRL + SHIFT + T
public class BookServiceTest {
BookService bookService = new BookServiceImpl();
@Test
public void addBook() {
bookService.addBook(new Book(null,"BibleBlackⅠ","dark",new BigDecimal(9999),1,1,null));
}
@Test
public void deleteBookById() {
bookService.deleteBookById(21);
}
@Test
public void updateBook() {
bookService.updateBook(new Book(21,"BibleBlackⅡ","darkⅡ",new BigDecimal(9999),1,1,null));
}
@Test
public void queryBookById() {
System.out.println(bookService.queryBookById(21));;
}
@Test
public void queryBooks() {
for (Book queryBook : bookService.queryBooks()) {
System.out.println(queryBook);
}
}
}
2.5、 编写图书模块的Web层,和页面联调测试
我们也需要对CRUD功能做出相应的效果
2.5.1、查询图书
图解
代码实现
配置servlet,这里的url路径,前面多加了/mange/不是,在mange目录下的意思嗷,是为了方便我们后面权限管理
<servlet-mapping>
<servlet-name>BookServlet</servlet-name>
<url-pattern>/manager/bookServlet</url-pattern>
</servlet-mapping>
回到我们之前写的common\manager_menu.jsp
,改改a标签的路径,这里action表示服务器调用,哪一个方法
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<div>
<a href="mange/bookServlet?action=list">图书管理</a>
<a href="order_manager.jsp">订单管理</a>
<a href="../../index.jsp">返回商城</a>
</div>
下面是BookServlet,我们只要完成List方法就行
public class BookServlet extends BaseServlet{
private BookService bookService = new BookServiceImpl();
protected void add(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void delete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void update(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void list(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1.通过BookService查询全部图书
List<Book> books = bookService.queryBooks();
// 2.把全部图书保存到Request域中
request.setAttribute("books",books);
// 3.请求转发到/pages/manger/book_manager.jsp页面
request.getRequestDispatcher("/pages/manager/book_manager.jsp").forward(request,response);
}
}
...
<div id="main">
<table>
<tr>
<td>名称</td>
<td>价格</td>
<td>作者</td>
<td>销量</td>
<td>库存</td>
<td colspan="2">操作</td>
</tr>
<c:forEach items="${requestScope.books}" var="book">
<tr>
<td>${book.name}</td>
<td>${book.price}</td>
<td>${book.author}</td>
<td>${book.sales}</td>
<td>${book.stock}</td>
<td><a href="book_edit.jsp">修改</a></td>
<td><a href="#">删除</a></td>
</tr>
</c:forEach>
...
因为a标签,点击是get请求,所以我们要去BaseServlet对get请求做1个聪明的处理,使得它为post请求😁
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req,resp);
}
2.5.2、添加图书
图解
代码实现
添加页面这里,我们改改😤,增加隐藏域,修改标签名,和JavaBean对应
<div id="main">
<form action="manager/bookServlet" method="get">
<input type="hidden" name="action" value="add">
<table>
<tr>
<td>名称</td>
<td>价格</td>
<td>作者</td>
<td>销量</td>
<td>库存</td>
<td colspan="2">操作</td>
</tr>
<tr>
<td><input name="name" type="text" value="时间简史"/></td>
<td><input name="price" type="text" value="30.00"/></td>
<td><input name="author" type="text" value="霍金"/></td>
<td><input name="sales" type="text" value="200"/></td>
<td><input name="stock" type="text" value="300"/></td>
<td><input type="submit" value="提交"/></td>
</tr>
</table>
</form>
</div>
完成BookServlet的add方法,这里不用请求转发的原因,是因为这样写会有个bug😰
表单重复提交:
当用户提交完请求,浏览器会记录下最后一次请求的全部信息。当用户按下功能键F5,就会发起浏览器记录的最后一次请求。下面代码使用请求重定向解决该问题
protected void add(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1、获取请求的参数==封装成为Book对象
Book book = WebUtils.copyParamToBean(request.getParameterMap(), new Book());
// 2、调用BookService. addBook()保存图书
bookService.addBook(book);
// 3、跳到图书列表页面 /manager/bookServlet?action=list
response.sendRedirect(request.getContextPath()+"/manager/bookServlet?action=list");
}
2.5.3、删除图书
图解
代码实现
修改删除的标签
<c:forEach items="${requestScope.books}" var="book">
<tr>
<td>${book.name}</td>
<td>${book.price}</td>
<td>${book.author}</td>
<td>${book.sales}</td>
<td>${book.stock}</td>
<td><a href="book_edit.jsp">修改</a></td>
<td><a href="manager/bookServlet?action=delete&id=${book.id}">删除</a></td>
</tr>
</c:forEach>
完成BookServlet的delet方法
protected void delete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1、 获取请求的参数id图书编程
String id = request.getParameter("id");
// 2、调用bookService.deleteBookById();删除图书
bookService.deleteBookById(WebUtils.parseInt(id,0));
// 3、重定向回图书列表管理页面, book/manager/bookServlet?action=list
response.sendRedirect(request.getContextPath()+"/manager/bookServlet?action=list");
}
这里方便让String转int,我们特意在WebUtils下加了parseInt()方法
public static int parseInt(String strInt,int defaultValue){
try {
return Integer.parseInt(strInt);
} catch (Exception e) {
e.printStackTrace();
}
return defaultValue;
}
还有一件事,我们这样直接删除,不给用户提示,属于是有点nt设计了🤗,所以我们要给用户提示(❌)jQuery复习时间(✔),先给a标签加个class
<td><a class="deleteClass" href="manager/bookServlet?action=delete&id=${book.id}">删除</a></td>
<script>
$(function (){
// 给删除的a标签绑定单击事件,用于删除的确认提示
$("a.deleteClass").click(function (){
// 在事件的function函数中,有一个this对象。这个this对象,是当前正在响应的dom对象
/**
* confirm 是曲儿提示框函数
* 参数是它的提示内容
* 它有两个按钮,一个确认(返回true),一个取消(返回false)
*/
return confirm("你确认要删除【"+$(this).parent().parent().find("td:first").text()+"】吗?");
})
// return false 阻止元素的默认行为===不提交请求
})
</script>
2.5.4、修改图书
图解
代码实现
数据回显
修改修改的a标签(嗨呀,有一种叠词词的感觉捏🤗),使它跳转到Servlet程序,调用getBook方法,传递id
<td><a href="manager/bookServlet?action=getBook&id=${book.id}">修改</a></td>
实现getBook方法
protected void getBook(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1、获取图书编号
String id = request.getParameter("id");
// 2、调用BookService. queryBookById(id):Book;得到修改的图书信息
Book book = bookService.queryBookById(WebUtils.parseInt(id,0));
// 3、把图书保存到Request域中
request.setAttribute("book",book);
// 4、请求转发到/pages/manager/book_edit. jsp页面
request.getRequestDispatcher("/pages/manager/book_edit.jsp").forward(request,response);
}
修改book_edit页面中显示的书本信息
<tr>
<td><input name="name" type="text" value="${requestScope.book.name}"/></td>
<td><input name="price" type="text" value="${requestScope.book.price}"/></td>
<td><input name="author" type="text" value="${requestScope.book.author}"/></td>
<td><input name="sales" type="text" value="${requestScope.book.sales}"/></td>
<td><input name="stock" type="text" value="${requestScope.book.stock}"/></td>
<td><input type="submit" value="提交"/></td>
</tr>
修改
我们这里用方案二,解决动态修改book_edit.jsp的功能
<input type="hidden" name="action" value="${empty param.id ? "add" : "update"}">
<input type="hidden" name="id" value="${requestScope.book.id}">
完成update()方法
protected void update(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1、获取请求的参数==封装成为Book对象
Book book = WebUtils.copyParamToBean(request.getParameterMap(), new Book());
// 2、调用BookService.updateBook(book) ;修改图书
bookService.updateBook(book);
// 3、重定向回图书列表管理页面 地址: /工程名/manager /bookServlet?action=list
response.sendRedirect(request.getContextPath()+"/manager/bookServlet?action=list");
}
🤗 3、图书分页
图解
3.1、编写page类
/***
* 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;
//get、set 和 toString 省略
}
3.2、分页功能初步实现
老师上课时从web层一直写到dao层,其实我觉得应该从Dao层写到Web层,测试也是从最小的Dao层,测到Web层
首先修改我们之前用静态包含的manager_menu的a标签,不调用list()方法了,我们调用page()方法😁
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<div>
<a href="/book/manager/bookServlet?action=page">图书管理</a>
<a href="order_manager.jsp">订单管理</a>
<a href="../../index.jsp">返回商城</a>
</div>
还有在book_manager.jsp修改,我们现在,需要在Requset域page.items里取值,而不是books里
<c:forEach items="${requestScope.page.items}" var="book">
<tr>
<td>${book.name}</td>
<td>${book.price}</td>
<td>${book.author}</td>
<td>${book.sales}</td>
<td>${book.stock}</td>
<td><a href="manager/bookServlet?action=getBook&id=${book.id}">修改</a></td>
<td><a class="deleteClass" href="manager/bookServlet?action=delete&id=${book.id}">删除</a></td>
</tr>
</c:forEach>
同时在</table>下面,加上页码栏
<div id="page_nav">
<a href="#">首页</a>
<a href="#">上一页</a>
<a href="#">3</a>
【${requestScope.page.pageNo}】
<a href="#">5</a>
<a href="#">下一页</a>
<a href="#">末页</a>
共${requestScope.page.pageTotal}页,${requestScope.page.pageTotalCount}条记录
到第<input value="4" name="pn" id="pn_input"/>页
<input type="button" value="确定">
</div>
🐛Web层BookServlet的page()方法
protected void page(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1获取请求的参数pageNo和pageSize
int pageNo = WebUtils.parseInt(request.getParameter("pageNo"),1);
int pageSize = WebUtils.parseInt(request.getParameter("pageSize"), Page.PAGE_SIZE);
//2调用BookService.page(pageNo, pageSize): Page对象
Page<Book> page = bookService.page(pageNo,pageSize);
//3保存age对象到Request域中
request.setAttribute("page",page);
//4请求转发到/pages/manager/book_manager.jsp页面
request.getRequestDispatcher("/pages/manager/book_manager.jsp").forward(request,response);
}
🐛Service层BookServiceImpl实现page()方法
@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++;
}
// 设置总页码
page.setPageTotal(pageTotal);
// 求当前页开始索引
int begin = (page.getPageNo() - 1) * pageSize;
// 求当前页数据
List<Book> items = bookDao.queryForPageItems(begin,pageSize);
// 设置当前页数据
page.setItems(items);
return page;
}
🐛Dao层BookDaoImpl实现queryForPageTotalCount()方法和queryForPageItems()方法
@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);
}
测试
最小单元优先测试,我们从Dao层测到Web层
🐛 测试Dao层
@Test
public void queryForPageTotalCount() {
System.out.println(bookDao.queryForPageTotalCount());
}
@Test
public void queryForPageItems() {
for (Book book : bookDao.queryForPageItems(8, 4)) {
System.out.println(book);
}
}
🐛 测试Service层
@Test
public void page() {
System.out.println(bookService.page(1,Page.PAGE_SIZE));
}
🐛 测试Web层
直接运行服务器,马上到我们的图书管理页面康康
3.3、首页、上一页、下一页、末页
我们会发现一个问题,如果我们在第1页,那么我们点击首页和上一页,没什么用,如果我们在最后一页,那么我们点击下一页和末页,也没什么用,所以我们要解决这个问题。因此,我们只要在第一页和最后一页不显示出那个选项就行了👌
<div id="page_nav">
<%--大于首页页码才显示--%>
<c:if test="${requestScope.page.pageNo > 1}">
<a href="manager/bookServlet?action=page&pageNo=1">首页</a>
<a href="manager/bookServlet?action=page&pageNo=${requestScope.page.pageNo-1}">上一页</a>
</c:if>
<a href="#">3</a>
【${requestScope.page.pageNo}】
<a href="#">5</a>
<%-- 如果已经是最后一页,就不会显示下一页,末页 --%>
<c:if test="${requestScope.page.pageNo > 1}">
<a href="manager/bookServlet?action=page&pageNo=${requestScope.page.pageNo+1}">下一页</a>
<a href="manager/bookServlet?action=page&pageNo=${requestScope.page.pageTotal}">末页</a>
</c:if>
共${requestScope.page.pageTotal}页,${requestScope.page.pageTotalCount}条记录
到第<input value="4" name="pn" id="pn_input"/>页
<input type="button" value="确定">
</div>
</div>
3.4、跳转到指定页码
为了使location.href 跳转更加精确,我们可以去common/head.jsp,把basePath的路径存到pageContext域中
<%
// http://localhost:8080/工程路径/
String basePath = request.getScheme()
+ "://"
+ request.getServerName()
+ ":"
+ request.getServerPort()
+ request.getContextPath() //request.getContextPath()返回 /工程路径,所以这里不需要加/
+ "/";
pageContext.setAttribute("basePath",basePath);
%>
下面搜索到指定页面的小改
...
到第<input value="${param.pageNo}" name="pn" id="pn_input"/>页
<input id="searchPageBtn" type="button" value="确定">
<script>
$(function (){
$("#searchPageBtn").click(function (){
let pageNo = $("#pn_input").val();
// js 提供了一个location地址栏对象
// 它有一个属性href 它可以获取浏览器地址栏的地址
// href属性可读,可写
location.href = "${pageScope.basePath}manager/bookServlet?action=page&pageNo="+pageNo;
});
});
</script>
为了避免出现有人无聊查-100页或者1000页这种不存在的页数,我们可以去Set页码的方法里这里直接进行判断
public void setPageNo(Integer pageNo) {
if(pageNo < 1){
pageNo = 1;
}else if(pageNo > pageTotal){
pageNo = pageTotal;
}
this.pageNo = pageNo;
}
这里一定要小♥,pageTotal的空指针异常,所以我们在BookServiceImpl中,一定要把setPageTotal()方法,放到setPageNo()方法前面(我因为这个bug,卡了亿点点时间
3.5、分页条页码的输出
要求 : 分页模块中,页码1,2,【3】 ,4,5 的显示,要显示5个页码,并且页码可以点击跳转
分析
情况1: 如果总页码小于等于5的情况,页码的范围是:1~总页码
1 页 1
2 页 1 ,2
3 页 1,2,3
4 页 1,2,3,4
5 页 1,2,3,4,5
情况2 : 总页码大于5的情况。假设一共10页
小情况1: 当前页码为前3个:1,2,3的情况,页码的范围是:1 - 5
【1】,2,3,4,5
1,【2】,3,4,5
1,2,【3】,4,5
小情况2: 当前页码为最后3个,8,9,10的情况,页码范围是:总页码-4 ~ 总页码
6,7,【8】,9,10
6,7,8,【9】,10
6,7,8,9,【10】
小情况3: 3,4,5,6,7的情况,页码范围是:当前页码-2 ~ 当前页码+2
2,3,【4】,5,6
3,4,【5】,6,7
4,5,【6】,7,8
5,6,【7】,8,9
代码实现
<%--页码输出的开始--%>
<c:choose>
<%-- 情况1:如果总页码小于等于5的情况,页码的范围是:1~总页码 --%>
<c:when test="${requestScope.page.pageTotal <= 5}">
<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>
<%--情况2:总页码大于5的情况。假设一共10页 --%>
<c:when test="${requestScope.page.pageTotal > 5}">
<c:choose>
<%--小情况1:当前页码为前3个:1,2,3的情况,页码的范围是:1 - 5 --%>
<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>
<%--小情况2:当前页码为最后3个,8,9,10的情况,页码范围是:总页码-4 ~ 总页码--%>
<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>
<%--小情况3:3,4,5,6,7的情况,页码范围是:当前页码-2 ~ 当前页码+2--%>
<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>
<%--页码输出的结束--%>
发现代码重复,我们可以用set优化
<%--页码输出的开始--%>
<c:choose>
<%-- 情况1:如果总页码小于等于5的情况,页码的范围是:1~总页码 --%>
<c:when test="${requestScope.page.pageTotal <= 5}">
<c:set var="begin" value="1"></c:set>
<c:set var="end" value="${requestScope.page.pageTotal}"></c:set>
</c:when>
<%--情况2:总页码大于5的情况。假设一共10页 --%>
<c:when test="${requestScope.page.pageTotal > 5}">
<c:choose>
<%--小情况1:当前页码为前3个:1,2,3的情况,页码的范围是:1 - 5 --%>
<c:when test="${requestScope.page.pageNo <= 3}">
<c:set var="begin" value="1"></c:set>
<c:set var="end" value="5"></c:set>
</c:when>
<%--小情况2:当前页码为最后3个,8,9,10的情况,页码范围是:总页码-4 ~ 总页码--%>
<c:when test="${requestScope.page.pageNo > requestScope.page.pageTotal-3}">
<c:set var="begin" value="${requestScope.page.pageTotal-4}"></c:set>
<c:set var="end" value="${requestScope.page.pageTotal}"></c:set>
</c:when>
<%--小情况3:3,4,5,6,7的情况,页码范围是:当前页码-2 ~ 当前页码+2--%>
<c:otherwise>
<c:set var="begin" value="${requestScope.page.pageNo-2}"></c:set>
<c:set var="end" value="${requestScope.page.pageNo+2}"></c:set>
</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>
<%--页码输出的结束--%>
3.6、解决分页对添、删、改的影响
添加
我们需要添加到最后一页,并且添加后需要跳转到最后一页
book_manager.jsp
中的a标签链接改改,并带参数
<td><a href="pages/manager/book_edit.jsp?action=page&pageNo=${requestScope.page.pageTotal}"+>添加图书</a></td>
传给book_edit.jsp
,当写好要添加的图书信息,提交表单的时候,启动bookServlet程序,调用add()方法
...
<form action="manager/bookServlet" method="get">
<input type="hidden" name="pageNo" value="${param.pageNo}">
<input type="hidden" name="action" value="${empty param.id ? "add" : "update"}">
<input type="hidden" name="id" value="${requestScope.book.id}">
<table>
...
add()方法也需要,在重定向的地方改改
protected void add(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 解决添加导致页面加1页,但是跳转没有加1页
int pageNo = WebUtils.parseInt(request.getParameter("pageNo"),0);
pageNo ++;
// 1、获取请求的参数==封装成为Book对象
Book book = WebUtils.copyParamToBean(request.getParameterMap(), new Book());
// 2、调用BookService. addBook()保存图书
bookService.addBook(book);
// 3、跳到图书列表页面 /manager/bookServlet?action=list
response.sendRedirect(request.getContextPath()+"/manager/bookServlet?action=page&pageNo="+pageNo);
}
删除
book_manager.jsp 中的a标签链接改改,并带当前页面参数
<td><a class="deleteClass" href="manager/bookServlet?action=delete&id=${book.id}&pageNo=${requestScope.page.pageNo}">删除</a></td>
delete()方法也需小改,重定向位置
protected void delete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1、 获取请求的参数id,图书编程
String id = request.getParameter("id");
// 2、调用bookService.deleteBookById();删除图书
bookService.deleteBookById(WebUtils.parseInt(id,0));
// 3、重定向回图书列表管理页面, book/manager/bookServlet?action=list
response.sendRedirect(request.getContextPath()+"/manager/bookServlet?action=page&pageNo="+request.getParameter("pageNo"));
}
修改
<td><a href="manager/bookServlet?action=getBook&id=${book.id}&pageNo=${requestScope.page.pageNo}">修改</a></td>
update()方法也需小改,重定向位置
protected void update(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1、获取请求的参数==封装成为Book对象
Book book = WebUtils.copyParamToBean(request.getParameterMap(), new Book());
// 2、调用BookService.updateBook(book) ;修改图书
bookService.updateBook(book);
// 3、重定向回图书列表管理页面 地址: /工程名/manager /bookServlet?action=list
response.sendRedirect(request.getContextPath()+"/manager/bookServlet?action=page&pageNo="+request.getParameter("pageNo"));
}
🤗4、前台数据的展示
小小解释下:
前台页面是
后台页面是
4.1、初步实现
我们需要现在page/clinet
下创建一个和 index.jsp
一模一样的index.jsp
写一个ClientBookServlet程序
public class ClientBookServlet extends BaseServlet{
private BookService bookService = new BookServiceImpl();
protected void page(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1获取请求的参数pageNo和pageSize
int pageNo = WebUtils.parseInt(request.getParameter("pageNo"),1);
int pageSize = WebUtils.parseInt(request.getParameter("pageSize"), Page.PAGE_SIZE);
//2调用BookService.page(pageNo, pageSize): Page对象
Page<Book> page = bookService.page(pageNo,pageSize);
//3保存age对象到Request域中
request.setAttribute("page",page);
//4请求转发到/pages/manager/book_manager.jsp页面
request.getRequestDispatcher("/pages/client/book_manager.jsp").forward(request,response);
}
}
配置XML
<servlet>
<servlet-name>ClientBookServlet</servlet-name>
<servlet-class>com.flzj.web.ClientBookServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ClientBookServlet</servlet-name>
<url-pattern>/client/bookServlet</url-pattern>
</servlet-mapping>
遍历显示数据库显示,页面
<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>
前台的分页效果,和后台的一样,只是路径需要换一换
4.2、分页的抽取
前台的分页条和后台的分页条完全一致,说以我们可以抽取分页条,
可以作为page的url属性
// 分页条的请求地址
private String url;
//get、set 省略
前台页面
在index.jsp
页面(前台页面)中替换
在前台的Servlet(ClinetBookServlet)中加上
page.setUrl("client/bookServlet?action=page");
后台页面
book_manager.jsp
(后台页面)也需要替换
在后台的Servlet(BookServlet)中加上
page.setUrl("manager/bookServlet?action=page");
这时候我们会惊奇的发现,前台的分页和后台的分页一样,所以我们可以把它弄成一个静态包含,内容放到page_nav.jsp
中
<div id="page_nav">
<%--大于首页页码才显示--%>
<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:choose>
<%-- 情况1:如果总页码小于等于5的情况,页码的范围是:1~总页码 --%>
<c:when test="${requestScope.page.pageTotal <= 5}">
<c:set var="begin" value="1"></c:set>
<c:set var="end" value="${requestScope.page.pageTotal}"></c:set>
</c:when>
<%--情况2:总页码大于5的情况。假设一共10页 --%>
<c:when test="${requestScope.page.pageTotal > 5}">
<c:choose>
<%--小情况1:当前页码为前3个:1,2,3的情况,页码的范围是:1 - 5 --%>
<c:when test="${requestScope.page.pageNo <= 3}">
<c:set var="begin" value="1"></c:set>
<c:set var="end" value="5"></c:set>
</c:when>
<%--小情况2:当前页码为最后3个,8,9,10的情况,页码范围是:总页码-4 ~ 总页码--%>
<c:when test="${requestScope.page.pageNo > requestScope.page.pageTotal-3}">
<c:set var="begin" value="${requestScope.page.pageTotal-4}"></c:set>
<c:set var="end" value="${requestScope.page.pageTotal}"></c:set>
</c:when>
<%--小情况3:3,4,5,6,7的情况,页码范围是:当前页码-2 ~ 当前页码+2--%>
<c:otherwise>
<c:set var="begin" value="${requestScope.page.pageNo-2}"></c:set>
<c:set var="end" value="${requestScope.page.pageNo+2}"></c:set>
</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="${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>
$(function (){
$("#searchPageBtn").click(function (){
let pageNo = $("#pn_input").val();
// js 提供了一个location地址栏对象
// 它有一个属性href 它可以获取浏览器地址栏的地址
// href属性可读,可写
location.href = "${pageScope.basePath}${requestScope.page.url}&pageNo="+pageNo;
});
});
</script>
</div>
静态包含
<%@ include file="/pages/common/page_nav.jsp"%>
🤗5、前台价格搜索
图解
编写代码
ClientBookServlet程序
protected void pageByPrice(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
int pageNo = WebUtils.parseInt(request.getParameter("pageNo"),1);
int pageSize = WebUtils.parseInt(request.getParameter("pageSize"), Page.PAGE_SIZE);
int min = WebUtils.parseInt(request.getParameter("min"), 0);
int max = WebUtils.parseInt(request.getParameter("max"), Integer.MAX_VALUE);
//2调用BookService.pageByPrice(pageNo, pageSize,min,max): Page对象
Page<Book> page = bookService.pageByPrice(pageNo,pageSize,min,max);
page.setUrl("client/bookServlet?action=pageByPrice");
//3保存age对象到Request域中
request.setAttribute("page",page);
//4请求转发到/pages/manager/book_manager.jsp页面
request.getRequestDispatcher("/pages/client/index.jsp").forward(request,response);
}
BookServiceImpl 修改这两处
@Override
public Page 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++;
}
// 设置总页码
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;
}
BookDao程序
@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);
}
测试代码
Dao层
@Test
public void queryForPageTotalCountByPrice() {
System.out.println(bookDao.queryForPageTotalCountByPrice(10,50));
}
@Test
public void queryForPageItemsByPrice() {
for (Book book : bookDao.queryForPageItemsByPrice(1,4,1, 50)) {
System.out.println(book);
}
}
Service层
@Test
public void pageByPrice(){
System.out.println(bookService.pageByPrice(1,4,1,50));
}
Web层
效果优化
价格回显
价格:<input id="min" type="text" name="min" value="${param.min}"> 元 -
<input id="max" type="text" name="max" value="${param.max}"> 元
分页中不带价格区间的bug
StringBuilder sb = new StringBuilder("client/bookServlet?action=pageByPrice");
// 如果有最小价格的参数,追加到分页条的地址栏参数中
if(request.getParameter("min") != null){
sb.append("&min=").append(request.getParameter("min"));
}
// 如果有最小价格的参数,追加到分页的地址栏参数中
if(request.getParameter("max") != null){
sb.append("&max=").append(request.getParameter("max"));
}
page.setUrl(sb.toString());
我的寒假结束了,奠😭