前言

为了巩固MVC的开发模式,下面就写一个购物车的小案例..

①构建开发环境

导入需要用到的开发包

建立程序开发包


②设计实体

书籍实体


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
}

购物车与购物项实体

可能我们会这样设计购物车


/*该类代表的是购物车*/
public class Cart { //关键字是书籍的id,值是书
private Map<String, Book> bookMap = new LinkedHashMap<>(); }

上面的做法是不合适的,试想一下:如果我要购买两本相同的书,购物车的页面上就出现了两本书,而不是书2。买三本相同的书就在购物页面上出现三本书,而不是书3.

因此,Map集合的值不能是Book对象,那我们怎么才能解决上面所说的问题呢?我们最常用的就是,再写一个实体CartItem(代表购物项)

  • 好的,我们先来写购物项实体吧,等会再写购物车!

/*购物项代表的是当前书,并表示该书出现了几次*/
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而已。


//既然是购物车案例,应该会有增删的操作,通过关键字查询书籍,所以使用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

dao层应该至少提供获取所有的书籍根据关键字获取得到书籍


public class BookDao { //获取存放着书籍的Map集合
public Map getAll() {
return BookDB.getAll();
} //根据关键字获取某本书籍
public Book find(String id) {
return BookDB.getAll().get(id);
}
}

⑤开发service#

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);
} //...待会还有其他的功能再从这里补充!
}

⑥开发web

列出所有的书

开发提供JSP页面的Servlet

        //调用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);

开发显示所有书籍的jsp


<%@ 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>

购买操作

作为购物车的案例,怎么能没有购买的操作呢?于是乎就增加购买的操作

开发处理购买的Servlet

        //获取得到传递过来的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);
  • 在我们前面开发BusinessService时,是没有buyBook()这个方法的!下面更新了BusinessService的代码


    /*
* 在购买书籍的时候,我们发现需要将书籍添加到购物车上
* 如果我们直接在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带过去,服务器哪知道你要删除的是哪个)


<td><a href="${pageContext.request.contextPath}/DeleteCartBook?bookid=${me.key}">删除</a></td>

开发DeleteCartBook的Servlet


//获取得到用户想要删除哪个书本的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);
}
  • BusinessService又多了一个功能

/*用户要在购物车中删除某个购物项*/
public void deleteBook(String id, Cart cart) throws CartNotFoundException { //如果用户是直接访问DeleteCartBook的Servlet的,在session中是没有cart这个属性的!
//告诉用户购物车是空的
if (cart == null) {
throw new CartNotFoundException("购物车为空");
} //把购物项移除出去集合就行了!
cart.getBookMap().remove(id);
}

效果:

多本一起购买

从上面的gif我们就可以发现,如果我重复买一本书,需要一本一本地点!这样会非常麻烦!

我们要怎么实现:用户想要买多少本,购物车的数量就修改为多少本呢

在购物车上,数量的值改成是输入框


<td><input type="text" name="quantity" value="${me.value.quantity}"></td>

效果:

好的,现在我们已经能够把数量随自己想要多少本,就改成是多少了。现在主要的问题就是,怎么在改的同时,数据也及时地更新?

写javascript代码,让输入框的信息提交给服务器

我们写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>

编写UpdateQuantity的Servlet


//获取得到用户想要修改哪一本书的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);
}

BusinessService增添了updateQuantity()方法


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)); }
  • 效果如下gif


清空购物车

清空购物车的做法和上面是类似的!也是首先通过javaScript代码询问用户是否要清空,如果要清空就跳转到相对应的Servlet中把购物车的数据清空了!

在清空购物车的链接上绑定事件


<td colspan="2">
<a href="${pageContext.request.contextPath}/ClearCart" onclick=" return clearCart()" >清空购物车</a>
</td>

javaScript代码做逻辑判断

        function clearCart() {

            var b = window.confirm("你确定要清空购物车吗?");

            //如果用户确定,就跳转到相对应的Servlet上
if(b) {
return true;
}else {
return false;
}
}

编写ClearCart代码


//得到用户相对应的购物车
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);
}

在BusinessService中添加清空购物车功能


public void clearCart(Cart cart) throws CartNotFoundException { //如果用户是直接访问DeleteCartBook的Servlet的,在session中是没有cart这个属性的!
//告诉用户购物车是空的
if (cart == null) {
throw new CartNotFoundException("购物车为空");
} //清空所有的购物项
cart.getBookMap().clear(); }
  • 效果:


总结

  1. 购物车的应该是一个以id作为key,以购物项作为value的一个Map集合。这样设计的话,我们在显示商品的时候,就不会重复显示同一种类型的商品了。
  2. 购物项代表着该商品,并且应该给予购物项 数量和价钱的属性。购物项的价钱应该是数量*单价
  3. 购物车应该提供把商品添加到购物车的功能。当然啦,购物项代表着商品,所以首先要判断该购物车是否有同类的商品,如果有,直接在购物项的数量上+1即可的。如果没有,就设置该购物项的属性,并把购物项添加到购物车中
  4. 购物车的总价就是所有购物项的总价
  5. 无论是增删改查购物车的数据,其实就是操作这个集合

如果文章有错的地方欢迎指正,大家互相交流。习惯在微信看技术文章的同学,可以关注微信公众号:Java3y

购物车【JavaWeb小项目、简单版】的更多相关文章

  1. JavaWeb小项目(一)

    总结一下前段时间,在学了JSP.Servlet.JavaBean后,配合Tomcat服务器加上MySQl数据库搭的第一个简单网站. 前前后后,在学习了以上说的这些概念知识后,还进一步熟悉了整个搭建的流 ...

  2. 前端练手小项目——网页版qq音乐仿写

    qq音乐网页版仿写 一些步骤与注意事项 一开始肯定就是html+css布局和页面了,这段特别耗时间,耐心写完就好了 首先要说一下大致流程: 一定要先布局html!,所以一定要先分析页面布局情况,用不同 ...

  3. Python之路【第十八篇】Django小项目简单BBS论坛部分内容知识点

    开发一个简单的BBS论坛 项目需求: 整体参考“抽屉新热榜” + “虎嗅网” 实现不同论坛版块 帖子列表展示 帖子评论数.点赞数展示 在线用户展示 允许登录用户发贴.评论.点赞 允许上传文件 帖子可被 ...

  4. Django小项目简单BBS论坛

    开发一个简单的BBS论坛 项目需求: 1 整体参考"抽屉新热榜" + "虎嗅网" 2 实现不同论坛版块 3 帖子列表展示 4 帖子评论数.点赞数展示 5 在线用 ...

  5. Vue小项目二手书商城:(四)详情页和购物车(emit、prop、computed)

    实现效果: 点击对应商品,对应的商品详情页出现,详情页里面还有“Add to cart”按钮和“×”退出按钮. 点击“Add to cart”可以将商品加入购物车,每件商品只能添加一次,如果把购物车的 ...

  6. Session小案例-----简单购物车的使用

    Session小案例-----简单购物车的使用 同上篇一样,这里的处理请求和页面显示相同用的都是servlet. 功能实现例如以下: 1,显示站点的全部商品 2.用户点击购买后,可以记住用户选择的商品 ...

  7. IOS-小项目(饿了么 网络部分 简单实现)

    在介绍小项目之前,在此说明一下此代码并非本人所写,我只是随笔的整理者. 在介绍之前先展现一下效果图. 看过效果图大家应该很熟悉了,就是饿了么的一个界面而已,值得注意的是,实现时并没有采用本地连接,而是 ...

  8. web实践小项目<一>:简单日程管理系统(涉及html/css,javascript,python,sql,日期处理)

    暑假自学了些html/css,javascript和python,苦于学完无处练手几乎过目即忘...最后在同学的建议下做了个简单日程管理系统.借第一版完成之际,希望能将实践期间犯过的错误和获得的新知进 ...

  9. C语言实现简单计算器小项目

    昨天刚安装上devc++,半夜想着练练C语言吧 于是就看到实验楼有一个计算器的项目 之前做过一次,这次写的主要是思路 首先我们先从原理思考jia,实现简单的计算器就要具备加减乘除这些,看普通的计算器也 ...

随机推荐

  1. 算法-java代码实现基数排序

    基数排序 第11节 基数排序练习题 对于一个int数组,请编写一个基数排序算法,对数组元素排序. 给定一个int数组A及数组的大小n,请返回排序后的数组.保证元素均小于等于2000. 测试样例: [1 ...

  2. 小白的Python之路 day5 configparser模块的特点和用法

    configparser模块的特点和用法 一.概述 主要用于生成和修改常见配置文件,当前模块的名称在 python 3.x 版本中变更为 configparser.在python2.x版本中为Conf ...

  3. Redis 数据结构与内存管理策略(下)

    Redis 数据结构与内存管理策略(下) 标签: Redis Redis数据结构 Redis内存管理策略 Redis数据类型 Redis类型映射 Redis 数据类型特点与使用场景 String.Li ...

  4. oc 快排算法

    直接复制粘贴就可以用了 - (void)viewDidLoad { [super viewDidLoad]; NSMutableArray *M_arr = [[NSMutableArray allo ...

  5. (Release Candidate)Candidate

    RC:(Release Candidate)Candidate是候选人的意思,用在软件或者操作系统上就是候选版本

  6. hive下UDF函数的使用

    1.编写函数 [java] view plaincopyprint?package com.example.hive.udf;    import org.apache.hadoop.hive.ql. ...

  7. J.U.C JMM. pipeline.指令重排序,happen-before(续MESI协议)

    缓存(Cache)       CPU的读/写(以及取指令)单元正常情况下甚至都不能直接访问内存——这是物理结构决定的:CPU都没有管脚直接连到内存.相反,CPU和一级缓存(L1 Cache)通讯,而 ...

  8. HEXO+Github,搭建属于自己的博客

    摘录自:http://www.jianshu.com/p/465830080ea9 1. github的准备 账号 密码 建立Repository建立与你用户名对应的仓库,仓库名必须为[your_us ...

  9. AMS常见问题

      1.安装完AMS是否就可以实现直播点播了? 答:是的,ams运行后,就可以提供各种协议的直播输出地址,和点播地址:不过一般情况下客户还得需要另外的Web系统来配合使用,来完成对前台用户界面的友好化 ...

  10. android 监听返回键

    android监听返回键 public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE ...