为了巩固MVC的开发模式,下面就写一个购物车的小案例.javascript
public class Book { private String id; private String name; private String author; private String description; private double price; public Book() { } public Book(String id, String name, String author, String description, double price) { this.id = id; this.name = name; this.author = author; this.description = description; this.price = price; } //...各类setter和getter }
可能咱们会这样设计购物车html
/*该类表明的是购物车*/ public class Cart { //关键字是书籍的id,值是书 private Map<String, Book> bookMap = new LinkedHashMap<>(); }
上面的作法是不合适的,试想一下:若是我要购买两本相同的书,购物车的页面上就出现了两本书,而不是书*2。买三本相同的书就在购物页面上出现三本书,而不是书*3.java
所以,Map集合的值不能是Book对象,那咱们怎么才能解决上面所说的问题呢?咱们最经常使用的就是,再写一个实体CartItem(表明购物项)web
/*购物项表明的是当前书,并表示该书出现了几回*/ public class CartItem { private Book book; private int quantity; //该购物项(书--不必定只有一本)的价钱应该等于书的数量*价格 private double price; //书的价钱*数量 public double getPrice() { return book.getPrice() * this.quantity; } public Book getBook() { return book; } public void setBook(Book book) { this.book = book; } public int getQuantity() { return quantity; } public void setQuantity(int quantity) { this.quantity = quantity; } public void setPrice(double price) { this.price = price; } }
/*该类表明的是购物车*/ public class Cart { //关键字是书籍的id,值是书 private Map<String, CartItem> bookMap = new LinkedHashMap<>(); //表明着购物车的总价 private double price; //把购物项(用户传递进来的书籍)加入到购物车里边去,也应该是购物车的功能 public void addBook(Book book) { //获取获得购物项 CartItem cartItem = bookMap.get(book.getId()); //判断购物车是否存在该购物项,若是不存在 if (cartItem == null) { //建立这个购物项对象 cartItem = new CartItem(); //将用户传递过来的书籍做为购物项 cartItem.setBook(book); //把该购物项的数量设置为1 cartItem.setQuantity(1); //把购物项加入到购物车去 bookMap.put(book.getId(), cartItem); } else { //若是存在该购物项,将购物项的数量+1 cartItem.setQuantity(cartItem.getQuantity() + 1); } } //购物车的总价就是全部购物项的价格加起来 public double getPrice() { double totalPrice = 0; for (Map.Entry<String, CartItem> me : bookMap.entrySet()) { //获得每一个购物项 CartItem cartItem = me.getValue(); //将每一个购物项的钱加起来,就是购物车的总价了! totalPrice += cartItem.getPrice(); } return totalPrice; } public Map<String, CartItem> getBookMap() { return bookMap; } public void setBookMap(Map<String, CartItem> bookMap) { this.bookMap = bookMap; } public void setPrice(double price) { this.price = price; } }
这里就直接用集合模拟数据库了,简单的domo而已。ajax
//既然是购物车案例,应该会有增删的操做,经过关键字查询书籍,因此使用LinkedHashMap集合 private static Map<String, Book> map = new LinkedHashMap<>(); static { map.put("1",new Book("1", "java", "zhongfucheng", "好书", 99)); map.put("2",new Book("2", "javaweb", "ouzicheng", "很差的书", 44)); map.put("3",new Book("3", "ajax", "xiaoming", "通常般", 66)); map.put("4",new Book("4", "spring", "xiaohong", "还行", 77)); } public static Map<String, Book> getAll() { return map; }
dao层应该至少提供获取全部的书籍和根据关键字获取获得书籍spring
public class BookDao { //获取存放着书籍的Map集合 public Map getAll() { return BookDB.getAll(); } //根据关键字获取某本书籍 public Book find(String id) { return BookDB.getAll().get(id); } }
service层就是对DAO层的一个封装数据库
public class BusinessService { BookDao bookDao = new BookDao(); /*列出全部的书*/ public Map getAll() { return bookDao.getAll(); } /*根据书的id获取书*/ public Book findBook(String id) { return bookDao.find(id); } //...待会还有其余的功能再从这里补充! }
//调用service层的方法,获取获得存放书籍的Map集合 BusinessService businessService = new BusinessService(); Map books = businessService.getAll(); //存放在request域对象中,交给jsp页面显示 request.setAttribute("books", books); //跳转到jsp页面中 request.getRequestDispatcher("/WEB-INF/listBook.jsp").forward(request, response);
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html> <head> <title>显示全部的书籍</title> </head> <body> <%--Servlet传递过来的是一个Map对象,要显示全部的书籍,就须要遍历Map集合(EL表达式和JSTL标签合用)--%> <table border="1px"> <tr> <td>书籍编号</td> <td>名称</td> <td>做者</td> <td>详细信息</td> <td>价格</td> </tr> <c:forEach items="${books}" var="me"> <tr> <td>${me.key}</td> <td>${me.value.name}</td> <td>${me.value.author}</td> <td>${me.value.description}</td> <td>${me.value.price}</td> </tr> </c:forEach> </table> </body> </html>
做为购物车的案例,怎么能没有购买的操做呢?因而乎就增长购买的操做!服务器
//获取获得传递过来的id String id = request.getParameter("bookid"); //把用户想要买的书放到购物车上 //用户不仅仅只有一个,要让购物车上只为当前的用户服务,就须要用到会话跟踪技术了 Cart cart = (Cart) request.getSession().getAttribute("cart"); //若是当前用户尚未点击过购买的商品,那么是用户的购物车是空的 if (cart == null) { cart = new Cart(); request.getSession().setAttribute("cart", cart); } //调用BussinessService的方法,实现购买功能! BusinessService businessService = new BusinessService(); businessService.buyBook(id, cart); //跳转到购物车显示的页面上 request.getRequestDispatcher("/listCart.jsp").forward(request, response);
/* * 在购买书籍的时候,咱们发现须要将书籍添加到购物车上 * 若是咱们直接在Servlet上使用Cart实体对象的addBook()和BookDao对象的find()方法,是能够完成功能的 * * 可是,这样web层的程序就跟Dao层的耦合了,为了代码性的健壮性和解耦,咱们在BusinessService中对他俩进行封装 * * 因而有了buyBook()这个方法! * */ /*把用户想买的书籍添加到当前用户的购物车上*/ public void buyBook(String id, Cart cart) { Book book = bookDao.find(id); cart.addBook(book); }
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html> <head> <title>购物车显示页面</title> </head> <body> <h1>购物车显示页面</h1> <%--empty函数是判断集合中有没有元素--%> <%--若是购物车是没有任何购物项的--%> <c:if test="${empty(cart.bookMap)}"> <h1>您尚未购买过任何的书籍呀!</h1> </c:if> <%--若是购物车有购物项,就应该把购物项的信息显示给用户--%> <c:if test="${!empty(cart.bookMap)}"> <table border="1px"> <tr> <td>书籍编号</td> <td>名称</td> <td>数量</td> <td>小计</td> <td>操做</td> </tr> <c:forEach items="${cart.bookMap}" var="me"> <tr> <td>${me.key}</td> <td>${me.value.book.name}</td> <td>${me.value.quantity}</td> <td>${me.value.price}</td> <td><a href="#">删除</a></td> </tr> </c:forEach> <tr> <td colspan="2"><a href="#">清空购物车</a></td> <td colspan="2">合计:</td> <td>${cart.price}</td> </tr> </table> </c:if> </table> </body> </html>
想要删除购物车中的商品,也很简单,把删除操做挂在超连接上,超连接指向DeleteCartServlet,并将想要删除的书本的id带过去(不将id带过去,服务器哪知道你要删除的是哪一个)!markdown
<td><a href="${pageContext.request.contextPath}/DeleteCartBook?bookid=${me.key}">删除</a></td>
//获取获得用户想要删除哪一个书本的id String id = request.getParameter("bookid"); //获取该用户相对应的购物车对象 Cart cart = (Cart) request.getSession().getAttribute("cart"); try { //删除购物车的商品,也应该是在BusinessService中有的功能,因而乎又回到BusinessService中写代码 BusinessService businessService = new BusinessService(); businessService.deleteBook(id, cart); //删除购物车的商品后,也应该直接跳转回去购物车的显示页面中 request.getRequestDispatcher("/WEB-INF/listCart.jsp").forward(request, response); } catch (CartNotFoundException e) { request.setAttribute("message", "购物车空了!"); request.getRequestDispatcher("/message.jsp").forward(request, response); } catch (Exception e) { e.printStackTrace(); request.setAttribute("message", "删除中出现了异常~待会再试试呗!"); request.getRequestDispatcher("/message.jsp").forward(request, response); }
/*用户要在购物车中删除某个购物项*/ public void deleteBook(String id, Cart cart) throws CartNotFoundException { //若是用户是直接访问DeleteCartBook的Servlet的,在session中是没有cart这个属性的! //告诉用户购物车是空的 if (cart == null) { throw new CartNotFoundException("购物车为空"); } //把购物项移除出去集合就好了! cart.getBookMap().remove(id); }
效果:session
从上面的gif咱们就能够发现,若是我重复买一本书,须要一本一本地点!这样会很是麻烦!
咱们要怎么实现:用户想要买多少本,购物车的数量就修改成多少本呢?
<td><input type="text" name="quantity" value="${me.value.quantity}"></td>
好的,如今咱们已经可以把数量随本身想要多少本,就改为是多少了。如今主要的问题就是,怎么在改的同时,数据也及时地更新?
咱们写javascript的代码,监控着输入框的变更,若是有变更,就响应事件,将变更的数据传递给服务器,更新数据!
<script type="text/javascript"> /* * @input 将输入框自己填入(这样能够获取获得输入框的值) * @id 将书本的id传递进来,告诉服务器是修改哪个购物项(书) * @oldValue 本来的值,若是用户不想修改了,就修改成本来的值(下面会询问用户是否肯定修改) * */ function update(input,id,oldValue) { //获取获得输入框的数据 var quantity = input.value; //询问用户是否真的修改 var b = window.confirm("你肯定修改吗?"); //若是肯定修改,就跳转到修改的Servlet上 if(b) { window.location.href = "${pageContext.request.contextPath}/UpdateQuantity?bookid=" + id + "&quantity=" + quantity + ""; }else { //若是不肯定修改,把输入框的数据改为是原来的 input.value = oldValue; } } </script>
//获取获得用户想要修改哪一本书的id和相对应的数量 String id = request.getParameter("bookid"); String quantity = request.getParameter("quantity"); //获得当前用户的购物车 Cart cart = (Cart) request.getSession().getAttribute("cart"); try { //调用BusinessService的方法去修改对应的数据 BusinessService businessService = new BusinessService(); businessService.updateQuantity(id, cart, quantity); //修改完再跳转回去购物车的页面中 request.getRequestDispatcher("/WEB-INF/listCart.jsp").forward(request, response); } catch (CartNotFoundException e) { e.printStackTrace(); request.setAttribute("message", "购物车是空的!"); request.getRequestDispatcher("message.jsp").forward(request, response); }
public void updateQuantity(String id, Cart cart, String quantity) throws CartNotFoundException { //若是用户是直接访问DeleteCartBook的Servlet的,在session中是没有cart这个属性的! //告诉用户购物车是空的 if (cart == null) { throw new CartNotFoundException("购物车为空"); } //经过书的id获取获得购物车的购物项,再修改购物项的数量便可!(由于书的id和获取购物项的关键字是一致的!) cart.getBookMap().get(id).setQuantity(Integer.parseInt(quantity)); }
清空购物车的作法和上面是相似的!也是首先经过javaScript代码询问用户是否要清空,若是要清空就跳转到相对应的Servlet中把购物车的数据清空了!
<td colspan="2"> <a href="${pageContext.request.contextPath}/ClearCart" onclick=" return clearCart()" >清空购物车</a> </td>
function clearCart() { var b = window.confirm("你肯定要清空购物车吗?"); //若是用户肯定,就跳转到相对应的Servlet上 if(b) { return true; }else { return false; } }
//获得用户相对应的购物车 Cart cart = (Cart) request.getSession().getAttribute("cart"); //调用相对应BusinessService的方法 BusinessService businessService = new BusinessService(); try { //清空购物车【实际上就是清空购物车的Map集合中的元素】 businessService.clearCart(cart); //返回给购物车显示页面 request.getRequestDispatcher("/WEB-INF/listCart.jsp").forward(request, response); } catch (CartNotFoundException e) { e.printStackTrace(); request.setAttribute("message", "购物车是空的!"); request.getRequestDispatcher("/message.jsp").forward(request, response); }
public void clearCart(Cart cart) throws CartNotFoundException { //若是用户是直接访问DeleteCartBook的Servlet的,在session中是没有cart这个属性的! //告诉用户购物车是空的 if (cart == null) { throw new CartNotFoundException("购物车为空"); } //清空全部的购物项 cart.getBookMap().clear(); }