阶段一:
前端表单验证:
<script type="text/javascript" src="../../static/script/jquery-1.7.2.js"></script>
<script type="text/javascript">
// 页面加载完成之后
$(function () {
// 给注册绑定单击事件
$("#sub_btn").click(function () {
// 验证用户名:必须由字母,数字下划线组成,并且长度为5到12位
//1 获取用户名输入框里的内容
var usernameText = $("#username").val();
//2 创建正则表达式对象
var usernamePatt = /^\w{5,12}$/;
//3 使用test方法验证
if (!usernamePatt.test(usernameText)) {
//4 提示用户结果
$("span.errorMsg").text("用户名不合法!");
return false;
}
// 验证密码:必须由字母,数字下划线组成,并且长度为5到12位
//1 获取用户名输入框里的内容
var passwordText = $("#password").val();
//2 创建正则表达式对象
var passwordPatt = /^\w{5,12}$/;
//3 使用test方法验证
if (!passwordPatt.test(passwordText)) {
//4 提示用户结果
$("span.errorMsg").text("密码不合法!");
return false;
}
// 验证确认密码:和密码相同
//1 获取确认密码内容
var repwdText = $("#repwd").val();
//2 和密码相比较
if (repwdText != passwordText) {
//3 提示用户
$("span.errorMsg").text("确认密码和密码不一致!");
return false;
}
// 邮箱验证:xxxxx@xxx.com
//1 获取邮箱里的内容
var emailText = $("#email").val();
//2 创建正则表达式对象
var emailPatt = /^[a-z\d]+(\.[a-z\d]+)*@([\da-z](-[\da-z])?)+(\.{1,2}[a-z]+)+$/;
//3 使用test方法验证是否合法
if (!emailPatt.test(emailText)) {
//4 提示用户
$("span.errorMsg").text("邮箱格式不合法!");
return false;
}
// 验证码:现在只需要验证用户已输入。因为还没讲到服务器。验证码生成。
var codeText = $("#code").val();
//去掉验证码前后空格
// alert("去空格前:["+codeText+"]")
codeText = $.trim(codeText);
// alert("去空格后:["+codeText+"]")
if (codeText == null || codeText == "") {
//4 提示用户
$("span.errorMsg").text("验证码不能为空!");
return false;
}
// 去掉错误信息
$("span.errorMsg").text("");
});
});
</script>
阶段二:
1.JavaEE 项目的三层架构
分层的目的是为了解耦。解耦就是为了降低代码的耦合度。方便项目后期的维护和升级。
web 层 com.kk.web/servlet/controller
service 层 com.kk.service Service 接口包 com.kk.service.impl Service 接口实现类
dao 持久层 com.kk.dao Dao 接口包 com.kk.dao.impl Dao 接口实现类
实体 bean 对象 com.kk.pojo/entity/domain/bean JavaBean
类 测试包 com.kk.test/junit
工具类 com.kk.utils
2、添加base标签
3、编写 RegistServlet 程序
protected void doPost(javax.servlet.http.HttpServletRequest req, javax.servlet.http.HttpServletResponse resp) throws javax.servlet.ServletException, IOException {
// 1、获取请求的参数
String username = req.getParameter ("username");
String password = req.getParameter ("password");
String email = req.getParameter ("email");
String code = req.getParameter ("code");
// 2、检查 验证码是否正确 === 写死,要求验证码为:abcde
if ("abcde".equalsIgnoreCase (code)) {
// 3、检查 用户名是否可用
if (userService.existsUsername (username)) {
System.out.println ("用户名[" + username + "]已存在!");
// 跳回注册页面
req.getRequestDispatcher ("/pages/user/regist.html").forward (req, resp);
} else { // 可用 // 调用 Sservice 保存到数据库
userService.registUser (new User (null, username, password, email));
// 跳到注册成功页面 regist_success.html
req.getRequestDispatcher ("/pages/user/regist_success.html").forward (req, resp);
}
} else {
System.out.println ("验证码[" + code + "]错误");
req.getRequestDispatcher ("/pages/user/regist.html").forward (req, resp);
}
}
4、调试的光标详解
5、用户登录功能的实现
LoginServlet 程序
package com.kk.web;
import com.kk.pojp.User;
import com.kk.service.UserService;
import com.kk.service.impl.UserServiceImpl;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class LoginServlet extends HttpServlet {
private UserService userService = new UserServiceImpl ();
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1、获取请求的参数
String username = req.getParameter("username");
String password = req.getParameter("password");
// 调用 userService.login()登录处理业务
User login = userService.login (new User (null, username, password,null));
// 如果等于 null,说明登录 失败!
if (login==null)
{
//跳回登录页面
req.getRequestDispatcher ("/pages/user/login.html").forward (req,resp);
}
else
{
// 登录 成功 //跳到成功页面 login_success.html
req.getRequestDispatcher ("/pages/user/login_success.html").forward (req,resp);
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
UserServlet(登录注册最终处理):
package com.kk.web;
import com.google.code.kaptcha.servlet.KaptchaServlet;
import com.kk.pojp.User;
import com.kk.service.UserService;
import com.kk.service.impl.UserServiceImpl;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import static com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY;
/*
* @Description: 登录+注册+注销
* @Author: Jk_kang
* @CreateDate: 2020/7/21 15:05
* @Param:
* @Return:
**/
public class UserServlet extends BaseServlet {
private UserService userService = new UserServiceImpl ( );
/**
* 登录
*
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void login(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1、获取请求的参数
String username = req.getParameter ("username");
String password = req.getParameter ("password");
// 调用 userService.login()登录处理业务
User login = userService.login (new User (null, username, password, null));
// 如果等于 null,说明登录 失败!
if (login == null) {
//错误信息,回显表单项信息,保存到request域中
req.setAttribute ("meg", "用户名或者密码错误");
req.setAttribute ("username", username);
//跳回登录页面
req.getRequestDispatcher ("/pages/user/login.jsp").forward (req, resp);
} else {
// 登录 成功
// 保存用户登录的信息到session域中
req.getSession ( ).setAttribute ("user", login);//login是查询出对应的User对象
//跳到成功页面 login_success.jsp
req.getRequestDispatcher ("/pages/user/login_success.jsp").forward (req, resp);
}
}
/**
* 注册
*
* @param req
* @param resp
* @throws javax.servlet.ServletException
* @throws IOException
*/
protected void regist(javax.servlet.http.HttpServletRequest req, javax.servlet.http.HttpServletResponse resp) throws javax.servlet.ServletException, IOException {
//获取 Session中的验证码
String token = (String) req.getSession ( ).getAttribute (KAPTCHA_SESSION_KEY);
//删除 Session中的验证码
req.getSession ( ).removeAttribute (KAPTCHA_SESSION_KEY);
//获取输入的 code(验证码值)
String code = req.getParameter ("code");
// 1、获取请求的参数
String username = req.getParameter ("username");
String password = req.getParameter ("password");
String email = req.getParameter ("email");
// 2、检查 验证码是否正确
if (token != null && token.equalsIgnoreCase (code)) {//equalsIgnoreCase不考虑大小写
// 3、检查 用户名是否可用
if (userService.existsUsername (username)) {
// 把 错误提示 信息,保存到Request域中回显
req.setAttribute ("msg", "用户名已存在!!");
// 跳回注册页面
req.getRequestDispatcher ("/pages/user/regist.jsp").forward (req, resp);
} else { // 调用 Sservice 保存到数据库
userService.registUser (new User (null, username, password, email));
// 跳到注册成功页面 regist_success.jsp
req.getRequestDispatcher ("/pages/user/regist_success.jsp").forward (req, resp);
}
} else {
req.setAttribute ("msg", "错误的验证码信息!!");
req.getRequestDispatcher ("/pages/user/regist.jsp").forward (req, resp);
}
}
/**
* 注销登录
*
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void logout(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1、销毁 Session 中用户登录的信息(或者销毁 Session)
req.getSession ( ).invalidate ( );
//2、重定向到首页(或登录页面)
System.out.println (req.getContextPath ( ));
resp.sendRedirect (req.getContextPath ( ));
}
}
阶段三:
1、页面JSP动态化
替换h5头部,防止后面静态包含公共jsp乱码
2、抽取公用的内容(方便维护:改一处,被引用的地方也改到)
抽取部位:
头部(css,jquery,base)
页脚(友情链接,水印)
重复的内容(菜单,登录信息)
抽取的完整jsp代码:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!--写 base 标签,永远固定相对路径跳转的结果-->
<base href="http://localhost:8080/book/">
<link type="text/css" rel="stylesheet" href="static/css/style.css">
<script type="text/javascript" src="static/script/jquery-1.7.2.js"></script>
引用的jsp代码
<%@include file="../common/head.jsp"%>
3、登录,注册错误提示,及表单回显
以登录回显为示例:
Servlet 程序端需要添加回显信息到 Request 域中
// 如果等于 null,说明登录 失败!
if (login==null)
{
//错误信息,回显表单项信息,保存到request域中
req.setAttribute ("meg","用户名或者密码错误");
req.setAttribute ("username",username);
//跳回登录页面
req.getRequestDispatcher ("/pages/user/login.jsp").forward (req,resp);
}
jsp 页面,需要输出回显信息
<div id="l_content">
<span class="login_word">欢迎注册</span>
</div>
4、BaseServlet 的抽取(重头戏)
在实际的项目开发中,一个模块,一般只使用一个 Servlet 程序。
4.1、代码优化一:代码优化:合并 LoginServlet 和 RegistServlet 程序为 UserServlet 程序
公用的UserServlet:
private UserService userService = new UserServiceImpl ();
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String action = request.getParameter ("action");
if ("login".equals (action))
{
login (request,response);
}
else if ("regist".equals (action)){
regist (request,response);
}
}
jsp上的修改:
4.2、优化代码二:使用反射优化大量 else if 代码:
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String action = request.getParameter ("action");
//优化二:反射
try {
// 获取 action 业务鉴别字符串,获取相应的业务 方法反射对象
Method method = this.getClass ( ).getDeclaredMethod (action, HttpServletRequest.class,
HttpServletResponse.class);
//System.out.println (method);
// 调用目标业务 方法
method.invoke (this,request,response);
} catch (Exception e) {
e.printStackTrace ( );
}
}
科普:
4.3、抽取 BaseServlet 程序(final)
BaseServlet 程序代码:
public abstract class BaseServlet extends HttpServlet {
//<a>标签请求默认为GET,所以。。
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String action = req.getParameter ("action");
try {
// 获取action业务鉴别字符串,获取相应的业务 方法反射对象
Method method = this.getClass ( ).getDeclaredMethod (action, HttpServletRequest.class, HttpServletResponse.class);
//System.out.println (method);
// 调用目标业务 方法
method.invoke (this, req, resp);
} catch (Exception e) {
e.printStackTrace ( );
}
}
}
修改 UserServlet 程序继承 BaseServlet 程序
public class UserServlet extends BaseServlet {
5、数据的封装和抽取 BeanUtils 的使用
WebUtils 工具类:
public class WebUtilts {
/**
* 把 Map 中的值注入到对应的 JavaBean 属性中。
* @param value
* @param bean */
public <T> T copyParamToBean(Map value,T bean)
{
try {
System.out.println("注入之前:" + bean);
/*** 把所有请求的参数都注入到 user 对象中 */
BeanUtils.populate (bean,value);
System.out.println("注入之后:" + bean);
} catch (IllegalAccessException|InvocationTargetException e) {
e.printStackTrace ( );
}
return bean;
}
}
6、EL 表达式修改表单回显(登录为例)
阶段四:
何为MVC:MVC 的作用还是为了降低耦合。让代码合理分层。方便后期升级和维护
1.1、插入数据库测试数据
1.2、编写图书模块的 JavaBean
1.3、编写图书模块的 Dao 和测试 Dao
1.4、编写图书模块的 Service 和测试 Service
1.5、编写图书模块的 Web 层,和页面联调测试
1.5.1、图书列表功能的实现:
1、图解列表功能流程:
image.png
2、BookServlet 程序中添加 list 方法
image.png
image.png
3、修改【图书管理】请求地址
image.png
4、修改 pages/manager/book_manager.jsp 页面的数据遍历输出
引入JST:<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
页面渲染:
<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>
1.5.2前后台概述:
1.5.3、添加图书功能的实现
1.5.3.1、添加图书流程细节:
image.png
1.5.3.2、问题说明:表单重复提交:
当用户提交完请求,浏览器会记录下最后一次请求的全部信息。当用户按下功能键 F5,就会再一次发起浏览器记录的最后一次 请求。
1.5.3.3、BookServlet 程序中添加 add 方法:
image.png
科普:请求转发和重定向区别:
image.png
1.5.3.4、修改 book_edit.jsp 页面:
image.png
1.5.4、删除图书功能的实现
1.5.4.1、图解删除流程:
image.png
1.5.4.2、BookServlet 程序中的 delete 方法:
image.png
1.5.4.3、给 WebUtils 工具类添加转换 int 类型的工具方法:
image.png
1.5.4.4、修改删除的连接地址:
image.png
1.5.4.5、给删除添加确认提示操作:
image.png
1.5.5、修改图书功能的实现
image.png
1.5.5.2、更新【修改】的请求地址:
image.png
1.5.5.3、BookServlet 程序中添加 getBook 方法:
image.png
1.5.5.4、在 book_edit.jsp 页面中显示修改的数据
image.png
1.5.5.5、在 BookServlet 程序中添加 update 方法:
image.png
1.5.5.6、解决 book_edit.jsp 页面,即要实现添加,又要实现修改操作:
image.png
阶段五(分页):
1、分页模块的分析:
image.png
后端的:
2、分页模型 Page 的抽取(当前页数,总页数,总记录数, 当前页数据,每页记录数):
image.png
3、分页的初步实现:
3.1、BookServlet 程序的代码:
image.png
3.2、BookService程序的代码:
image.png
3.3、BookDao程序的代码:
image.png
前端的:
4、请求地址的修改
4.1、manager_menu.jsp 中【图书管理】请求地址的修改:
image.png
4.2、book_manager.jsp 修改:
image.png
未实现,默认
5、首页、上一页、下一页、末页实现
image.png
6、分页模块中,页码 1,2,【3】,4,5 的显示,要显示 5 个页 码,并且页码可以点击跳转
image.png
image.png
科普:pageEncoding="utf-8"image.png
科普:${param}:image.png
7、Page 对象中的修改:
image.png
8、BookService 中 page 方法的修改:(避免步骤7导致崩溃)
image.png
9、分页模块中,页码 1,2,【3】,4,5 的显示,要显示 5 个页 码,并且页码可以点击跳转:
情况分析一:
image.png
情况分析二:
image.png
代码实现:
<%@ taglib prefix="k" uri="http://java.sun.com/jsp/jstl/core" %>
<%--
Created by IntelliJ IDEA.
User: Administrator
Date: 2020/7/17
Time: 14:58
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
String path = request.getScheme ( ) + "://"
+ request.getServerName ( ) + ":"
+ request.getServerPort ( )
+ request.getContextPath ( )
+ "/";
%>
<%--分页条的开始--%>
<div id="page_nav">
<%--大于首页,才显示--%>
<k: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>
</k:if>
<%--页码输出开始--%>
<k:choose>
<%--情况1:如果总页码小于等于5的情况,页码的范围是:1-总页码--%>
<k:when test="${requestScope.page.pageTotal <= 5}">
<k:set var="begin" value="1"/>
<k:set var="end" value="${requestScope.page.pageTotal}"/>
</k:when>
<%--情况2:总页码大于5的情况--%>
<k:when test="${requestScope.page.pageTotal > 5}">
<k:choose>
<%--小情况1:当前页码为前面3个:1,2,3的情况,页码范围是:1-5.--%>
<k:when test="${requestScope.page.pageTotal} <= 3">
<k:set var="begin" value="1"/>
<k:set var="end" value="5"/>
</k:when>
<%--小情况2:当前页码为最后3个,8,9,10,页码范围是:总页码减4 - 总页码--%>
<k:when test="${requestScope.page.pageNo} > ${requestScope.page.pageTotal -3}">
<k:set var="begin" value="${requestScope.page.pageTotal -4}"/>
<k:set var="end" value="${requestScope.page.pageTotal}"/>
</k:when>
<%--小情况3:4,5,6,7,页码范围是:当前页码减2 - 当前页码加2--%>
<k:otherwise>
<k:set var="begin" value="${requestScope.page.pageTotal -2}"/>
<k:set var="end" value="${requestScope.page.pageTotal +2}"/>
</k:otherwise>
</k:choose>
</k:when>
</k:choose>
<k:forEach begin="${begin}" end="${end}" var="i">
<k:if test="${i==requestScope.page.pageNo}">
<span style="color: #8b1813">【${i}】</span>
</k:if>
<k:if test="${i!=requestScope.page.pageNo}">
<a href="${requestScope.page.url}&pageNo=${i}">${i}</a>
</k:if>
</k:forEach>
<%-- 如果已经 是最后一页,则不显示下一页,末页 --%>
<k: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>
</k:if>
共${requestScope.page.pageTotal}页,${requestScope.page.pageTotalCount}条记录
到第<input value="${param.pageNo}" name="pn" id="pn_input"/>页
<input id="searchPageBtn" type="button" value="GO">
<%--下拉框--%>
跳转至:<select onchange="location='<%=path%>${requestScope.page.url}&pageNo='+this.value">
<k:forEach begin="1" end="${requestScope.page.pageTotal}" var="i">
<option value="${i}" ${requestScope.page.pageNo == i ?'selected':''}>第 ${i} 页</option>
</k:forEach>
</select>
<script type="text/javascript">
$(function () {
$("#searchPageBtn").click(function () {
var pageNo = $("#pn_input").val();
// javaScript 语言中提供了一个 location 地址栏对象
// 它有一个属性叫 href.它可以获取浏览器地址栏中的地址
// href 属性可读,可写
location.href = "<%=path%>${requestScope.page.url}&pageNo=" + pageNo;
});
});
</script>
</div>
<%--分页条的结束--%>
10、修改分页后,增加,删除,修改图书信息的回显页面
以修改图书为示例:
1、在修改的请求地址上追加当前页码参数:
image.png
2、在 book_edit.jsp 页面中使用隐藏域记录下 pageNo 参数:
image.png
3、在服务器重定向的时候,获取当前页码追加上进行跳转:
image.png
3、首页 index.jsp 的跳转
image.png
后端代码:
image.png
前端代码:注:记得用base标签
image.png
image.png
4、分页条的抽取
4.1、抽取分页条中请求地址为 url 变量
4.1.1.在 page 对象中添加 url 属性:
image.png
4.1.2 在 Servlet 程序的 page 分页方法中设置 url 的分页请求地址:
image.png
4.1.3、修改分页条中请求地址为 url 变量输出,并抽取一个单独的 jsp 页面:
image.png
5、首页价格搜索
1、分析图:
image.png
2、后端:
2.1、Controller层:
image.png
2.2、Service层:
image.png
2.3、dao层:
image.png
3、前端:
image.png
阶段六:自动登录+验证码(session&&cookie)
1、登陆---显示用户名
1.1、UserServlet 程序中保存用户登录的信息:
image.png
1.2、修改 login_succuess_menu.jsp:
image.png
1.3、还要修改首页 index.jsp 页面的菜单 :
image.png
2、登出---注销用户
1、销毁 Session 中用户登录的信息(或者销毁 Session)
2、重定向到首页(或登录页面)
UserServlet 程序中添加 logout 方法:
image.png
修改【注销】的菜单地址:
image.png
3、表单重复提交之-----验证码
分析:image.png
原理图:
image.png
4、谷歌 kaptcha 图片验证码的使用
谷歌验证码 kaptcha 使用步骤如下:
1、导入谷歌验证码的 jar 包:kaptcha-2.3.2.jar
2、在 web.xml 中去配置用于生成验证码的 Servlet 程序:
image.png
3、在表单中(注册页面)使用 img 标签去显示验证码图片并使用它:
image.png
4、在服务器(注册控制层接口)获取谷歌生成的验证码和客户端发送过来的验证码比较使用:
image.png
5、切换验证码:
阶段七:购物车
1、购物车模块分析
2、购物车模型编写
2.1、购物车模型:
CartItem:购物车中的商品对象
image.png
Cart:购物车对象:
image.png
补充:BigDecimal类运算Api
image.png
参考文章:https://blog.csdn.net/PacosonSWJTU/article/details/80490464?utm_source=blogxgwz1
package com.kk.pojp;
import lombok.Data;
import java.math.BigDecimal;
import java.util.LinkedHashMap;
import java.util.Map;
//购物车
@Data
public class Cart {
/**
* key为商品编号
* value为具体商品信息
*/
private Map<Integer, CartItem> items = new LinkedHashMap<> ( );
/**
* 添加商品项
*
* @param cartItem
*/
public void addItem(CartItem cartItem) {
// 先查看购物车中是否已经添加过此商品,如果已添加,则数量累加,总金额更新,如果没有添加过,直接放到 集合中即可
CartItem item = items.get (cartItem.getId ( ));
if (item == null) {
// 之前没添加过此商品
items.put (cartItem.getId ( ), cartItem);
} else {
// 已经 添加过的情况
item.setCount (item.getCount ( ) + 1);// 数量 累加
item.setTotalPrice (item.getPrice ( ).multiply (new BigDecimal (item.getCount ( ))));// 更新总金额
}
}
/**
* 删除商品项
*
* @param id
*/
public void deleteItem(Integer id) {
items.remove (id);
}
/**
* 清空购物车
*/
public void clear() {
items.clear ( );
}
/**
* 修改购物车中的商品数量
*
* @param id
* @param count
*/
public void updateCount(Integer id, Integer count) {
// 先查看购物车中是否有此商品。如果有,修改商品数量,更新总金额
CartItem item = items.get (id);
if (item != null) {
item.setCount (count);// 修改商品数量
item.setTotalPrice (item.getPrice ( ).multiply (new BigDecimal (item.getCount ( ))));// 更新总金额
}
}
/**
* 获取购物车中总数量
*
* @return
*/
public Integer getTotalCount() {
Integer totalCount = 0;
for (Map.Entry<Integer, CartItem> entry : items.entrySet ( )) {
totalCount += entry.getValue ( ).getCount ( );
}
return totalCount;
}
/**
* 获取购物车中总价格
*
* @return
*/
public BigDecimal getTotalPrice() {
BigDecimal titalPrice = new BigDecimal (0);
for (Map.Entry<Integer, CartItem> entry : items.entrySet ( )) {
titalPrice = titalPrice.add (entry.getValue ( ).getTotalPrice ().multiply (new BigDecimal (entry.getValue ().getCount ())));
}
return titalPrice;
}
@Override
public String toString() {
return "Cart{" + "totalCount=" + getTotalCount ( ) + ", totalPrice=" + getTotalPrice ( ) + ", items=" + items + '}';
}
}
2.2、购物车的测试:
@Test
public void addItem() {
Cart cart = new Cart ( );
cart.addItem (new CartItem (1, "java从入门到精通", 1, new BigDecimal (1000), new BigDecimal (1000)));
cart.addItem (new CartItem (1, "java从入门到精通", 1, new BigDecimal (1000), new BigDecimal (1000)));
cart.addItem (new CartItem (2, "数据结构与算法", 1, new BigDecimal (100), new BigDecimal (100)));
System.out.println (cart);
}
@Test
public void deleteItem() {
Cart cart = new Cart ( );
cart.addItem (new CartItem (1, "java从入门到精通", 1, new BigDecimal (1000), new BigDecimal (1000)));
cart.addItem (new CartItem (1, "java从入门到精通", 1, new BigDecimal (1000), new BigDecimal (1000)));
cart.addItem (new CartItem (2, "数据结构与算法", 1, new BigDecimal (100), new BigDecimal (100)));
cart.deleteItem (1);
System.out.println (cart);
}
@Test
public void clear() {
Cart cart = new Cart ( );
cart.addItem (new CartItem (1, "java从入门到精通", 1, new BigDecimal (1000), new BigDecimal (1000)));
cart.addItem (new CartItem (1, "java从入门到精通", 1, new BigDecimal (1000), new BigDecimal (1000)));
cart.addItem (new CartItem (2, "数据结构与算法", 1, new BigDecimal (100), new BigDecimal (100)));
cart.deleteItem (1);
cart.clear ( );
System.out.println (cart);
}
@Test
public void updateCount() {
Cart cart = new Cart ( );
cart.addItem (new CartItem (1, "java从入门到精通", 1, new BigDecimal (1000), new BigDecimal (1000)));
cart.addItem (new CartItem (1, "java从入门到精通", 1, new BigDecimal (1000), new BigDecimal (1000)));
cart.addItem (new CartItem (2, "数据结构与算法", 1, new BigDecimal (100), new BigDecimal (100)));
cart.deleteItem (1);
cart.clear ( );
cart.addItem (new CartItem (1, "java从入门到精通", 1, new BigDecimal (1000), new BigDecimal (1000)));
cart.updateCount (1, 10);
System.out.println (cart);
}
3、加入购物车功能的实现
3.1、CartServlet 程序中的代码:
private BookService bookService = new BookServiceImpl ();
/**
* 加入购物车
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void addItem(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取请求的参数 商品编号
Integer id = WebUtilts.getParameter ("id", Integer.class,req);
// 调用 bookService.queryBookById(id):Book 得到图书的信息
Book book = bookService.queryBookById (id);
// 把图书信息,转换成为 CartItem 商品项
CartItem cartItem = new CartItem (book.getId ( ), book.getName ( ), 1, book.getPrice ( ), book.getPrice ( ));
// 调用 Cart.addItem(CartItem);添加商品项
Cart cart = (Cart) req.getSession ( ).getAttribute ("cart");
if (cart==null)
{
cart = new Cart ();
req.getSession ().setAttribute ("cart",cart);
}
cart.addItem (cartItem);
System.out.println (cart );
System.out.println ("请求头Referer的值: "+req.getHeader ("Referer") );
// 重定向回原来商品所在的地址页面
resp.sendRedirect(req.getHeader("Referer"));
}
3.2、index.jsp 页面 js 的代码:
3.3、图解说明,如何跳回添加商品的页面:
4、购物车的展示(cart.jsp页面渲染)
5、删除购物车商品项
5.1、CartServlet 程序:
/**
* 删除购物车
*
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void deleteItem(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取商品编号
Integer id = WebUtilts.getParameter ("id", Integer.class, req);
// 获取购物车对象
Cart cart = (Cart) req.getSession ( ).getAttribute ("cart");
if (cart != null) {
// 删除 了购物车商品项
cart.deleteItem (id);
System.out.println ("请求头Referer的值: " + req.getHeader ("Referer"));
// 重定向回原来购物车展示页面
resp.sendRedirect (req.getHeader ("Referer"));
}
}
5.2、购物车/pages/cart/cart.jsp 页面的代码:
<td><a class="deleteItem" href="cartServlet?action=deleteItem&id=${entry.value.id}">删除</a></td>
5.3、删除的确认提示操作:
$(function () {
// 给 【删除】绑定单击事件
$("a.deleteItem").click(function () {
return confirm('你确定删除【'+$(this).parent().parent().find("td:first").text()+'】吗');
});
});
6、清空购物车
6.1、CartServlet 程序:
/**
* 清空购物车
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void clear(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取购物车对象
Cart cart = (Cart) req.getSession ( ).getAttribute ("cart");
if (cart!=null)
{
// 清空购物车
cart.clear ();
//重定向回来
resp.sendRedirect (req.getHeader ("Referer"));
}
}
6.2、修改 pages/cart/cart.jsp 购物车页面:
<span id="clearCart" class="cart_span"><a href="cartServlet?action=clear">清空购物车</a></span>
6.3、清空的确认提示操作:
// 给 【清空】绑定单击事件
$("#clearCart").click(function () {
return confirm("你确定清空购物车吗");
});
7、修改购物车商品数量
7.1、CartServlet 程序:
/**
* 修改商品订单
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void updateCount(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取请求的参数 商品编号 、商品数量
int id = WebUtilts.parseInt (req.getParameter ("id"), 0);
int count = WebUtilts.parseInt (req.getParameter ("count"), 1);
// 获取购物车
Cart cart = (Cart) req.getSession ( ).getAttribute ("cart");
if (cart!=null)
{
// 修改商品数量
cart.updateCount (id,count);
// 重定向回去
resp.sendRedirect (req.getHeader ("Referer"));
}
}
7.2、修改 pages/cart/cart.jsp 购物车页面:
<td>
<input type="text" class="updateCount"
bookId="${entry.value.id}"
value="${entry.value.count}"
style="width: 80px">
</td>
7.3、修改商品数量 js 代码:
// 给输入框绑定 onchange 内容发生改变事件
// 注:通过渲染出来的多个控件,动态绑定用class选择器()
// change-改变,发生改变触发
$(".updateCount").change(function () {
// 获取商品名称
var name = $(this).parent().parent().find("td:first").text();
var id = $(this).attr("bookId");
// 获取商品数量
//var count = $(this).val(); //写法一
var count = this.value;
if (confirm("你确定把【" + name + "】商品修改数量为:" + count + "吗?")) {
//发起请求。给服务器保存修改
location.href = 'cartServlet?action=updateCount&id=' + id + "&count=" + count;
}
else {
// defaultValue 属性是表单项 Dom 对象的属性。它表示默认的 value 属性值。
this.value = this.defaultValue;
}
});
8、首页,购物车数据回显
8.1、在添加商品到购物车的时候,保存最后一个添加的商品名称:
8.2、在 pages/client/index.jsp 页面中输出购物车信息:
阶段八:订单
1、订单模块的分析:
2:订单模块的实现:
2.1、创建订单模块的数据库表:
use book;
create table t_order(
`order_id` varchar(50) primary key,
`create_time` datetime,
`price` decimal(11,2),
`status` int,
`user_id` int,
foreign key(`user_id`) references t_user(`id`)
);
create table t_order_item(
`id` int primary key auto_increment,
`name` varchar(100), `count` int,
`price` decimal(11,2),
`total_price` decimal(11,2),
`order_id` varchar(50),
foreign key(`order_id`) references t_order(`order_id`)
);
2.2、创建订单模块的数据模型:
/**
* 订单
*/
public class Order {
private String orderId;
private Date createTime;
private BigDecimal price;
// 0 未发货,1 已发货,2 表示已签收
private Integer status = 0;
private Integer userId;
}
/**
* 订单项
*/
@Data
public class OrderItem {
private Integer id;
private String name;
private Integer count;
private BigDecimal price;
private BigDecimal totalPrice;//全部的价格
private String orderId;
}
2.3、编写订单模块的 Dao 程序(接口就不整理进来,但是有):
2.3.1、OrderDao:
public class OrderDaoImpl extends BaseDao implements OrderDao {
@Override
public int addOrder(Order order) {
String sql = "insert into t_order(`order_id`,`create_time`,`price`,`status`,`user_id`) values(?,?,?,?,?)";
return update (sql,order.getOrderId (),order.getCreateTime (),order.getPrice (),order.getStatus (),order.getUserId ());
}
}
2.3.2、OrderItemDap:
public class OrderItemDaoImpl extends BaseDao implements OrderItemDao {
@Override
public int addOrderItem(OrderItem orderItem) {
String sql = "insert into t_order_item(`name`,`count`,`price`,`total_price`,`order_id`) values(?,?,?,?,?)";
return update (sql,orderItem.getName (),orderItem.getCount (),orderItem.getPrice (),orderItem.getTotalPrice (),
orderItem.getOrderId ());
}
}
2.4、编写订单模块的 Service (接口就不整理进来,但是有):
2.4.1、OrderService
public class OrderServiceImpl implements OrderService {
private OrderDao orderDao = new OrderDaoImpl ();
private OrderItemDao orderitemDao = new OrderItemDaoImpl ();
@Override
public String createOrder(Cart cart, Integer useId) {
// 订单号===唯一性
String orderId = System.currentTimeMillis ()+""+useId;
// 创建一个订单对象
Order order = new Order (orderId, new Date ( ), cart.getTotalPrice ( ), 0, useId);
// 保存订单
orderDao.addOrder (order);
// 遍历购物车中每一个商品项转换成为订单项保存到数据库
for (Map.Entry<Integer, CartItem> entry: cart.getItems ().entrySet ())
{
// 获取每一个购物车中的商品项
CartItem item = entry.getValue ( );
// 转换为每一个订单项
OrderItem orderItem = new OrderItem (null, item.getName ( ), item.getCount ( ), item.getPrice ( ),
item.getTotalPrice ( ), orderId);
// 保存订单项到数据库
orderitemDao.addOrderItem (orderItem);
}
// 清空购物车
cart.clear ();
return orderId;
}
}
2.5、编写订单模块的 web 层和页面联调:
修改 OrderService 程序:
public class OrderServiceImpl implements OrderService {
private OrderDao orderDao = new OrderDaoImpl ();
private OrderItemDao orderitemDao = new OrderItemDaoImpl ();
private BookDao bookDao = new BookDaoImpl ();
@Override
public String createOrder(Cart cart, Integer useId) {
// 订单号===唯一性
String orderId = System.currentTimeMillis ()+""+useId;
// 创建一个订单对象
Order order = new Order (orderId, new Date ( ), cart.getTotalPrice ( ), 0, useId);
// 保存订单
orderDao.addOrder (order);
// 遍历购物车中每一个商品项转换成为订单项保存到数据库
for (Map.Entry<Integer, CartItem> entry: cart.getItems ().entrySet ())
{
// 获取每一个购物车中的商品项
CartItem item = entry.getValue ( );
// 转换为每一个订单项
OrderItem orderItem = new OrderItem (null, item.getName ( ), item.getCount ( ), item.getPrice ( ),
item.getTotalPrice ( ), orderId);
// 保存订单项到数据库
orderitemDao.addOrderItem (orderItem);
// 更新库存和销量(注:book的出售不大于存货,可以setSales处理)
Book book = bookDao.queryBookById (item.getId ( ));
book.setSales (book.getSales () + item.getCount ());
book.setStock (book.getStock () - item.getCount ());
bookDao.updateBook (book);
}
// 清空购物车
cart.clear ();
return orderId;
}
}
OrderServlet 程序:
public class OrderServlet extends BaseServlet{
private OrderService orderService = new OrderServiceImpl ();
/**
* 生成订单
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void createOrder(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 先获取 Cart 购物车对象
Cart cart = (Cart) req.getSession ( ).getAttribute ("cart");
// 获取 Userid
User loginUser = (User) req.getSession ( ).getAttribute ("user");
if (loginUser==null)
{
req.getRequestDispatcher ("pages/user/login.jsp").forward (req,resp);
return;
}
Integer userId = loginUser.getId ( );
// 调用 orderService.createOrder(Cart,Userid);生成订单
String orderId = orderService.createOrder (cart, userId);
// 保存订单号
req.getSession ().setAttribute ("orderId",orderId);
resp.sendRedirect (req.getContextPath ()+"/pages/cart/checkout.jsp");
}
}
前端:
1、修改 pages/cart/cart.jsp 页面,结账的请求地址:
2、修改 pages/cart/checkout.jsp 页面,输出订单号:
阶段九:过滤器拦截改造
1、使用 Filter 过滤器拦截/pages/manager/所有内容,实 现权限检查:
Filter 代码:
public class ManagerFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
Object user = request.getSession ( ).getAttribute ("user");
if (user == null) {
request.getRequestDispatcher ("/pages/user/login.jsp").forward (servletRequest, servletResponse);
} else {
filterChain.doFilter (servletRequest, servletResponse);
}
}
@Override
public void destroy() {
}
}
web.xml代码:
<filter>
<filter-name>ManagesFilter</filter-name>
<filter-class>com.kk.filter.ManagerFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ManagesFilter</filter-name>
<url-pattern>/pages/manager/*</url-pattern>
<url-pattern>/manager/bookServlet</url-pattern>
</filter-mapping>
2、ThreadLocal 的使用
2.1概要:
作用:
ThreadLocal 的作用,它可以解决多线程的数据安全问题。
ThreadLocal 它可以给当前线程关联一个数据(可以是普通变量,可以是对象,也可以是数组,集合)
特点:
1、ThreadLocal 可以为当前线程关联一个数据。(它可以像 Map 一样存取数据,key 为当前线程)
2、每一个 ThreadLocal 对象,只能为当前线程关联一个数据,如果要为当前线程关联多个数据,就需要使用多个 ThreadLocal 对象实例。
3、每个 ThreadLocal 对象实例定义的时候,一般都是 static 类型
4、ThreadLocal 中保存数据,在线程销毁后。会由 JVM 虚拟自动释放。
2.2、测试类:案例
package com.kk.test;
import java.util.Random;
public class OrderServiceTest {
public void createOrder() {
String name = Thread.currentThread ( ).getName ( );
System.out.println ("Service-----当前线程名为【" + name + "】,保存为的数据为:" + ThreadLocalTest.threadLocal.get ( ));
new OrderDaoTest ( ).saveOrder ( );
}
}
class OrderDaoTest {
public void saveOrder() {
String name = Thread.currentThread ( ).getName ( );
System.out.println ("Dao------当前线程名为:【" + name + "】,中保存的数据:" + ThreadLocalTest.threadLocal.get ( ));
}
}
class ThreadLocalTest {
public static ThreadLocal<Object> threadLocal = new ThreadLocal<> ( );
private static Random random = new Random ( );
public static class Task implements Runnable {
@Override
public void run() {
// 在 Run 方法中,随机生成一个变量(线程要关联的数据),然后以当前线程名为 key 保存到 map 中
Integer i = random.nextInt (1000);
// 获取当前线程名
String name = Thread.currentThread ( ).getName ( );
System.out.println ("web--------当前线程名为:【" + name + "】,生成的随机数:" + i);
threadLocal.set (i);
try {
Thread.sleep (3000);
} catch (InterruptedException e) {
e.printStackTrace ( );
}
new OrderServiceTest ( ).createOrder ( );
// 在 Run 方法结束之前,以当前线程名获取出数据并打印。查看是否可以取出操作
Object o = threadLocal.get ( );
System.out.println ("close-------在线程【" + name + "】快结束时取出关联的数据是:" + o);
}
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
new Thread (new Task ( )).start ( );
}
}
}
}
/*****************************运行测试某次输出(线程每次数次都不同)********************************
web--------当前线程名为:【Thread-1】,生成的随机数:25
web--------当前线程名为:【Thread-0】,生成的随机数:416
web--------当前线程名为:【Thread-2】,生成的随机数:360
Service-----当前线程名为【Thread-0】,保存为的数据为:416
Service-----当前线程名为【Thread-1】,保存为的数据为:25
Service-----当前线程名为【Thread-2】,保存为的数据为:360
Dao------当前线程名为:【Thread-1】,中保存的数据:25
close-------在线程【Thread-1】快结束时取出关联的数据是:25
Dao------当前线程名为:【Thread-2】,中保存的数据:360
Dao------当前线程名为:【Thread-0】,中保存的数据:416
close-------在线程【Thread-0】快结束时取出关联的数据是:416
close-------在线程【Thread-2】快结束时取出关联的数据是:360
3、使用 Filter 和 ThreadLocal 组合管理事务
3.1、使用 ThreadLocal 来确保所有 dao 操作都在同一个 Connection 连接对象中完 成
原理分析图:
javaEE-X-各层封装(反射)&&工具类](https://www.jianshu.com/p/184fa8875dd8)
JdbcUtils 工具类的修改:
BaseDao工具类的修改:
3.2、使用 Filter 过滤器统一给所有的 Service 方法都加上 try-catch。来进行实现的管理
原理分析图:
Filter 类代码:
public class TransactionFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
try {
filterChain.doFilter (servletRequest, servletResponse);
JdbcUtils.commitAndClose ();//提交事务
} catch (IOException |ServletException e) {
JdbcUtils.rollbackAndClose ();//回滚事务
e.printStackTrace ( );
}
}
在 web.xml 中的配置:
<!--设置事务过滤器-->
<filter>
<filter-name>TransactionFilter</filter-name>
<filter-class>com.kk.filter.TransactionFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>TransactionFilter</filter-name>
<!-- /* 表示当前工程下所有请求 -->
<url-pattern>/*</url-pattern>
</filter-mapping>
一定要记得把 BaseServlet 中的异常往外抛给 Filter 过滤器:
在原有基础加了句throw new RuntimeException(e);
public abstract class BaseServlet extends HttpServlet {
//<a>标签请求默认为GET,所以。。
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding ("UTF-8");//处理post乱码
resp.setCharacterEncoding ("utf-8");//在页面显示测试防止乱码
resp.setContentType ("text/html;charset=UTF-8");
String action = req.getParameter ("action");
try {
// 获取action业务鉴别字符串,获取相应的业务 方法反射对象
Method method = this.getClass ( ).getDeclaredMethod (action, HttpServletRequest.class, HttpServletResponse.class);
method.setAccessible (true);
//System.out.println (method);
// 调用目标业务 方法
method.invoke (this, req, resp);
} catch (Exception e) {
e.printStackTrace ( );
throw new RuntimeException (e);
}
}
}
3.3、将所有异常都统一交给 Tomcat,让 Tomcat 展示友好的错误信息页面
web.xml 中配置错误页面:
<!--报错500显示页面-->
<error-page>
<error-code>500</error-code>
<location>/pages/error/error500.jpg</location>
</error-page>
<!--报错404显示页面-->
<error-page>
<error-code>404</error-code>
<location>/pages/error/error404.jpg</location>
</error-page>
阶段十:Ajax改造
1、Ajax 验证用户名是否可用。
需求分析:
使用 Ajax 验证用户名是否可用。我们需要在页面端,给用户名输入框添加一个失去焦点事件。当用户名输入框失去 焦点的时候,触发事件。获取输入的用户名。然后发送 Ajax 请求到服务器諯去验证。 然后服务器通过 json 数据,返回是否存在,result 为 0 表示 不存在,result 为 1 表示存在。当然我们还要做一个用 户名不为空的简单验证。才能让请求发送到服务器端。
1.1、修改 pages/user/regist.jsp 页面。给用户名输入框添加失去焦点事件
//用户名是否存在的验证
$("#username").blur(function () {
// 获取用户名
var usernameValue = this.value;
// 判断用户名不能为空
if (usernameValue == "") {
$("#errorSpan").html("用户名不能为空");
return;
}
// 发送 ajax 请求验证
$.getJSON("userServlet?action=existsUsername&username=" + usernameValue, function (data) {
// result 等于 0,说明用户名不存在
if (data == 0) {
$("#errorSpan").html("用户名可用");
} else if (data == 1) {
$("#errorSpan").html("用户名已经存在");
}
});
});
1.2、修改 UserServlet 类,添加检查用户名是否存在的方法:
/**
* ajax校验用户名
*
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void existsUsername(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter ("username");
if (userService.existsUsername (username)) {
//说明用户名存在,返回 1
resp.getWriter ().write ("1");
} else {
//用户名不存在,返回 0
resp.getWriter ().write ("0");
}
}
理想写法:
2、Ajax 修改购物车模块---添加商品---修改数量
需求:
以 Ajax 请求的方式修改购物车的模块。我们修改的功能有,添加到购物车,修改数量,以及删除商品,和清空购物 车。我们以添加购物车和修改商品数量为例
2.1、添加商品:
添加商品到购物车。首先我们要把商品的编号,以 Ajax 的方式传到服务器。然后器添加成功后把购物车的数量,最 后一本书的名字返回,给用户显示。
2.1.1、修改 pages/client/index.jsp 页面,添加购物车的 a 标签代码:
<div class="book_add">
<!--<button bookId="${book.id}" class="addToCart" onclick="addBookCart(this)">加入购物车</button>-->
<a idv="${book.id}" class="addToCart" onclick="return onajax(this)">加入购物车</a>
</div>
2.1.2、添加 Ajax 请求的 js 代码:
<script src="static/script/jquery-1.7.2.js"></script>
<script>
// 添加购物车 Ajax 请求
function onajax(da) {
// 通过添加 idv 属性保存商品 id 信息
var idv = $(da).attr("idv");
$.getJSON("cartServlet?action=ajaxAddItem&id=" + idv, function (data) {
if (data.result == 0) {
$("#cart_totalCount").html("您购物车有<span style='color: salmon;'>" + data.totalCount + "</span>件商品");
$("#last_product").html("您刚刚将<span style='color:indianred;'>" + data.lastProduct + "</span>添加到购物车");
}
});
return false;
};
</script>
2.1.3、修改添加购物车后。搜索下方的购物车显示:
注:为什么购物车空还要绑定id?,很简单,为了第一次进来是没有数据的,ajax只能在没有数据的框上面进行渲染,而不是在有数据的框内。
<div style="text-align: center">
<k:choose>
<%--购物车为空的输出--%>
<k:when test="${empty cart.items}">
<span id="cart_totalCount">您的购物车为空</span>
<div id="last_product"> </div>
</k:when>
<%--购物车不为空的输出--%>
<k:otherwise>
<span id="cart_totalCount">您的购物车中有<span style="color: salmon">${ cart.totalCount }</span>件商品</span>
<div id="last_product">您刚刚将<span style="color: indianred">${ sessionScope.lastProduct }</span>加入到了购物车中
</div>
</k:otherwise>
</k:choose>
</div>
2.1.4、 CartServlet 中添加 Ajax 版的添加购物车代码:
/**
* Ajax 版--添加到购物车
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void ajaxAddItem(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取购物车
Cart cart = (Cart) req.getSession ( ).getAttribute ("cart");
if (cart == null) {
// 生成一个新的购物车,放到 Session 对象中
cart = new Cart ();
req.getSession ().setAttribute ("cart",cart);
}
// 获取图书 的 id
Integer id = WebUtilts.getParameter ("id", Integer.class, req);
// 查找图书
Book book = bookService.queryBookById (id);
// 图书转换商品
CartItem cartItem = new CartItem (book.getId ( ), book.getName ( ), 1, book.getPrice ( ), book.getPrice ( ));
// 添加到购物车中
cart.addItem (cartItem);
// 添加最后一个商品名到 Session 对象中
req.getSession ().setAttribute ("lastProduct",cartItem.getName ());
// 打印测试
System.out.println(cart);
// 创建一个 map 用于返回结果
Map<String, Object> result = new HashMap<> ( );
result.put ("result",0);
result.put ("totalCount",cart.getTotalCount ());
result.put ("lastProduct",cartItem.getName ());
Gson gson = new Gson ( );
resp.getWriter ().write (gson.toJson (result));
}
2.2、修改数量
分析:
修改购物车数量,我们要把修改的商品编号和数量发送到服务器。然后服务器把修改后商品的总价 item_totalMoney, 购物车的总数量 cart_totalCount,以及购物车的总金额 cart_totalMoney 返回用于前端的修改。
2.2.1、修改原来购物车更新数量的方法(Cart),返回修改后商品的总金额:
/**
* 修改购物车中的商品数量(更新前)
*
* @param id
* @param count
*/
public void updateCount(Integer id, Integer count) {
// 先查看购物车中是否有此商品。如果有,修改商品数量,更新总金额
CartItem item = items.get (id);
if (item != null) {
item.setCount (count);// 修改商品数量
item.setTotalPrice (item.getPrice ( ).multiply (new BigDecimal (item.getCount ( ))));// 更新总金额
}
}
--------------------------------------------------------------------------------------------------------------------------------------------
/**
* ajax版:修改购物车中的商品数量(更新后)
* @param id
* @param count
* @return
*/
public double updateItem(Integer id,Integer count)
{
CartItem item = items.get (id);
if (item !=null)
{
item.setCount (count);// 修改商品数量
item.setTotalPrice (item.getPrice ( ).multiply (new BigDecimal (item.getCount ( ))));// 更新总金额
return item.getTotalPrice ().intValue ();//返回最终价格, intValue () 转 int
}
return 0;
}
2.2.2、升级Servlet代码:
/**
* 修改商品订单(旧:页面跳转)
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void updateCount(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取请求的参数 商品编号 、商品数量
int id = WebUtilts.parseInt (req.getParameter ("id"), 0);
int count = WebUtilts.parseInt (req.getParameter ("count"), 1);
// 获取购物车
Cart cart = (Cart) req.getSession ( ).getAttribute ("cart");
if (cart!=null)
{
// 修改商品数量
cart.updateCount (id,count);
// 重定向回去
resp.sendRedirect (req.getHeader ("Referer"));
}
}
--------------------------------------------------------------------------------------------------------------------------------------------
/**
* 修改商品订单(新:ajax版)
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void updateItemCount(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取请求的参数 商品编号 、商品数量
int id = WebUtilts.parseInt (req.getParameter ("id"), 0);
int count = WebUtilts.parseInt (req.getParameter ("count"), 1);
// 获取购物车
Cart cart = (Cart) req.getSession ( ).getAttribute ("cart");
if (cart != null) {
// 修改商品数量
double item_totalmonery = cart.updateItem (id, count);
// 创建一个 Map 返回要显示的内容
Map<String, Object> result = new HashMap<> ( );
result.put ("item_totalmonery",item_totalmonery);
result.put ("cart_titalmonery",cart.getTotalPrice ());
result.put ("cart_titalcount",cart.getTotalCount ());
//用ajax后不需要页面跳转
Gson gson = new Gson ( );
resp.getWriter ( ).write (gson.toJson (result));
}
}
2.2.3、修改 pages/cart/cart.jsp 页面中的内容:
改动点1:
改动点2:
项目需求补充:
1、主页购物车点击后,应该开启一个新页面:
1.1、需要改动页面:
1.2、变动的代码: