WEB 小案例 -- 网上书城(二)
寒假结束了,自己的颓废时间同样结束了,早该继续写博客了,尽管我的格式以及内容由于各种原因老被卡,但必须坚持写下去!!!
上次我们对于本案例的数据库部分进行了阐述,这次主要接着上次的内容分享本案例的翻页操作,其演示如下:
GIF 中主要演示了翻页操作,首先进入该页面前先查询数据库中所有书籍的数量,根据每页显示的数量生成首页部分数据。
1. 点击 “下一页”,当前第几页数字改变,页面显示下一页书籍信息;
2. 点击 “上一页”,当前第几页数字改变,页面显示上一页书籍信息;
3. 点击 “末页”,根据数据库信息显示最后一页的信息,此时页面上没有 “下一页” 超链接;
4. 点击 “首页”,页面信息跳转到首页信息,此时页面上没有 “上一页” 超链接;
5. 可以在输入框中输入数字直接跳转到你输入的页面,倘若输入错误的数字以及非数字将会跳转到首页
一、 操作准备条件
1. 我们将显示页面封装为一个类(Page<T>,页面所要显示的不仅仅是 Book,也可能是其他类型所以为其加上泛型),其包含的成员变量有当前页面所要显示的书籍的列表(List<T> pageList),当前页页码(int pageNo),每页可显示的书籍数量(int pageSize)以及对于该数据库共多少页(long totalItemNum),在本类中我们还利用 totalItemNum 和 pageSize 获得对于当前数据库中的数据共有多少页(int totalPageNum),我们还根据当前页码进行判断是否存在下一页或者上一页并获取下一页和上一页的页码。
1 package com.book.store.web; 2 3 import java.util.List; 4 5 /** 6 * 封装首页显示的页面 7 */ 8 public class Page<T> { 9 private int pageNo; 10 private List<T> pageList; 11 private int pageSize; 12 private long totalItemNum; 13 14 /* 15 * 在这里我们将每页可显示的书籍数量设为 5 16 * */ 17 public Page(int pageNo) { 18 pageSize = 5; 19 this.pageNo = pageNo; 20 } 21 22 /* 23 * 对输入的页码进行纠正判断,若小于 0 则使其等于 1,若大于最大的页码则使其等于页码的最大值并返回 24 * */ 25 public int getPageNo() { 26 if (pageNo < 0) { 27 pageNo = 1; 28 } 29 30 if (pageNo > getTotalPageNum()) { 31 pageNo = getTotalPageNum(); 32 } 33 return pageNo; 34 } 35 36 public int getPageSize() { 37 return pageSize; 38 } 39 40 public List<T> getPageList() { 41 return pageList; 42 } 43 44 public void setPageList(List<T> pageList) { 45 this.pageList = pageList; 46 } 47 48 public long getTotalItemNum() { 49 return totalItemNum; 50 } 51 52 public void setTotalItemNum(long totalItemNum) { 53 this.totalItemNum = totalItemNum; 54 } 55 /* 56 * 根据 totalItemNum 和 pageSize 获取总页数 57 * */ 58 public int getTotalPageNum() { 59 int flag = (int) (getTotalItemNum() % getPageSize()); 60 int totalPageNum = (int) (getTotalItemNum() / getPageSize()); 61 if (flag > 0) { 62 totalPageNum++; 63 } 64 return totalPageNum; 65 } 66 /* 67 * 判断是否有下一页 68 * */ 69 public boolean isHasNext() { 70 if (getPageNo() == getTotalPageNum()) { 71 return false; 72 } 73 return true; 74 } 75 /* 76 * 判断是否有上一页 77 * */ 78 public boolean isHasPrev() { 79 if (getPageNo() == 1) { 80 return false; 81 } 82 return true; 83 } 84 /* 85 * 获取下一个的 页码 86 * */ 87 public int getNextPage() { 88 if (isHasNext()) { 89 return pageNo + 1; 90 } 91 return pageNo; 92 } 93 /* 94 * 获取上一页的页码 95 * */ 96 public int getPrevPage() { 97 if (isHasPrev()) { 98 return pageNo - 1; 99 } 100 return pageNo; 101 } 102 103 @Override 104 public String toString() { 105 return "Page{" + 106 "pageNo=" + pageNo + 107 ", pageList=" + pageList + 108 ", pageSize=" + pageSize + 109 ", totalItemNum=" + totalItemNum + 110 '}'; 111 } 112 }
2. 封装查询条件为一个单独的类( CriteriaBook),其包括页面上部的查询条件最低价(int minPrice)和最高价(int maxPrice)区间和页面底部转到多少页的条件(int pageNo),并为其赋初值,极端值。
package com.book.store.web; /** * 封装查询条件的类 */ public class CriteriaBook { private int minPrice = 0; private int maxPrice = Integer.MAX_VALUE; private int pageNo; public CriteriaBook() {} @Override public String toString() { return "CriteriaBook{" + "minPrice=" + minPrice + ", maxPrice=" + maxPrice + ", pageNo=" + pageNo + '}'; } public CriteriaBook(int minPrice, int maxPrice, int pageNo) { this.minPrice = minPrice; this.maxPrice = maxPrice; this.pageNo = pageNo; } public int getMinPrice() { return minPrice; } public void setMinPrice(int minPrice) { this.minPrice = minPrice; } public int getMaxPrice() { return maxPrice; } public void setMaxPrice(int maxPrice) { this.maxPrice = maxPrice; } public int getPageNo() { return pageNo; } public void setPageNo(int pageNo) { this.pageNo = pageNo; } }
二、 编写思路以及步骤
1. 在到达显示页面之前我们首先需要其显示首页信息,因为我们通过一个页面重定向到 Servlet 中,在 Servlet 中获取首页信息后将其包装在 request 中转发到显示页面!
1 <%@ page contentType="text/html;charset=UTF-8" language="java" %> 2 <html> 3 <head> 4 <title>BookStore</title> 5 </head> 6 <body> 7 <h3><% 8 /* 9 * /days_1212_JavaWebBookStore,query 方法中负责封装数据并返回显示页面 10 * */ 11 response.sendRedirect(request.getContextPath() + "/query.do"); 12 System.out.println(request.getContextPath()); 13 %></h3> 14 </body> 15 </html>
2. query 方法要封装信息就需要查询数据库,所以为了 query 方法的简洁明了我们将逻辑方法置于一个单独的类。在 query 方法中我们将查询条件都赋初值,以免在没有查询条件的情况下不会出错,方法中我们给三个查询条件分别加以异常处理为了使假若其中一个出错不会影响其他的查询条件(以下为Servlet 的 query 方法)。
1 protected void query(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 2 // 获取查询条件之最低价 3 String minPriceStr = request.getParameter("minPrice"); 4 // 获取查询条件之最高价 5 String maxPriceStr = request.getParameter("maxPrice"); 6 // 获取查询条件之页码 7 String pageNoStr = request.getParameter("pageNo"); 8 // 初始化查询条件,即无查询条件的默认值 9 int minPrice = 0; 10 int maxPrice = Integer.MAX_VALUE; 11 int pageNo = 1; 12 13 try { 14 minPrice = Integer.parseInt(minPriceStr); 15 } catch (NumberFormatException e) {} 16 try { 17 maxPrice = Integer.parseInt(maxPriceStr); 18 } catch (NumberFormatException e) {} 19 try { 20 pageNo = Integer.parseInt(pageNoStr); 21 } catch (NumberFormatException e) {} 22 // 构建查询条件所对应的类 23 CriteriaBook criteriaBook = new CriteriaBook(minPrice, maxPrice, pageNo); 24 // 根据查询条件获得首页显示信息所构建的类 25 Page<Books> page = bookService.getPage(criteriaBook); 26 // 将首页需要显示的信息存入 request 中,用于显示 27 request.setAttribute("page", page); 28 // 转发回首页 29 request.getRequestDispatcher("/showView/bookList.jsp").forward(request, response); 30 }
3. 上述方法中从页面获取查询条件将其封装为对应的类(CriteriaBook),再根据查询条件所封装的类构建 Page 类(BookDao 接口所要实现的功能之一,方法 getPage(CriteriaBook criteriaBook) )!Page 类包括了所要显示的书籍,根据书籍数量获得共多少页,共多少书籍等信息。我们在 getPage 方法中首先新建 Page 对象,然后利用 setXxx 方法为 page 对象赋值。
1 /* 2 * 初始化首页显示页 3 * */ 4 @Override 5 public Page<Books> getPage(CriteriaBook criteriaBook) { 6 // 在新建 page 对象的时候就初始化页码(pageNo) 7 Page<Books> page = new Page<Books>(criteriaBook.getPageNo()); 8 // 设置现显示页的总数量(需要根据总数量和 pageSize 计算共多少页) 9 page.setTotalItemNum(getTotalNum(criteriaBook)); 10 // 设置查询条件的页码(利用 Page 类中的 getPageNo 使页码合法) 11 criteriaBook.setPageNo(page.getPageNo()); 12 // 初始化显示页的 list 集合 13 page.setPageList(getBookList(criteriaBook, 5)); 14 return page; 15 }
4. 上述 getPage(CriteriaBook criteriaBook) 方法中所调用的 getTotalNum(criteriaBook) 方法以及 getBookList(criteriaBook,pageSize) 均需要结合数据库数据方可完成赋值。
1 /* 2 * 根据查询条件获取该条件下商品的总数量 3 * */ 4 @Override 5 public long getTotalNum(CriteriaBook criteriaBook) { 6 String sql = "SELECT COUNT(id) FROM books WHERE price>=? AND price<=?"; 7 long totalNum = (Long) getCount(sql, criteriaBook.getMinPrice(), criteriaBook.getMaxPrice()); 8 return totalNum; 9 } 10 11 /* 12 * 根据查询条件(最高价、最低价、)和每页显示的数量(pageSize)获取对应商品的 list 集合 13 * */ 14 @Override 15 public List<Books> getBookList(CriteriaBook criteriaBook, int pageSize) { 16 String sql = "SELECT id, author, title, price, publish_date publishDate, sales_count salesCount, store_number storeNumber, remark " + 17 "FROM books WHERE price >= ? AND price <= ? LIMIT ?, ?"; 18 List<Books> booksList = getList(sql,criteriaBook.getMinPrice(), criteriaBook.getMaxPrice(), (criteriaBook.getPageNo() - 1) * pageSize, pageSize); 19 return booksList; 20 }
5. 到这里我们已经成功将首页信息获取到,接下来需要将获取到的信息显示到页面,首先我们将 page 对象添加到 request 域中通过请求转发至页面并在页面利用 JSTL 将其进行显示。接着为 “上一页”、“下一页”、“首页”、“末页” 等超链接添加响应事件。为上述超链接添加超链接的时候其 href 属性是重点,我们利用 href 属性将其链接到 servlet 中的 query 方法中,在 servlet 方法中进行处理对应的操作!
1 <%@ page contentType="text/html;charset=UTF-8" language="java" %> 2 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 3 <html> 4 <head> 5 <title>BookList</title> 6 <%@ include file="/commons/queryCondition.jsp" %> 7 </head> 8 <body> 9 <div> 10 <form method="post" action="${pageContext.request.contextPath}/query.do"> 11 Price: <input type="text" size="1" name="minPrice"> - 12 <input type="text" size="1" name="maxPrice"> 13 <button type="submit">Submit</button> 14 </form> 15 <table cellpadding="15"> 16 <c:forEach items="${requestScope.page.pageList}" var="book"> 17 <tr> 18 <td> 19 <a href="${pageContext.request.contextPath}/getBookInfo.do?id=${book.id}&pageNo=${requestScope.page.pageNo}">${book.title}</a><br>${book.author} 20 </td> 21 <td>${book.price}</td> 22 <td><a href="#">加入购物车</a></td> 23 </tr> 24 </c:forEach> 25 </table> 26 <br><br> 27 <span>共${requestScope.page.totalPageNum}页 当前第${requestScope.page.pageNo}页</span> 28 <c:if test="${requestScope.page.hasPrev}"> 29 <span><a href="${pageContext.request.contextPath}/query.do?pageNo=1">首页</a> 30 <a href="${pageContext.request.contextPath}/query.do?pageNo=${requestScope.page.prevPage}">上一页</a></span> 31 </c:if> 32 <c:if test="${requestScope.page.hasNext}"> 33 <span><a href="${pageContext.request.contextPath}/query.do?pageNo=${requestScope.page.nextPage}">下一页</a> 34 <a href="${pageContext.request.contextPath}/query.do?pageNo=${requestScope.page.totalPageNum}">尾页</a></span> 35 </c:if> 36 转到 <input type="text" size="1" name="toPage" id="pageNo"> 页 37 </div> 38 </body> 39 </html>
6. 上述代码中翻页的超链接点击之后伴随 nextPageNo 或 prePageNo 参数传递到 Servlet 中的 query 方法中进行处理,此时的 query 方法中除了 pageNo(利用 request 参数以及 JSTL 调用 Page 类中的 getXxx 方法结合构造器中的 pageNo 参数得到 nextPageNo 和 prePageNo) 参数其余的均为初始值,所以其处理过程如上显示首页一般,至于转到多少多少页我们使用 Ajax 及时响应请求并处理!
1 <script type="text/javascript" src="${pageContext.request.contextPath}/jquery-1.7.2.js"></script> 2 <script type="text/javascript"> 3 $(function () { 4 $("#pageNo").change(function () { 5 var $pageVal = $("#pageNo").val() 6 window.location.href = "${pageContext.request.contextPath}/query.do?pageNo=" + $pageVal; 7 }) 8 }); 9 </script>
7. 上述代码中利用 JS 对 id 为 pageNo 的输入框进行监测,若其值变化则执行 Ajax 函数,新建变量($pageNo)为其赋值为所输入的值,然后将请求的发送到 window.location.href ,即 query 方法执行查询!
至此我们就将本案例中的翻页操作讲述完毕,如果阅读过程过有更好的方法或者发现什么问题还望大家可以提出,我将及时更正,谢谢!!!