- 1. Servlet 优化
- 2 书城项目改造
- 3 图书业务实现
- 4. 会话技术
- 5. 书城项目优化4
- 6 Ajax
- 7 书城项目第五阶段
- 8 购物车功能实现
- 9 过滤器
- 10 监听器
- 11 WEB组件注解用法
- 12 订单模块实现
- 13 事务控制
1. Servlet 优化
1.1 Servlet优化说明
说明: 每个业务逻辑都需要一个Servlet,如果业务逻辑众多,则需要更多的Servlet代码, 并且每个Servlet代码的结构几乎类似,都包含doGet/doPost的方式. 那么该代码可以优化吗?
1.2 Servlet优化方式一
1.2.1 优化策略说明
-
- 指定统一的Servlet
-
- 用户传递参数时
<a href="xxxServlet?method="业务名称"&key=value></a>
- 用户传递参数时
-
- 通过Servlet获取业务名称,完成业务调用
1.2.2 编辑index.html页面
说明: 修改页面将参数提交进行修改 改为 userServlet?method="xxxx"
的形式
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<base th:href="@{/}" />
<meta charset="UTF-8">
<title>模块练习页面</title>
</head>
<body>
<h1>模块练习页面</h1>
<a href="userServlet?method=findUserList"><button>点击展现用户信息</button></a>
<hr>
<!--编辑表格数据-->
<table width="800px" border="1px" cellpadding="0" cellspacing="0" align="center">
<tr>
<th>序号</th>
<th>ID号</th>
<th>用户名</th>
<th>邮箱</th>
<th>操作</th>
</tr>
<tr th:if="${#lists.isEmpty(userList)}" align="center">
<td colspan="5"><b>暂时没有用户信息请添加</b></td>
</tr>
<tr th:unless="${#lists.isEmpty(userList)}" align="center" th:each="user,status : ${userList}">
<td th:text="${status.count}"></td>
<td th:text="${user.id}"></td>
<td th:text="${user.username}"></td>
<td th:text="${user.email}"></td>
<td>
<a th:href="@{/userServlet(method=toUpdateUser,id=${user.id})}"><button>编辑</button></a>
<a th:href="@{/userServlet(method=deleteUserById,id=${user.id})}"><button>删除</button></a>
</td>
</tr>
</table>
<hr>
<form action="userServlet?method=addUser" method="post">
<table align="center">
<tr align="center">
<td colspan="2">
<h3>新增用户</h3>
</td>
</tr>
<tr>
<td>用户名:</td>
<td><input type="text" name="username"/></td>
</tr>
<tr>
<td>密码:</td>
<td><input type="password" name="password"/></td>
</tr>
<tr>
<td>邮箱:</td>
<td><input type="text" name="email"/></td>
</tr>
<tr>
<td th:cols="2">
<button type="submit">提交</button>
</td>
</tr>
</table>
</form>
<form action="userServlet?method=updateUser" method="post">
<table align="center" th:if="${updateUser != null}">
<tr align="center">
<td colspan="2">
<h3>修改用户</h3>
</td>
</tr>
<tr hidden>
<td>ID号:</td>
<td><input type="text" name="id" th:value="${updateUser.id}" /></td>
</tr>
<tr>
<td>用户名:</td>
<td><input type="text" name="username" th:value="${updateUser.username}" /></td>
</tr>
<tr>
<td>密码:</td>
<td><input type="password" name="password" placeholder="密码不显示,有值默认修改"/></td>
</tr>
<tr>
<td>邮箱:</td>
<td><input type="text" name="email" th:value="${updateUser.email}"/></td>
</tr>
<tr>
<td th:cols="2">
<button type="submit">提交</button>
</td>
</tr>
</table>
</form>
</body>
</html>
1.2.3 编辑UserServlet
package com.atguigu.servlet;
import com.atguigu.bean.User;
import com.atguigu.service.UserService;
import com.atguigu.service.impl.UserServiceImpl;
import org.apache.commons.beanutils.BeanUtils;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Map;
public class UserServlet extends ViewBaseServlet {
private UserService userService = new UserServiceImpl();
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request,response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.获取指定参数的业务名称
request.setCharacterEncoding("utf-8");
String methodName = request.getParameter("method");
if("findUserList".equals(methodName)){
findUserList(request,response);
}else if ("addUser".equals(methodName)){
addUser(request,response);
}else if ("toUpdateUser".equals(methodName)){
toUpdateUser(request,response);
}else if("updateUser".equals(methodName)){
updateUser(request,response);
}else if("deleteUserById".equals(methodName)){
deleteUserById(request,response);
}
}
//封装查询用户方法
protected void findUserList(HttpServletRequest request, HttpServletResponse response) throws IOException {
//查询所有的数据库信息
List<User> userList = userService.findUserList();
//System.out.println(userList);
request.setAttribute("userList",userList);
this.processTemplate("index",request,response);
}
protected void addUser(HttpServletRequest request, HttpServletResponse response) throws IOException {
//获取用户提交的数据信息
//解决中文乱码问题
request.setCharacterEncoding("utf-8");
Map<String, String[]> parameterMap = request.getParameterMap();
//将数据转化为对象
User user = new User();
try {
BeanUtils.populate(user,parameterMap);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
//实现数据入库操作
int rows = userService.addUser(user);
if(rows>0){ //表示用户新增成功 则展现列表数据
response.sendRedirect(request.getContextPath()+"/userServlet?method=findUserList");
}else{ //否则跳转到新增页面
response.sendRedirect(request.getContextPath()+"/userServlet?method=toAddUser");
}
}
protected void toUpdateUser(HttpServletRequest request, HttpServletResponse response) throws IOException {
//根据ID查询用户信息
int id = Integer.valueOf(request.getParameter("id"));
User user = userService.findUserById(id);
request.setAttribute("updateUser",user);
this.processTemplate("index",request,response);
}
protected void updateUser(HttpServletRequest request, HttpServletResponse response) throws IOException {
Map<String, String[]> parameterMap = request.getParameterMap();
User user = new User();
try {
BeanUtils.populate(user,parameterMap);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
int rows = userService.updateUser(user);
System.out.println(user);
//不管成功还是失败都应该跳转到展现页面
response.sendRedirect(request.getContextPath()+"/userServlet?method=findUserList");
}
protected void deleteUserById(HttpServletRequest request, HttpServletResponse response) throws IOException {
//当删除成功之后,重定向到用户列表页面
int id = Integer.valueOf(request.getParameter("id"));
userService.deleteUserById(id);
response.sendRedirect(request.getContextPath()+"/userServlet?method=findUserList");
}
}
1.3 Servlet优化方式-反射机制
1.3.1 业务说明
虽然上述的代码可以在一定程度上解决耦合性的问题. 都是代码中频繁出现判断 导致代码耦合,并且该方法只适用一个Servlet.如果有多个Servlet的都需要重复编辑.
- 问是否可以优化?
- 答: 可以通过反射机制让程序自动调用方法.
1.3.2 反射机制知识回顾
//1.获取类型
Class targetClass = xxxxxx.getClass();
//2.获取方法对象
Method method1 = targetClass.getDeclaredMethod("方法名称",参数类型...)
//获取公共方法
Method method2 = targetClass.getMethod("方法名称",参数类型...)
//3.让方法执行
method2 .invoke("哪个对象调用",调用时传递的参数列表....)
123456789
1.3.3 反射机制1.0版本
package com.atguigu.servlet;
import com.atguigu.bean.User;
import com.atguigu.service.UserService;
import com.atguigu.service.impl.UserServiceImpl;
import org.apache.commons.beanutils.BeanUtils;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
public class UserServlet extends ViewBaseServlet {
private UserService userService = new UserServiceImpl();
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request,response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
//1.获取用户传递的方法对象
String methodName = request.getParameter("method");
//2.获取当前类的方法对象
Class targetClass = this.getClass();
try {
Method method = targetClass.getDeclaredMethod(methodName,HttpServletRequest.class,HttpServletResponse.class);
//3.让方法执行
method.setAccessible(true); //设置暴力反射 表示都可以访问
method.invoke(this,request,response);
} catch (Exception e) {
e.printStackTrace();
}
}
/* @Override 优化方式1
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.获取指定参数的业务名称
request.setCharacterEncoding("utf-8");
String methodName = request.getParameter("method");
if("findUserList".equals(methodName)){
findUserList(request,response);
}else if ("addUser".equals(methodName)){
addUser(request,response);
}else if ("toUpdateUser".equals(methodName)){
toUpdateUser(request,response);
}else if("updateUser".equals(methodName)){
updateUser(request,response);
}else if("deleteUserById".equals(methodName)){
deleteUserById(request,response);
}
}*/
//封装查询用户方法
protected void findUserList(HttpServletRequest request, HttpServletResponse response) throws IOException {
//查询所有的数据库信息
List<User> userList = userService.findUserList();
//System.out.println(userList);
request.setAttribute("userList",userList);
this.processTemplate("index",request,response);
}
protected void addUser(HttpServletRequest request, HttpServletResponse response) throws IOException {
//获取用户提交的数据信息
//解决中文乱码问题
request.setCharacterEncoding("utf-8");
Map<String, String[]> parameterMap = request.getParameterMap();
//将数据转化为对象
User user = new User();
try {
BeanUtils.populate(user,parameterMap);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
//实现数据入库操作
int rows = userService.addUser(user);
if(rows>0){ //表示用户新增成功 则展现列表数据
response.sendRedirect(request.getContextPath()+"/userServlet?method=findUserList");
}else{ //否则跳转到新增页面
response.sendRedirect(request.getContextPath()+"/userServlet?method=toAddUser");
}
}
protected void toUpdateUser(HttpServletRequest request, HttpServletResponse response) throws IOException {
//根据ID查询用户信息
int id = Integer.valueOf(request.getParameter("id"));
User user = userService.findUserById(id);
request.setAttribute("updateUser",user);
this.processTemplate("index",request,response);
}
protected void updateUser(HttpServletRequest request, HttpServletResponse response) throws IOException {
Map<String, String[]> parameterMap = request.getParameterMap();
User user = new User();
try {
BeanUtils.populate(user,parameterMap);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
int rows = userService.updateUser(user);
System.out.println(user);
//不管成功还是失败都应该跳转到展现页面
response.sendRedirect(request.getContextPath()+"/userServlet?method=findUserList");
}
protected void deleteUserById(HttpServletRequest request, HttpServletResponse response) throws IOException {
//当删除成功之后,重定向到用户列表页面
int id = Integer.valueOf(request.getParameter("id"));
userService.deleteUserById(id);
response.sendRedirect(request.getContextPath()+"/userServlet?method=findUserList");
}
}
1.3.4 反射机制2.0版本
1.3.4.1 编辑BaseServlet
package com.atguigu.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
public class BaseServlet extends ViewBaseServlet{
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request,response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
//1.获取用户传递的方法对象
String methodName = request.getParameter("method");
//2.获取当前类的方法对象
Class targetClass = this.getClass();
try {
Method method = targetClass.getDeclaredMethod(methodName,HttpServletRequest.class,HttpServletResponse.class);
//3.让方法执行
method.setAccessible(true); //设置暴力反射 表示都可以访问
method.invoke(this,request,response);
} catch (Exception e) {
e.printStackTrace();
}
}
}
1.3.4.2 编辑业务Servlet
说明: 以后UserServlet只需要继承BaseServlet即可, 用户提交的参数与方法名称对应即可.
1.3.4.3 关于中文乱码问题说明
说明: 如果新增中文出现乱码问题,则检查字符集是否设定
- 1 post请求乱码问题
- 2 数据库连接乱码问题
#key=value
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/atweb?serverTimezone=UTC&rewriteBatchedStatements=true&characterEncoding=utf-8
username=root
password=root
initialSize=20
maxActive=50
maxWait=1000
2 书城项目改造
2.1 项目导入
说明: 之前学习了JDBC/Servlet/Thymeleaf 现在要将所有学习过的知识全部整合,所以导入项目
2.1.1 代码结构说明
该代码都是之前项目中的代码.
2.1.2 配置tomcat服务器
2.2 默认首页跳转
2.2.1 编辑IndexServlet
说明: 由于IndexServlet默认跳转首页,所以只需要继承ViewBaseServlet
public class IndexServlet extends ViewBaseServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request,response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.processTemplate("index",request,response);
}
}
2.2.2 编辑web.xml
说明: 配置thymeleaf中的默认跳转配置
<!-- 在上下文参数中配置视图前缀和视图后缀 -->
<context-param>
<param-name>view-prefix</param-name>
<param-value>/WEB-INF/pages/</param-value>
</context-param>
<context-param>
<param-name>view-suffix</param-name>
<param-value>.html</param-value>
</context-param>
<!--1.默认indexServlet-->
<servlet>
<servlet-name>IndexServlet</servlet-name>
<servlet-class>com.atguigu.servlet.IndexServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>IndexServlet</servlet-name>
<url-pattern>/index.html</url-pattern>
</servlet-mapping>
2.2.3 编辑index.html
说明: 根据thymeleaf中的默认跳转规则 做出如下修改
修改登录和注册的请求路径:
<a href="user?method=toLogin" class="login">登录</a>
<a href="user?method=toRegist" class="register">注册</a>
2.3 用户登录实现
2.3.1 业务逻辑说明
2.3.2 编辑登录页面
注意事项:
- 添加thymeleaf路径:
- 添加post请求路径:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>尚硅谷会员登录页面</title>
<base th:href="@{/}">
<link type="text/css" rel="stylesheet" href="static/css/style.css" />
<!--1. 导入vue.js文件-->
<script src="static/script/vue.js"></script>
</head>
<body>
<div id="app">
<div id="login_header">
<a href="index.html">
<img class="logo_img" alt="" src="static/img/logo.gif" />
</a>
</div>
<div class="login_banner">
<div id="l_content">
<span class="login_word">欢迎登录</span>
</div>
<div id="content">
<div class="login_form">
<div class="login_box">
<div class="tit">
<h1>尚硅谷会员</h1>
</div>
<div class="msg_cont">
<b></b>
<span class="errorMsg" v-text="msg"></span>
</div>
<div class="form">
<!--<form action="login_success.html" >-->
<!--<form action="loginServlet" method="post">-->
<form action="user?method=login" method="post">
<label>用户名称:</label>
<input
class="itxt"
type="text"
placeholder="请输入用户名"
autocomplete="off"
tabindex="1"
name="username"
id="username"
v-model="username"
/>
<br />
<br />
<label>用户密码:</label>
<input
class="itxt"
type="password"
placeholder="请输入密码"
autocomplete="off"
tabindex="1"
name="password"
id="password"
v-model="password"
/>
<br />
<br />
<input type="submit" value="登录" id="sub_btn" @click="login"/>
</form>
<div class="tit">
<a href="user?method=toRegist">立即注册</a>
</div>
</div>
</div>
</div>
</div>
</div>
<div id="bottom">
<span>
尚硅谷书城.Copyright ©2015
</span>
</div>
</div>
<script>
const app = new Vue({
el: "#app",
data: {
username: '[[${loginUsername}]]',
password: '',
msg: '[[${loginMsg==null?"请输入用户名和密码":loginMsg}]]'
},
methods: {
login(){
if(this.username === ''){
this.msg = "请输入用户名"
//阻止事件提交
event.preventDefault()
}
if(this.password === ''){
this.msg = "请输入密码"
//阻止事件提交
event.preventDefault()
}
}
}
})
</script>
</body>
</html>
2.3.3 编辑登录Servlet
public class UserServlet extends BaseServlet {
private UserService userService = new UserServiceImpl();
//1.跳转到用户登陆页面
protected void toLogin(HttpServletRequest request, HttpServletResponse response) throws IOException {
//跳转到user/login.html的页面下
this.processTemplate("user/login",request,response);
}
//2.跳转到注册页面
protected void toRegist(HttpServletRequest request, HttpServletResponse response) throws IOException {
//跳转到user/login.html的页面下
this.processTemplate("user/regist",request,response);
}
//3.完成用户登录操作
protected void login(HttpServletRequest request, HttpServletResponse response) throws IOException {
//用户用户提交的数据信息
Map<String, String[]> parameterMap = request.getParameterMap();
User user = new User();
try {
BeanUtils.populate(user,parameterMap);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
//2.实现用户登录操作
User userDB = userService.findUserByUP(user);
//3.判断查询数据有结果 如果有数据则重定向到首页 否则转发到登录页面
if(userDB == null){
request.setAttribute("loginUsername",user.getUsername());
request.setAttribute("loginMsg","用户名或密码错误");
this.processTemplate("user/login",request,response);
}else{
request.setAttribute("user",userDB);
this.processTemplate("user/login_success",request,response);
}
}
}
2.3.4 编辑UserService接口和实现类
- 编辑UserService接口
public interface UserService {
User findUserByUP(User user);
}
- 编辑UserServiceImpl实现类
package com.atguigu.service.impl;
import com.atguigu.bean.User;
import com.atguigu.dao.UserDao;
import com.atguigu.dao.impl.UserDaoImpl;
import com.atguigu.service.UserService;
import com.atguigu.util.MD5Util;
import com.mysql.cj.util.StringUtils;
import java.util.List;
public class UserServiceImpl implements UserService {
private UserDao userDao = new UserDaoImpl();
@Override
public User findUserByUP(User user) {
//将密码加密
user.setPassword(MD5Util.encode(user.getPassword()));
User userDB = userDao.findUserByUP(user);
return userDB==null?null:userDB;
}
}
2.3.5 编辑UserDao接口/实现类
- 编辑UserDao接口
public interface UserDao {
User findUserByUP(User user);
}
- 编辑UserDaoImpl实现类
public class UserDaoImpl extends BaseDaoImpl implements UserDao {
@Override
public User findUserByUP(User user) {
String sql = "select * from user where username=? and password=?";
return this.getBean(User.class,sql,user.getUsername(),user.getPassword());
}
}
2.4 用户注册实现
2.4.1 编辑注册页面
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<base th:href="@{/}">
<title>尚硅谷会员注册页面</title>
<link type="text/css" rel="stylesheet" href="static/css/style.css" />
<link rel="stylesheet" href="static/css/register.css" />
<script src="static/script/vue.js"></script>
<style type="text/css">
.login_form {
height: 420px;
margin-top: 25px;
}
</style>
</head>
<body>
<div id="app">
<div id="login_header">
<a href="index.html">
<img class="logo_img" alt="" src="static/img/logo.gif" />
</a>
</div>
<div class="login_banner">
<div class="register_form">
<h1>注册尚硅谷会员</h1>
<!--<form action="regist_success.html" method="post">-->
<form action="user?method=regist" method="post">
<div class="form-item">
<div>
<label>用户名称:</label>
<input type="text" placeholder="请输入用户名" name="username" v-model="user.usermame" @blur="checkUsername"/>
</div>
<span class="errMess" v-text="msg.usernameErrorMsg"></span>
</div>
<div class="form-item">
<div>
<label>用户密码:</label>
<input type="password" placeholder="请输入密码" name="password" v-model="user.password" @blur="checkPassword"/>
</div>
<span class="errMess" v-text="msg.passwordErrorMsg"></span>
</div>
<div class="form-item">
<div>
<label>确认密码:</label>
<input type="password" placeholder="请输入确认密码" v-model="user.password2" @blur="checkPassword2"/>
</div>
<span class="errMess"v-text="msg.passwordErrorMsg2"></span>
</div>
<div class="form-item">
<div>
<label>用户邮箱:</label>
<input type="text" placeholder="请输入邮箱" name="email" v-model="user.email" @blur="checkEmail"/>
</div>
<span class="errMess" v-text="msg.emailErrorMsg"></span>
</div>
<div class="form-item">
<div>
<label>验证码:</label>
<div class="verify">
<input type="text" placeholder="" />
<img src="static/img/code.bmp" alt="" height="50px"/>
</div>
</div>
<span class="errMess" v-text="msg.codeMsg"></span>
</div>
<button class="btn" @click="regist">注册</button>
</form>
</div>
</div>
<div id="bottom">
<span>
尚硅谷书城.Copyright ©2015
</span>
</div>
</div>
<!-- 定义vue对象 -->
<script type="text/javascript">
const app = new Vue({
el: "#app",
data: {
msg: {
usernameErrorMsg: "用户名应为6~16位数字和字母组成",
passwordErrorMsg: "密码的长度至少为8位",
passwordErrorMsg2: "密码两次输入不一致",
emailErrorMsg: "请输入正确的邮箱格式",
codeMsg: "请输入正确的验证码"
},
user: {
usermame: '',
password: '',
password2: '',
email: ''
},
//定义全局表单提交标识符
flagArray : [0,0,0,0]
},
methods: {
checkUsername(){
//1.定义正则表达式语法
let usernameRege = /^[a-zA-Z0-9]{6,16}$/
let usernameFlag = usernameRege.test(this.user.usermame)
if(usernameFlag){
this.msg.usernameErrorMsg = "√"
this.flagArray[0] = 1
}else{
this.msg.usernameErrorMsg = "用户名应为6~16位数字和字母组成"
this.flagArray[0] = 0
}
},
checkPassword(){
let passwordRege = /^.{8,}$/
let passwordFlag = passwordRege.test(this.user.password)
if(passwordFlag){
this.msg.passwordErrorMsg = "√"
this.flagArray[1] = 1
}else{
this.msg.passwordErrorMsg = "密码的长度至少为8位"
this.flagArray[1] = 0
}
},
checkPassword2(){
if(this.user.password === this.user.password2){
this.msg.passwordErrorMsg2 = "√"
this.flagArray[2] = 1
}else{
this.msg.passwordErrorMsg2 = "密码两次输入不一致"
this.flagArray[2] = 0
}
},
checkEmail(){
let emailRege = /^[a-zA-Z0-9_.-]+@([a-zA-Z0-9-]+[.]{1})+[a-zA-Z]+$/
if(emailRege.test(this.user.email)){
this.msg.emailErrorMsg = '√'
this.flagArray[3] = 1
}else{
this.msg.emailErrorMsg = "请输入正确的邮箱格式"
this.flagArray[3] = 0
}
},
regist(){
//再次校验密码是否相同即可.
this.checkPassword2()
let flagStr = this.flagArray.join(",")
if(flagStr !== "1,1,1,1"){
//应该阻止表单提交
event.preventDefault()
alert("表单校验不通过!!!")
}else{
alert("表单校验成功")
}
}
}
})
</script>
</body>
</html>
2.4.2 编辑UserServlet
/**
* 注册: 用户注册说明
* URL地址: user?method=regist
* 参数: form表单提交
*/
protected void regist(HttpServletRequest request, HttpServletResponse response) throws IOException {
Map<String, String[]> parameterMap = request.getParameterMap();
User user = new User();
try {
BeanUtils.populate(user,parameterMap);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
//完成用户注册操作,注册成功返回布尔类型数据 true表示成功, false表示失败
boolean flag = userService.addUser(user);
//如果注册成功,则转发到注册成功页面,否则转发到注册页面
if(flag){
request.setAttribute("regist_username", user.getUsername());
this.processTemplate("user/regist_success",request, response);
}else{
this.processTemplate("user/regist",request, response);
}
}
2.4.3 编辑UserService
- 编辑UserService接口
boolean addUser(User user);
1
- 编辑UserServiceImpl实现类
@Override
public boolean addUser(User user) {
//1.密码加密
String md5Pass = MD5Util.encode(user.getPassword());
user.setPassword(md5Pass);
return userDao.addUser(user);
}
1234567
2.4.3 编辑UserDao
- 编辑UserDao接口
boolean addUser(User user);
1
- 编辑UserDaoImpl实现类
@Override
public boolean addUser(User user) {
String sql = "insert into user(id,username,password,email) values (null,?,?,?)";
int rows = this.update(sql,user.getUsername(),user.getPassword(),user.getEmail());
return rows>0;
}
2.4.4 编辑注册成功页面
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>尚硅谷会员注册页面</title>
<base th:href="@{/}">
<link type="text/css" rel="stylesheet" href="static/css/style.css" >
<style type="text/css">
h1 {
text-align: center;
margin-top: 200px;
}
h1 a {
color:red;
}
</style>
</head>
<body>
<div id="header">
<a href="index.html">
<img class="logo_img" alt="" src="static/img/logo.gif" />
</a>
<span class="wel_word"></span>
<div>
<span>欢迎<span class="um_span" th:text="${regist_username}">张总</span>光临尚硅谷书城</span>
<a href="../order/order.html">我的订单</a>
<a href="index.html">注销</a>
<a href="index.html">返回</a>
</div>
</div>
<div id="main">
<h1>注册成功! <a href="index.html">转到主页</a></h1>
</div>
<div id="bottom">
<span>
尚硅谷书城.Copyright ©2015
</span>
</div>
</body>
</html>
3 图书业务实现
3.1 公共标签封装
3.1.1 业务说明
说明: 后端图书管理模块中 每个页面都有相同的代码 所以可以优化.
优化策略: 将共同的标签放到统一的页面中. 之后其它页面包含该标签即可.
3.1.2 定义base.html页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<base th:href="@{/}">
<title></title>
</head>
<body>
<div class="header-right" th:fragment="head">
<a href="./book_manager.html" class="order">图书管理</a>
<a href="./order_manager.html" class="destory">订单管理</a>
<a href="index.html" class="gohome">返回商城</a>
</div>
</body>
</html>
123456789101112131415
3.1.3 实现标签共享
1.页面结构分析
\2. 添加thymeleaf标记
\3. 编辑页面html
3.2 业务模块准备
3.2.1 导入books表
说明: 根据课前资料中的信息导入数据,其中包含了books表中的所有数据 ,通过sqlyog工具导入即可.
如图所示:
3.2.2 编辑Book类
package com.atguigu.bean;
import java.io.Serializable;
public class Book implements Serializable {
private Integer id;
private String title; //查询数据时注意_线问题
private String author;
private Double price;
private Integer sales;
private Integer stock;
private String imgPath;
public Book(){
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
public Integer getSales() {
return sales;
}
public void setSales(Integer sales) {
this.sales = sales;
}
public Integer getStock() {
return stock;
}
public void setStock(Integer stock) {
this.stock = stock;
}
public String getImgPath() {
return imgPath;
}
public void setImgPath(String imgPath) {
this.imgPath = imgPath;
}
@Override
public String toString() {
return "Book{" +
"id=" + id +
", title='" + title + '\'' +
", author='" + author + '\'' +
", price=" + price +
", sales=" + sales +
", stock=" + stock +
", imgPath='" + imgPath + '\'' +
'}';
}
}
3.2.3 创建层级代码
说明: 为了后期业务逻辑实现,准备层级代码.
3.3 图书列表实现
3.3.1 编辑Index.html实现业务跳转
说明: 编辑后台管理URL地址 添加如下请求连接
<a href="bookServlet?method=findBookList" class="admin">后台管理</a>
1
3.3.2 编辑BookServlet实现列表展现
public class BookServlet extends BaseServlet {
private BookService bookService = new BookServiceImpl();
/**
* 业务说明: 查询book表全部数据
* 参数: 无
* 返回数据: 将booklist返回给页面
* 跳转页面: manager/book_manager.html
* @param request
* @param response
* @throws ServletException
* @throws IOException
*/
protected void findBookList(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
List<Book> bookList = bookService.findBookList();
request.setAttribute("bookList",bookList);
this.processTemplate("manager/book_manager",request,response);
}
}
3.3.3 编辑web.xml
说明: 实现Servlet映射关系
<servlet>
<servlet-name>BookServlet</servlet-name>
<servlet-class>com.atguigu.servlet.BookServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>BookServlet</servlet-name>
<url-pattern>/bookServlet</url-pattern>
</servlet-mapping>
3.3.4 编辑BookService接口和实现类
- 编辑BookService接口
public interface BookService {
List<Book> findBookList();
}
- 编辑BookServiceImpl实现类
package com.atguigu.service.impl;
import com.atguigu.bean.Book;
import com.atguigu.dao.BookDao;
import com.atguigu.dao.impl.BookDaoImpl;
import com.atguigu.service.BookService;
import java.util.List;
public class BookServiceImpl implements BookService {
private BookDao bookDao = new BookDaoImpl();
@Override
public List<Book> findBookList() {
return bookDao.findBookList();
}
}
3.3.5 编辑BookDao接口与实现类
- 编辑BookDao接口
public interface BookDao {
List<Book> findBookList();
}
123
- 编辑BookDaoImpl实现类
package com.atguigu.dao.impl;
import com.atguigu.bean.Book;
import com.atguigu.bean.User;
import com.atguigu.dao.BookDao;
import java.util.List;
public class BookDaoImpl extends BaseDaoImpl implements BookDao {
private static final String COLUMN = " id,title, author ,price, sales , stock , img_path imgPath ";
@Override
public List<Book> findBookList() {
String sql = "SELECT "+ COLUMN+" FROM books";
return this.getList(Book.class,sql);
}
}
3.3.6 编辑book_manager.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base th:href="@{/}">
<title>Document</title>
<link rel="stylesheet" href="static/css/minireset.css" />
<link rel="stylesheet" href="static/css/common.css" />
<link rel="stylesheet" href="static/css/cart.css" />
<link rel="stylesheet" href="static/css/bookManger.css" />
</head>
<body>
<div class="header">
<div class="w">
<div class="header-left">
<a href="index.html">
<img src="static/img/logo.gif" alt=""
/></a>
<h1>图书管理系统</h1>
</div>
<div class="header-right" th:include="manager/base::head">
</div>
</div>
</div>
<div class="list">
<div class="w">
<div class="add">
<a href="bookServlet?method=toAddBook">添加图书</a>
</div>
<table>
<thead>
<tr>
<th>图片</th>
<th>商品名称</th>
<th>价格</th>
<th>作者</th>
<th>销量</th>
<th>库存</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr th:each="book,status : ${bookList}">
<td>
<img th:src="${book.imgPath}" alt="" />
</td>
<td th:text="${book.title}">活着</td>
<td th:text="${book.price}">
100.00
</td>
<td th:text="${book.author}">余华</td>
<td th:text="${book.sales}">200</td>
<td th:text="${book.stock}">400</td>
<td>
<a th:href="@{/bookServlet(method='toUpdateBook',id=${book.id})}">修改</a>
<a th:href="@{/bookServlet(method='deleteBookById',id=${book.id})}" class="del">删除</a>
</td>
</tr>
</tbody>
</table>
<div class="footer">
<div class="footer-right">
<div>首页</div>
<div>上一页</div>
<ul>
<li class="active">1</li>
<li>2</li>
<li>3</li>
</ul>
<div>下一页</div>
<div>末页</div>
<span>共10页</span>
<span>30条记录</span>
<span>到第</span>
<input type="text" />
<span>页</span>
<button>确定</button>
</div>
</div>
</div>
</div>
<div class="bottom">
<div class="w">
<div class="top">
<ul>
<li>
<a href="">
<img src="static/img/bottom1.png" alt="" />
<span>大咖级讲师亲自授课</span>
</a>
</li>
<li>
<a href="">
<img src="static/img/bottom.png" alt="" />
<span>课程为学员成长持续赋能</span>
</a>
</li>
<li>
<a href="">
<img src="static/img/bottom2.png" alt="" />
<span>学员真是情况大公开</span>
</a>
</li>
</ul>
</div>
<div class="content">
<dl>
<dt>关于尚硅谷</dt>
<dd>教育理念</dd>
<!-- <dd>名师团队</dd>
<dd>学员心声</dd> -->
</dl>
<dl>
<dt>资源下载</dt>
<dd>视频下载</dd>
<!-- <dd>资料下载</dd>
<dd>工具下载</dd> -->
</dl>
<dl>
<dt>加入我们</dt>
<dd>招聘岗位</dd>
<!-- <dd>岗位介绍</dd>
<dd>招贤纳师</dd> -->
</dl>
<dl>
<dt>联系我们</dt>
<dd>http://www.com.atguigu.com</dd>
<dd></dd>
</dl>
</div>
</div>
<div class="down">
尚硅谷书城.Copyright ©2015
</div>
</div>
</body>
</html>
3.3.7 页面效果展现
3.4 图书新增页面跳转
3.4.1 修改页面链接
说明: 当点击按钮时,实现新增页面跳转
<div class="add">
<a href="bookServlet?method=toAddBook">添加图书</a>
</div>
3.4.2 编辑BookServlet
/**
* 业务说明: 实现book新增
* 请求路径: bookServlet?method=toAddBook
* 请求参数: 无
* 跳转页面: manager/book_add
*/
protected void toAddBook(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.processTemplate("manager/book_add",request,response);
}
3.4.3 页面效果展现
3.5 图书新增实现
3.5.1 编辑book_add.html
规则说明:
- 将页面改造为thymeleaf页面
- 修改form表单提交路径和提交方式
<form action="bookServlet?method=addBook" method="post">
- 为form表单提交指定name属性,否则form提交有误.
<input type="text" placeholder="请输入名称" name="title"/>
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base th:href="@{/}">
<title>Document</title>
<link rel="stylesheet" href="static/css/minireset.css" />
<link rel="stylesheet" href="static/css/common.css" />
<link rel="stylesheet" href="static/css/style.css" />
<link rel="stylesheet" href="static/css/cart.css" />
<link rel="stylesheet" href="static/css/bookManger.css" />
<link rel="stylesheet" href="static/css/register.css" />
<link rel="stylesheet" href="static/css/book_edit.css" />
</head>
<body>
<div class="header">
<div class="w">
<div class="header-left">
<a href="index.html">
<img src="static/img/logo.gif" alt=""
/></a>
<h1>添加图书</h1>
</div>
<div class="header-right" th:include="manager/base::head">
</div>
<!-- <div class="header-right">
<a href="./book_manager.html" class="order">图书管理</a>
<a href="./order_manager.html" class="destory">订单管理</a>
<a href="index.html" class="gohome">返回商城</a>
</div>-->
</div>
</div>
<div class="login_banner">
<div class="register_form">
<form action="bookServlet?method=addBook" method="post">
<div class="form-item">
<div>
<label>名称:</label>
<input type="text" placeholder="请输入名称" name="title"/>
</div>
<span class="errMess" style="visibility: visible;"
>请输入正确的名称</span
>
</div>
<div class="form-item">
<div>
<label>价格:</label>
<input type="number" placeholder="请输入价格" name="price"/>
</div>
<span class="errMess">请输入正确数字</span>
</div>
<div class="form-item">
<div>
<label>作者:</label>
<input type="text" placeholder="请输入作者" name="author"/>
</div>
<span class="errMess">请输入正确作者</span>
</div>
<div class="form-item">
<div>
<label>销量:</label>
<input type="number" placeholder="请输入销量" name="sales"/>
</div>
<span class="errMess">请输入正确销量</span>
</div>
<div class="form-item">
<div>
<label>库存:</label>
<input type="number" placeholder="请输入库存" name="stock" />
</div>
<span class="errMess">请输入正确库存</span>
</div>
<button class="btn">提交</button>
</form>
</div>
</div>
<div class="bottom">
尚硅谷书城.Copyright ©2015
</div>
</body>
</html>
3.5.2 实现BookServlet-新增
/**
* 业务说明: 完成图书新增操作
* URL地址: bookServlet?method=addBook
* 参数: form表单数据提交
* 返回数据: 重定向到图书列表页面
* @param request
* @param response
* @throws ServletException
* @throws IOException
*/
protected void addBook(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException, InvocationTargetException, IllegalAccessException {
//1.获取用户提交的参数
Map<String, String[]> parameterMap = request.getParameterMap();
//2.使用beanUtils实现数据转化 由于业务调整至暂时没有文件上传操作
Book book = new Book();
BeanUtils.populate(book,parameterMap);
//3.实图书新增操作
boolean flag = bookService.addBook(book);
//4.如果新增成功,则重定向到book列表页面, 否则 暂时不考虑
if(flag){
response.sendRedirect(request.getContextPath()+"/bookServlet?method=findBookList");
}
}
3.5.3 编辑BookService接口和实现类-新增
- 编辑BookService接口
boolean addBook(Book book);
- 编辑BookServiceImpl实现类
@Override
public boolean addBook(Book book) {
return bookDao.addBook(book);
}
3.5.4 编辑BookDao接口和实现类-新增
- 编辑BookDao 接口
boolean addBook(Book book);
- 编辑BookDao实现类
@Override
public boolean addBook(Book book) {
String sql = "insert into books values (null,?,?,?,?,?,?)";
int rows = this.update(sql,book.getTitle(), book.getAuthor(),book.getPrice(),book.getSales(),book.getStock(),book.getImgPath());
return rows>0;
}
3.6 图书修改页面跳转
3.6.1 实现页面跳转
<td>
<a th:href="@{/bookServlet(method='toUpdateBook',id=${book.id})}">修改</a>
<a th:href="@{/bookServlet(method='deleteBookById',id=${book.id})}" class="del">删除</a>
</td>
3.6.2 编辑BookServlet实现页面跳转
/***
* 业务说明: 跳转到图书修改页面
* URL: <a th:href="@{/bookServlet(method='toUpdateBook',id=${book.id})}">修改</a>
* 参数: id=xxx
* 返回值: 跳转到图书修改页面 manager/book_edit
*/
protected void toUpdateBook(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException, InvocationTargetException, IllegalAccessException {
//1.获取图书Id
int id = Integer.valueOf(request.getParameter("id"));
//2.根据Id查询图书数据
Book book = bookService.findBookById(id);
//3.将数据保存到request域中
request.setAttribute("book",book);
//4.跳转到图书修改页面
this.processTemplate("manager/book_edit",request,response);
}
3.6.3 编辑BookService接口/实现类
- 编辑BookService接口
Book findBookById(int id);
- 编辑BookServiceImpl实现类
@Override
public Book findBookById(int id) {
return bookDao.findBookById(id);
}
3.6.4 编辑BookDao接口/实现类
- 编辑BookDao接口
Book findBookById(int id);
- 编辑BookDao实现类
@Override
public Book findBookById(int id) {
String sql = "select "+COLUMN+" from books where id = ?";
return this.getBean(Book.class,sql,id);
}
3.6.5 页面效果展现
3.7 图书修改实现
3.7.1 编辑修改页面
1.请求路径: bookServlet?method=updateBook
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base th:href="@{/}">
<title>Document</title>
<link rel="stylesheet" href="static/css/minireset.css" />
<link rel="stylesheet" href="static/css/common.css" />
<link rel="stylesheet" href="static/css/style.css" />
<link rel="stylesheet" href="static/css/cart.css" />
<link rel="stylesheet" href="static/css/bookManger.css" />
<link rel="stylesheet" href="static/css/register.css" />
<link rel="stylesheet" href="static/css/book_edit.css" />
</head>
<body>
<div class="header">
<div class="w">
<div class="header-left">
<a href="index.html">
<img src="static/img/logo.gif" alt=""
/></a>
<h1>编辑图书</h1>
</div>
<div class="header-right" th:include="manager/base::head">
</div>
</div>
</div>
<div class="login_banner">
<div class="register_form">
<form action="bookServlet?method=updateBook" method="post">
<!--设置修改的ID数据-->
<input type="hidden" name="id" th:value="${book.id}"/>
<input type="hidden" name="imgPath" th:value="${book.imgPath}"/>
<div class="form-item">
<div>
<label>名称:</label>
<input type="text" placeholder="请输入名称" name="title" th:value="${book.title}"/>
</div>
<span class="errMess" style="visibility: visible;"
>请输入正确的名称</span
>
</div>
<div class="form-item">
<div>
<label>价格:</label>
<input type="number" placeholder="请输入价格" name="price" th:value="${book.price}" />
</div>
<span class="errMess">请输入正确数字</span>
</div>
<div class="form-item">
<div>
<label>作者:</label>
<input type="text" placeholder="请输入作者" name="author" th:value="${book.author}"/>
</div>
<span class="errMess">请输入正确作者</span>
</div>
<div class="form-item">
<div>
<label>销量:</label>
<input type="number" placeholder="请输入销量" name="sales" th:value="${book.sales}" />
</div>
<span class="errMess">请输入正确销量</span>
</div>
<div class="form-item">
<div>
<label>库存:</label>
<input type="number" placeholder="请输入库存" name="stock" th:value="${book.stock}" />
</div>
<span class="errMess">请输入正确库存</span>
</div>
<button class="btn">提交</button>
</form>
</div>
</div>
<div class="bottom">
尚硅谷书城.Copyright ©2015
</div>
</body>
</html>
3.7.2 编辑BookServlet-修改操作
/**
* 业务说明: 完成图书的修改操作
* URL地址:bookServlet?method=updateBook
* 参数: 整个form表单
* 返回值: 重定向到图书列表页面
*/
protected void updateBook(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException, InvocationTargetException, IllegalAccessException {
//1.获取用户参数
Map<String, String[]> parameterMap = request.getParameterMap();
Book book = new Book();
BeanUtils.populate(book,parameterMap);
//2.完成图书修改操作
boolean flag = bookService.updateBookById(book);
//3.判断如果修改成功,则重定向到图书列表页面
if(flag){
//4.跳转到图书修改页面
response.sendRedirect(request.getContextPath()+"/bookServlet?method=findBookList");
}
}
12345678910111213141516171819202122
3.7.3 编辑BookService接口/实现类
- 编辑BookService接口
boolean updateBookById(Book book);
1
2.编辑BookService实现类
@Override
public boolean updateBookById(Book book) {
return bookDao.updateBookById(book);
}
3.7.4 编辑BookDao接口/实现类
- 编辑BookDao接口
boolean updateBookById(Book book);
- 编辑BookDaoImpl实现类
@Override
public boolean updateBookById(Book book) {
String sql = "update books set title=?,author=?,price=?,sales=?,stock=?,img_path=? where id=?";
int rows = this.update(sql,book.getTitle(),book.getAuthor(),book.getPrice(),book.getSales(),book.getStock(),book.getImgPath(),book.getId());
return rows>0;
}
3.8 图书删除操作
3.8.1 业务说明
编辑URL地址: <a th:href="@{/bookServlet(method='deleteBookById',id=${book.id})}" class="del">删除</a>
3.8.2 编辑BookServlet
/***
* 业务说明: 删除图片数据
* URL: /bookServlet(method='deleteBookById',id=${book.id})"
* 参数: id=xxx
* 返回值: 如果删除数据成功,则重定向图片列表页面
*/
protected void deleteBookById(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException, InvocationTargetException, IllegalAccessException {
//1.获取图书Id
int id = Integer.valueOf(request.getParameter("id"));
//2.根据ID删除数据
boolean flag = bookService.deleteBookById(id);
//3.判断条件
if(flag){
response.sendRedirect(request.getContextPath()+"/bookServlet?method=findBookList");
}
}
=
3.8.3 编辑BookService
- 编辑BookService
boolean deleteBookById(int id);
2.编辑BookServiceImpl
@Override
public boolean deleteBookById(int id) {
return bookDao.deleteBookById(id);
}
3.8.4 编辑BookDao
- 编辑删除接口
boolean deleteBookById(int id);
- 编辑删除实现类
@Override
public boolean deleteBookById(int id) {
String sql = "delete from books where id=?";
return this.update(sql,id)>0;
}
4. 会话技术
4.1 为什么需要会话控制
保持用户登录状态,就是当用户在登录之后,会在服务器中保存该用户的登录状态,当该用户后续访问该项目中的其它动态资源(Servlet或者Thymeleaf)的时候,能够判断当前是否是已经登录过的。而从用户登录到用户退出登录这个过程中所发生的所有请求,其实都是在一次会话范围之内
4.2 域对象的范围
4.2.1 请求域的范围
每一次请求都有一个请求域对象,当请求结束的时候对应的请求域对象也就销毁了
4.2.2 会话域的范围
会话域(打开一个浏览器窗口)是从客户端连接上服务器开始,一直到客户端关闭,这一整个过程中发生的所有请求都在同一个会话域中;而不同的客户端是不能共用会话域的
4.2.3 应用域的范围
整个项目部署之后,只会有一个应用域对象,所有客户端都是共同访问同一个应用域对象,在该项目的所有动态资源中也是共用一个应用域对象. 所以在应用域中的数据,所有的项目共享
4.3 Cookie技术
4.3.1 Cookie的概念
Cookie,有时也用其复数形式 Cookies。类型为“小型文本文件”,是某些网站为了辨别用户身份,进行Session跟踪而储存在用户本地终端上的数据(通常经过加密),由用户客户端计算机暂时或永久保存的信息.
Cookie是一种客户端的会话技术,它是服务器存放在浏览器的一小份数据,浏览器以后每次访问该服务器的时候都会将这小份数据携带到服务器去。
4.3.2 Cookie的特点
- 在浏览器中存放数据
- 将浏览器中存放的数据携带到服务器
- Cookie是一段不超过4KB的小型文本数据,由一个名称(Name)、一个值(Value)和其它几个用于控制Cookie有效期、安全性、使用范围的可选属性组成。
4.3.3 Cookie的应用场景
1.记住用户名
当我们在用户名的输入框中输入完用户名后,浏览器记录用户名,下一次再访问登录页面时,用户名自动填充到用户名的输入框.
2.保存电影的播放进度
在网页上播放电影的时候,如果中途退出浏览器了,下载再打开浏览器播放同一部电影的时候,会自动跳转到上次退出时候的进度,因为在播放的
时候会将播放进度保存到cookie中
4.3.4 Cookie的时效性
如果我们不设置Cookie的时效性,默认情况下Cookie的有效期是一次会话范围内,我们可以通过cookie的setMaxAge()方法让Cookie持久化保存到浏览器上
- 会话级Cookie
- 服务器端并没有明确指定Cookie的存在时间
- 在浏览器端,Cookie数据存在于内存中
- 只要浏览器还开着,Cookie数据就一直都在
- 浏览器关闭,内存中的Cookie数据就会被释放
- 持久化Cookie
- 服务器端明确设置了Cookie的存在时间
- 在浏览器端,Cookie数据会被保存到硬盘上
- Cookie在硬盘上存在的时间根据服务器端限定的时间来管控,不受浏览器关闭的影响
- 持久化Cookie到达了预设的时间会被释放
cookie.setMaxAge(int expiry)
参数单位是秒,表示cookie的持久化时间,如果设置参数为0,表示将浏览器中保存的该cookie删除
4.3.5 关于Cookie使用AP说明
public class CookieServlet extends BaseServlet {
/**
* 实现Cookie添加操作
*/
protected void addCookie(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
//1.设定Cookie的数据
Cookie cookie1 = new Cookie("key1", "密钥信息1");
Cookie cookie2 = new Cookie("key2", "密钥信息2");
//2.设定Cookie的有效期 单位是秒
cookie2.setMaxAge(10);
//3.设定Cookie有效路径 表示请求 /项目名称/aaa时才会携带该Cookie
cookie2.setPath(request.getContextPath()+"/aaa");
//4.设定Cookie 在哪个域名有效
cookie2.setDomain("www.baidu.com");
response.addCookie(cookie1);
response.addCookie(cookie2);
//给出响应
response.getWriter().write("Cookie添加成功!!!");
}
protected void getCookie(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Cookie[] cookies = request.getCookies();
if(cookies !=null && cookies.length>0){
for (Cookie cookie : cookies){
String name = cookie.getName();
String value = cookie.getValue();
System.out.println(name+":"+value);
}
}
//response.sendRedirect(request.getContextPath()+"/index.html");
request.getRequestDispatcher("index.html").forward(request,response);
}
}
4.3.6 浏览器检查Cookie
说明: 通过F12 开发工具打开浏览器 可以检查cookie的相关信息
4.4 Session技术
4.4.1 Session概述
session是服务器端的技术。服务器为每一个浏览器开辟一块内存空间,即session对象。由于session对象是每一个浏览器特有的,所以用户的记录可以存放在session对象中
4.4.2 Session的API介绍
- request.getSession(); 获得session(如果第一次调用的时候其实是创建session,第一次之后通过sessionId找到session进行使用)
- Object getAttribute(String name) ;获取值
- void setAttribute(String name, Object value) ;存储值
- void removeAttribute(String name) ;移除值
4.4.3 Session的代码
package com.atguigu.servlet;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
public class SessionServlet extends BaseServlet {
//1.添加Session
protected void addSession(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession session = request.getSession();
session.setAttribute("name","Session数据");
response.sendRedirect(request.getContextPath()+"/index.html");
}
//2.获取Session
protected void getSession(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession session = request.getSession();
System.out.println(session.getAttribute("name"));
}
//3.移除Session数据
protected void removeSession(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession session = request.getSession();
session.removeAttribute("name");
response.sendRedirect(request.getContextPath()+"/index.html");
}
}
5. 书城项目优化4
5.1 优化说明
- 添加Session实现用户信息共享
- 添加验证码信息
5.2 Session保存用户登录信息
5.2.1 重构登录Servlet
用户登录成功之后,将用户信息保存到Session域中.那么该会话内可以实现数据共享.
//3.完成用户登录操作
protected void login(HttpServletRequest request, HttpServletResponse response) throws IOException {
//用户用户提交的数据信息
Map<String, String[]> parameterMap = request.getParameterMap();
User user = new User();
try {
BeanUtils.populate(user,parameterMap);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
//2.实现用户登录操作
User userDB = userService.findUserByUP(user);
//3.判断查询数据有结果 如果有数据则重定向到首页 否则转发到登录页面
if(userDB == null){
request.setAttribute("loginUsername",user.getUsername());
request.setAttribute("loginMsg","用户名或密码错误");
this.processTemplate("user/login",request,response);
}else{
//将数据通过session域进行保存
request.getSession().setAttribute("user",userDB);
this.processTemplate("user/login_success",request,response);
}
}
5.2.2 重构登录成功页面
<div>
<span>欢迎<span class="um_span" th:text="${session.user.username}">张总</span>光临尚硅谷书城</span>
<a href="../order/order.html">我的订单</a>
<a href="user?method=logout" class="register">注销</a>
<a href="index.html">返回</a>
</div>
123456
5.2.3 重构系统首页
说明: 如果session中有user信息则展现用户数据,如果Session中没有用户信息,则展现登录和注册功能
知识点:
- thymeleaf 通过session取值
- th:if 和 th:unless的使用
<div class="topbar-right" th:if="${session.user == null}">
<a href="user?method=toLogin" class="login">登录</a>
<a href="user?method=toRegist" class="register">注册</a>
<a
href="cart/cart.html"
class="cart iconfont icon-gouwuche
"
>
购物车
<div class="cart-num">3</div>
</a>
<a href="bookServlet?method=findBookList" class="admin">后台管理</a>
</div>
<!-- 登录后风格-->
<div class="topbar-right" th:unless="${session.user == null}" >
<span>欢迎你: <b th:text="${session.user.username}">张总</b></span>
<a href="user?method=logout" class="register">注销</a>
<a
href="pages/cart/cart.jsp"
class="cart iconfont icon-gouwuche">
购物车
<div class="cart-num">3</div>
</a>
<a href="bookServlet?method=findBookList" class="admin">后台管理</a>
</div>
5.3 用户注销功能
5.3.1 编辑注销按钮
- 修改超链接按钮
<a href="user?method=logout" class="register">注销</a>
1
- 整体页面结构
<!-- 登录后风格-->
<div class="topbar-right" th:unless="${session.user == null}" >
<span>欢迎你: <b th:text="${session.user.username}">张总</b></span>
<a href="user?method=logout" class="register">注销</a>
<a
href="pages/cart/cart.jsp"
class="cart iconfont icon-gouwuche">
购物车
<div class="cart-num">3</div>
</a>
<a href="bookServlet?method=findBookList" class="admin">后台管理</a>
</div>
5.3.2 编辑注销Servlet
/**
* 退出操作
* URL: user?method=login
* 参数: 无
* 跳转要求: 删除将session数据删除之后,重定向到系统首页即可.
*/
protected void logout(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//删除用户信息.
request.getSession().removeAttribute("user");
response.sendRedirect(request.getContextPath()+"/index.html");
}
5.5 验证码功能说明
5.5.1 关于验证码说明
业务说明: 图中的验证码需要实时更新. 点击更新等操作.
5.5.2 验证码工具API-Kaptcha介绍
Kaptcha是一个基于SimpleCaptcha的验证码开源项目。官网地址:http://code.google.com/p/kaptcha/
Kaptcha的使用比较方便,只需添加jar包依赖之后简单地配置就可以使用了。kaptcha所有配置都可以通过web.xml来完成
5.5.2.1 引入jar包
说明: 导入lib目录 即可.
5.5.2.2 编辑web.xml文件
说明: 工具API自己有Servlet,所以需要配置web.xml即可
- 代码结构说明
- 编辑web.xml配置文件
<!--添加验证码Servlet-->
<servlet>
<servlet-name>KaptchaServlet</servlet-name>
<servlet-class>com.google.code.kaptcha.servlet.KaptchaServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>KaptchaServlet</servlet-name>
<url-pattern>/kaptchaServlet</url-pattern>
</servlet-mapping>
5.5.2.3 访问测试
说明: 通过浏览器访问数据,工具API会返回一个有验证码数据的图片,如图所示:
URL地址: http://localhost:8090/bookstore_04/kaptchaServlet
5.5.3 源码介绍
- 源码结构
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.google.code.kaptcha.servlet;
import com.google.code.kaptcha.Producer;
import com.google.code.kaptcha.util.Config;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Date;
import java.util.Enumeration;
import java.util.Properties;
import javax.imageio.ImageIO;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class KaptchaServlet extends HttpServlet implements Servlet {
private Properties props = new Properties();
private Producer kaptchaProducer = null;
private String sessionKeyValue = null;
private String sessionKeyDateValue = null;
public KaptchaServlet() {
}
public void init(ServletConfig conf) throws ServletException {
super.init(conf);
ImageIO.setUseCache(false);
Enumeration initParams = conf.getInitParameterNames();
while(initParams.hasMoreElements()) {
String key = (String)initParams.nextElement();
String value = conf.getInitParameter(key);
this.props.put(key, value);
}
Config config = new Config(this.props);
this.kaptchaProducer = config.getProducerImpl();
this.sessionKeyValue = config.getSessionKey();
this.sessionKeyDateValue = config.getSessionDate();
}
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setDateHeader("Expires", 0L);
resp.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
resp.addHeader("Cache-Control", "post-check=0, pre-check=0");
resp.setHeader("Pragma", "no-cache");
resp.setContentType("image/jpeg");
String capText = this.kaptchaProducer.createText();
req.getSession().setAttribute(this.sessionKeyValue, capText);
req.getSession().setAttribute(this.sessionKeyDateValue, new Date());
BufferedImage bi = this.kaptchaProducer.createImage(capText);
ServletOutputStream out = resp.getOutputStream();
ImageIO.write(bi, "jpg", out);
}
}
- 核心代码讲解
key=“KAPTCHA_SESSION_KEY”,通过默认值key从Session中获取数据.
5.6 验证码功能实现
5.6.1 非空校验实现
- 编辑页面html
<input type="text" placeholder="请输入验证码" v-model="code" @click="checkCode"/>
- 编辑页面JS(暂时)
checkCode(){
//暂时只做非空判断 后期ajax维护
if(this.code === ''){
this.msg.codeMsg = "验证码不能为空"
this.flagArray[4] = 0
}else{
this.msg.codeMsg = '√'
this.flagArray[4] = 1
}
}
5.6.2 验证码刷新
业务说明: 当用户点击验证码按钮时,需要重新发起请求,获取全新的验证码信息
- 1编辑页面HTML 添加事件
<div class="form-item">
<div>
<label>验证码:</label>
<div class="verify">
<input type="text" placeholder="请输入验证码" v-model="code" name="code" @click="checkCode"/>
<img :src="codeSrc" alt="验证码" width="90px" height="40px" @click="reloadCode"/>
<!--KAPTCHA_SESSION_KEY-->
</div>
</div>
<span class="errMess" v-text="msg.codeMsg"></span>
</div>
- 2 编辑页面JS
说明: 通过属性绑定的形式 动态的为src属性赋值, 但是由于浏览器缓存问题导致没有办法刷新页面. 所以通过date获取当前最新时间,实现重新发起请求.
reloadCode(){
//重新加载属性
this.codeSrc = "kaptchaServlet?date="+new Date()
},
5.6.3 重构UserServlet业务
知识点:
\1. 通过KEY=KAPTCHA_SESSION_KEY
获取真实的验证码数据
\2. 对比用户输入用户输入的验证码是否正确. 正确继续执行, 否则返回错误消息
/**
* 注册: 用户注册说明
* URL地址: user?method=regist
* 参数: form表单提交
*/
protected void regist(HttpServletRequest request, HttpServletResponse response) throws IOException {
//1.判断验证码是否正确
String code = request.getParameter("code");
String realCode = (String) request.getSession().getAttribute("KAPTCHA_SESSION_KEY");
//System.out.println(code+":"+realCode);
if(!code.equals(realCode)){
//表示验证码错误,转发回注册页面 提示用户即可
request.setAttribute("codeMsg","验证码错误");
this.processTemplate("user/regist",request, response);
}
Map<String, String[]> parameterMap = request.getParameterMap();
User user = new User();
try {
BeanUtils.populate(user,parameterMap);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
//完成用户注册操作,注册成功返回布尔类型数据 true表示成功, false表示失败
boolean flag = userService.addUser(user);
//如果注册成功,则转发到注册成功页面,否则转发到注册页面
if(flag){
request.setAttribute("regist_username", user.getUsername());
this.processTemplate("user/regist_success",request, response);
}else{
this.processTemplate("user/regist",request, response);
}
}
页面动态获取返回值提示信息: JS中获取thymeleaf数据
语法: [[ ${key数据} ]]
msg: {
usernameErrorMsg: "用户名应为6~16位数字和字母组成",
passwordErrorMsg: "密码的长度至少为8位",
passwordErrorMsg2: "密码两次输入不一致",
emailErrorMsg: "请输入正确的邮箱格式",
codeMsg: "[[${codeMsg==null?'请输入正确的验证码':codeMsg}]]"
},
6 Ajax
6.1 Ajax介绍
6.1.1 服务器端渲染
6.1.2 Ajax渲染(局部更新)
6.1.3 前后端分离
真正的前后端分离是前端项目和后端项目分服务器部署,在我们这里我们先理解为彻底舍弃服务器端渲染,数据全部通过Ajax方式以JSON格式/xml格式来传递
6.2 同步与异步
Ajax本身就是Asynchronous JavaScript And XML的缩写,直译为:异步的JavaScript和XML。
在实际应用中Ajax指的是:
- 不刷新浏览器窗口
- 不做页面跳转
- 局部更新页面内容的技术。
6.2.1 什么是同步
多个操作按顺序执行前面的操作没有完成,后面的操作就必须等待 所以同步操作通常是串行的。
6.2.2 什么是异步
多个操作相继开始并发执行,即使开始的先后顺序不同,但是由于它们各自是在自己独立的进程或线程中完成,所以互不干扰谁也不用等谁
6.3 Axios
6.3.1 Axios介绍
使用原生的JavaScript程序执行Ajax极其繁琐,所以一定要使用框架来完成。而Axios就是目前最流行的前端Ajax框架。
Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。
官方网址: http://www.axios-js.com/
6.3.2 Axios特点
- 从浏览器中创建 XMLHttpRequests
- 从 node.js 创建 http 请求
- 支持 Promise API
- 拦截请求和响应
- 转换请求数据和响应数据
- 取消请求
- 自动转换 JSON 数据
- 客户端支持防御 XSRF
6.3.3 导入JS的方式
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
6.3 Axios入门案例
6.3.1 导入JS
说明: 根据课前资料中的文件 导入vue.js/axios.js 其中axios.min.js是生产环境的要求
6.3.2 编辑index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Axios入门案例</title>
<script src="js/vue.js"></script>
<script src="js/axios.js"></script>
</head>
<body>
<h1>Axios入门案例用法</h1>
<script>
//1.发起ajax-get请求
axios({
url: "axiosGet",
method: "get",
params: {
id: 200,
name: "tomcat"
}
}).then(function (promise){
console.log(promise)
console.log(promise.data)
//获取服务器返回值业务数据
//console.log(promise.data.name)
//遍历数据
for(let i=0;i<promise.data.length;i++){
console.log(promise.data[i])
}
}).catch(function (error){
alert(error)
})
</script>
</body>
</html>
6.3.3 实现数据获取
package com.atguigu.servler;
import com.atguigu.bean.User;
import com.fasterxml.jackson.databind.ObjectMapper;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class AxiosGetServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request,response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.获取Ajax提交的数据
String id = request.getParameter("id");
String name = request.getParameter("name");
System.out.println(id+":"+name);
//2.响应数据
response.setContentType("text/html;charset=utf-8");
/*response.getWriter().write("服务器响应数据!!!");*/
//3.手动封装JSON数据
//String json = "{\"id\":100,\"name\":\"Axios练习\"}";
//4. jackson用法
User user1 = new User();
user1.setId(1001);
user1.setName("Axios练习测试1");
User user2 = new User();
user2.setId(1002);
user2.setName("Axios练习测试2");
List<User> list = new ArrayList<>();
list.add(user1);
list.add(user2);
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(list);
response.getWriter().write(json);
//JSON转化为对象
List<User> list2 = mapper.readValue(json,ArrayList.class);
System.out.println(list2);
}
}
6.3.4 额外扩展
7 书城项目第五阶段
7.1 用户名唯一性校验
7.1.1 添加jar包文件
说明: 添加axios.js和json相关jar包文件
7.1.2 SysResult对象封装
说明: 为了实现前后端数据交互,需要后端使用统一的数据返回值.
指定属性:
- flag: true 表示业务执行成功, false 表示业务执行失败.
- data: 表示后端返回页面的服务器数据.
- msg: 表示后端返回页面的提示信息.
package com.atguigu.vo;
import java.io.Serializable;
public class SysResult implements Serializable {
private Boolean flag; //true成功 false失败
private Object data; //服务器返回业务数据
private String msg; //服务器返回的提示信息
public SysResult(){
}
public SysResult(Boolean flag, Object data, String msg) {
this.flag = flag;
this.data = data;
this.msg = msg;
}
public Boolean getFlag() {
return flag;
}
public void setFlag(Boolean flag) {
this.flag = flag;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public static SysResult success(){
return new SysResult(true,null,"业务执行成功!");
}
public static SysResult success(Object data){
return new SysResult(true,data,"业务执行成功!");
}
public static SysResult success(Object data,String msg){
return new SysResult(true,data,msg);
}
public static SysResult fail(){
return new SysResult(false,null,"业务执行失败"); //默认值为false
}
}
7.1.3 重构编辑注册页面
checkUsername(){
//1.定义正则表达式语法
let usernameRege = /^[a-zA-Z0-9]{6,16}$/
let usernameFlag = usernameRege.test(this.user.username)
//2.完成正则表达式校验
if(!usernameFlag){
this.msg.usernameErrorMsg = "用户名应为6~16位数字和字母组成"
this.flagArray[0] = 0
}
//3.校验用户名是否重复
axios.get("user",{params: {"method":"checkUsername","username": this.user.username}})
.then(promise => {
//获取服务器数据:
if(promise.data.flag){ //true表示用户可以使用
this.msg.usernameErrorMsg = "√"
this.flagArray[0] = 1
}else{
this.msg.usernameErrorMsg = "用户名已存在,请更换!"
this.flagArray[0] = 0
}
}).catch(error => {
this.msg.usernameErrorMsg = "用户名校验失败"
this.flagArray[0] = 0
})
},
7.1.4 编辑UserServlet校验方法
/**
* 业务说明: 完成用户名校验
* URL:user?method=checkUsername
* 参数: {username:this.user.username}
* 返回值: SysResult对象
*/
protected void checkUsername(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
//只需要查询有没有即可
long count = userService.findUserByUserName(username);
response.setContentType("text/html;charset=utf-8");
if(count>0){
//表示用户名已存在
SysResult sysResult = SysResult.fail();
String json = MAPPER.writeValueAsString(sysResult);
response.getWriter().write(json);
}else{
//表示用户名可以使用
SysResult sysResult = SysResult.success();
String json = MAPPER.writeValueAsString(sysResult);
response.getWriter().write(json);
}
}
7.1.5 编辑UserService
- 编辑UserService接口
long findUserByUserName(String username);
1
- 编辑UserServiceImpl实现类
@Override
public long findUserByUserName(String username) {
return userDao.findUserByUserName(username);
}
7.1.6 编辑UserDao和UserDaoImpl
- 编辑UserDao接口
long findUserByUserName(String username);
- 编辑UserDaoImpl实现类
@Override
public long findUserByUserName(String username) {
String sql = "select count(*) from user where username=?";
return (long) this.getValue(sql,username);
}
7.1.7 页面效果展现
7.2 验证码校验
7.2.1 编辑页面
<div class="verify">
<input type="text" placeholder="请输入验证码" v-model="code" name="code" @click="checkCode"/>
<img :src="codeSrc" alt="验证码" width="90px" height="40px" @click="reloadCode"/>
<!--KAPTCHA_SESSION_KEY-->
</div>
7.2.2 编辑页面JS
checkCode(){
//暂时只做非空判断 后期ajax维护
if(this.code === ''){
this.msg.codeMsg = "验证码不能为空"
this.flagArray[4] = 0
}else{
//发起ajax请求 校验是否正确
axios.get("user",{params: {
method: "checkCode",
code: this.code
}}).then(promise => {
if(promise.data.flag){
this.msg.codeMsg = '√'
this.flagArray[4] = 1
}else{
this.msg.codeMsg = "验证码错误请修改!"
this.flagArray[4] = 0
}
}).catch(error => {
this.msg.codeMsg = "验证码校验失败"
this.flagArray[4] = 0
})
}
}
7.2.3 编辑UserServlet
//http://localhost:8090/user?method=checkCode&code=ddddd
protected void checkCode(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String code = request.getParameter("code");
String realCode = (String) request.getSession().getAttribute("KAPTCHA_SESSION_KEY");
response.setContentType("text/html;charset=utf-8");
if(realCode.equals(code)){
String json = MAPPER.writeValueAsString(SysResult.success());
response.getWriter().write(json);
}else{
String json = MAPPER.writeValueAsString(SysResult.fail());
response.getWriter().write(json);
}
}
7.2.4 页面效果展现
8 购物车功能实现
8.1 购物车数据封装
8.1.1 购物项说明
关于购物车项目业务说明:
- 指定购物项CartItem
[book对象,数量,单项总价]
- 指定购物车信息 Cart
[图书信息集合, 总件数, 总金额]
8.1.2 封装CartItem对象
数据说明: 其中的总价可以计算,为了简单重写其中的set方法.为单项总价赋值即可. 代码如下:
package com.atguigu.bean;
import java.io.Serializable;
public class CartItem implements Serializable {
private Book book; //购物项图书
private Integer count; //购物数量
private Double amount; //购物项总价
public CartItem(){
}
//总价: 书单价*数量
public CartItem(Book book,Integer count){
this.book = book;
this.count = count;
this.amount = book.getPrice() * count;
}
public Book getBook() {
return book;
}
public void setBook(Book book) {
this.book = book;
this.amount = book.getPrice() * this.count;
}
public Integer getCount() {
return count;
}
public void setCount(Integer count) {
this.count = count;
this.amount = this.book.getPrice() * count;
}
public Double getAmount() {
return amount;
}
@Override
public String toString() {
return "CartItem{" +
"book=" + book +
", count=" + count +
", amount=" + amount +
'}';
}
}
8.1.3 封装Cart对象
说明:
- Map集合封装CartItem对象, 其中的key: 图书ID号, 如果重复添加只做数量更新.
- totalCount 所有的总数 通过get方法为该属性赋值.
- totalAmound 所有的总价 通过get方法为属性赋值.
package com.atguigu.bean;
import java.io.Serializable;
import java.util.*;
public class Cart implements Serializable {
private Map<Integer,CartItem> map = new HashMap<>();
private Integer totalCount; //购物车的总数量
private Double totalAmound; //购物车的总价格
private Collection cartItems; //为了页面获取数据方便 添加的属性
//将书籍添加到购物项中
public void addCart(Book book){
//获取数据信息
CartItem cartItem = map.get(book.getId());
if(cartItem ==null){
//说明:用户第一次添加,将书籍添加到购物项中
cartItem = new CartItem(book,1);
map.put(book.getId(),cartItem);
}else{
//说明:之前已经添加过,将数据的数量+1
cartItem.setCount(cartItem.getCount()+1);
}
}
@Override
public String toString() {
return "Cart{" +
"map=" + map +
", totalCount=" + totalCount +
", totalAmound=" + totalAmound +
'}';
}
//计算购物车书的总数量
public Integer getTotalCount() {
Collection<CartItem> cartItems = map.values();
int totalCount = 0;
for (CartItem cartItem : cartItems){
totalCount += cartItem.getCount();
}
//将总数赋值给业务数据
this.totalCount = totalCount;
return totalCount;
}
//获取总价格
public Double getTotalAmound(){
Collection<CartItem> cartItems = map.values();
double totalAmound = 0L;
for (CartItem cartItem : cartItems){
//每项总价: 价格* 数量
totalAmound += cartItem.getAmount();
}
//计算购物车商品总价
this.totalAmound = totalAmound;
return totalAmound;
}
public Collection getCartItems() {
return this.map.values();
}
}
8.2 购物车新增操作
8.2.1 页面分析
- 新增购物车按钮编辑 由于图书信息,需要添加图书的ID号, 但是由于该页面采用thymeleaf和vue.js混合的方式.所以需要获取图书ID 通过id属性动态获取.
- 编辑页面JS
<script>
const app = new Vue({
el: "#app",
data: {
totalCount: "[[${session.cart==null?0:session.cart.totalCount}]]"
},
methods: {
async addCart(){
//利用event对象获取目标标签的ID属性.
let id = event.target.id
//利用ajax请求实现远程数据获取.
let {data: result} = await axios.get("cart",{params: {method: "addCart",id: id}})
if(result.flag){
alert("新增购物车成功!")
this.totalCount = result.data
//console.log(this.totalCount)
}else{
alert("新增购物车失败!")
}
}
}
})
</script>
- 整体页面结构
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>书城首页</title>
<base th:href="@{/}" />
<link rel="stylesheet" href="static/css/minireset.css" />
<link rel="stylesheet" href="static/css/common.css" />
<link rel="stylesheet" href="static/css/iconfont.css" />
<link rel="stylesheet" href="static/css/index.css" />
<link rel="stylesheet" href="static/css/swiper.min.css" />
<script src="static/script/vue.js"></script>
<script src="static/script/axios.js"></script>
</head>
<body>
<div id="app">
<div class="topbar">
<div class="w">
<div class="topbar-left">
<i>送至:</i>
<i>北京</i>
<i class="iconfont icon-ai-arrow-down"></i>
</div>
<div class="topbar-right" th:if="${session.user == null}">
<a href="user?method=toLogin" class="login">登录</a>
<a href="user?method=toRegist" class="register">注册</a>
<a
href="cart?method=toCartList"
class="cart iconfont icon-gouwuche
"
>
购物车
<div class="cart-num" v-text="totalCount"></div>
</a>
<a href="bookServlet?method=findBookList" class="admin">后台管理</a>
</div>
<!-- 登录后风格-->
<div class="topbar-right" th:unless="${session.user == null}" >
<span>欢迎你: <b th:text="${session.user.username}">张总</b></span>
<a href="user?method=logout" class="register">注销</a>
<a
href="cart?method=toCartList"
class="cart iconfont icon-gouwuche">
购物车
<div class="cart-num" v-text="totalCount"></div>
</a>
<a href="bookServlet?method=findBookList" class="admin">后台管理</a>
</div>
</div>
</div>
<div class="header w">
<a href="#" class="header-logo"></a>
<div class="header-nav">
<ul>
<li><a href="#">java</a></li>
<li><a href="#">前端</a></li>
<li><a href="#">小说</a></li>
<li><a href="#">文学</a></li>
<li><a href="#">青春文学</a></li>
<li><a href="#">艺术</a></li>
<li><a href="#">管理</a></li>
</ul>
</div>
<div class="header-search">
<input type="text" placeholder="十万个为什么" />
<button class="iconfont icon-search"></button>
</div>
</div>
<div class="banner w clearfix">
<div class="banner-left">
<ul>
<li>
<a href="">
<span>文学 鉴赏</span>
<i class="iconfont icon-jiantou"></i
></a>
</li>
<li>
<a href="">
<span>社科 研究</span>
<i class="iconfont icon-jiantou"></i
></a>
</li>
<li>
<a href="">
<span>少儿 培训</span>
<i class="iconfont icon-jiantou"></i
></a>
</li>
<li>
<a href="">
<span>艺术 赏析</span>
<i class="iconfont icon-jiantou"></i
></a>
</li>
<li>
<a href="">
<span>生活 周边</span>
<i class="iconfont icon-jiantou"></i
></a>
</li>
<li>
<a href="">
<span>文教 科技</span>
<i class="iconfont icon-jiantou"></i
></a>
</li>
<li>
<a href="">
<span>热销 畅读</span>
<i class="iconfont icon-jiantou"></i
></a>
</li>
</ul>
</div>
<div class="banner-right">
<div class="swiper-container">
<ul class="swiper-wrapper">
<li class="swiper-slide">
<img src="static/uploads/banner4.jpg" alt="">
<!-- <div class="banner-img"></div> -->
</li>
<li class="swiper-slide">
<img src="static/uploads/banner5.jpg" alt="">
<!-- <div class="banner-img"></div> -->
</li>
<li class="swiper-slide">
<img src="static/uploads/banner6.jpg" alt="">
<!-- <div class="banner-img"></div> -->
</li>
</ul>
<div class="swiper-button-prev"></div>
<div class="swiper-button-next"></div>
<!-- Add Pagination -->
<div class="swiper-pagination"></div>
</div>
</div>
</div>
<div class="books-list ">
<div class="w">
<div class="list">
<div class="list-header">
<div class="title">图书列表</div>
<div class="price-search">
<span>价格:</span>
<input type="text">
<span>-元</span>
<input type="text">
<span>元</span>
<button>查询</button>
</div>
</div>
<div class="list-content">
<div class="list-item" th:each="book,status : ${bookList}">
<img th:src="${book.imgPath}" alt="">
<p>书名:<span th:text="${book.title}"></span></p>
<p>作者:<span th:text="${book.author}"></span></p>
<p>价格:¥<span th:text="${book.price}"></span></p>
<p>销量:<span th:text="${book.sales}"></span></p>
<p>库存:<span th:text="${book.stock}"></span></p>
<button th:id="${book.id}" @click="addCart">加入购物车</button>
</div>
<!-- <div href="" class="list-item">
<img src="static/uploads/huranqiri.jpg" alt="">
<p>书名:活着</p>
<p>作者:余华</p>
<p>价格:¥66.6</p>
<p>销量:230</p>
<p>库存:1000</p>
<button>加入购物车</button>
</div>
<div href="" class="list-item">
<img src="static/uploads/Javabianchengsixiang.jpg" alt="">
<p>书名:活着</p>
<p>作者:余华</p>
<p>价格:¥66.6</p>
<p>销量:230</p>
<p>库存:1000</p>
<button>加入购物车</button>
</div>
<div href="" class="list-item">
<img src="static/uploads/jiaofu.jpg" alt="">
<p>书名:活着</p>
<p>作者:余华</p>
<p>价格:¥66.6</p>
<p>销量:230</p>
<p>库存:1000</p>
<button>加入购物车</button>
</div>
<div href="" class="list-item">
<img src="static/uploads/jieyouzahuodian.jpg" alt="">
<p>书名:活着</p>
<p>作者:余华</p>
<p>价格:¥66.6</p>
<p>销量:230</p>
<p>库存:1000</p>
<button>加入购物车</button>
</div>
<div href="" class="list-item">
<img src="static/uploads/kanjian.jpg" alt="">
<p>书名:活着</p>
<p>作者:余华</p>
<p>价格:¥66.6</p>
<p>销量:230</p>
<p>库存:1000</p>
<button>加入购物车</button>
</div>
<div href="" class="list-item">
<img src="static/uploads/pinang.jpg" alt="">
<p>书名:活着</p>
<p>作者:余华</p>
<p>价格:¥66.6</p>
<p>销量:230</p>
<p>库存:1000</p>
<button>加入购物车</button>
</div>
<div href="" class="list-item">
<img src="static/uploads/pingfandeshijie.jpg" alt="">
<p>书名:活着</p>
<p>作者:余华</p>
<p>价格:¥66.6</p>
<p>销量:230</p>
<p>库存:1000</p>
<button>加入购物车</button>
</div>
<div href="" class="list-item">
<img src="static/uploads/qiadaohaochudexingfu.jpg" alt="">
<p>书名:活着</p>
<p>作者:余华</p>
<p>价格:¥66.6</p>
<p>销量:230</p>
<p>库存:1000</p>
<button>加入购物车</button>
</div>
<div href="" class="list-item">
<img src="static/uploads/renyueshenhua.jpg" alt="">
<p>书名:活着</p>
<p>作者:余华</p>
<p>价格:¥66.6</p>
<p>销量:230</p>
<p>库存:1000</p>
<button>加入购物车</button>
</div>-->
</div>
<div class="list-footer">
<div>首页</div>
<div>上一页</div>
<ul><li class="active">1</li><li>2</li><li>3</li></ul>
<div>下一页</div>
<div>末页</div>
<span>共10页</span>
<span>30条记录</span>
<span>到第</span>
<input type="text">
<span>页</span>
<button>确定</button>
</div>
</div>
</div>
</div>
<div class="cate w">
<div class="list">
<a href="" class="list-item">
<i class="iconfont icon-java"></i>
<span>java</span>
</a>
<a href="" class="list-item"
><i class="iconfont icon-h5"></i>h5</a
>
<a href="" class="list-item">
<i class="iconfont icon-python"></i>python
</a>
<a href="" class="list-item"
><i class="iconfont icon-tianchongxing-"></i>pm</a
>
<a href="" class="list-item"
><i class="iconfont icon-php_elephant"></i>php</a
>
<a href="" class="list-item"
><i class="iconfont icon-go"></i>go</a
>
</div>
<a href="" class="img">
<img src="static/uploads/cate4.jpg" alt="" />
</a>
<a href="" class="img">
<img src="static/uploads/cate5.jpg" alt="" />
</a>
<a href="" class="img">
<img src="static/uploads/cate6.jpg" alt="" />
</a>
</div>
<div class="books">
<div class="w">
<div class="seckill">
<div class="seckill-header">
<div class="title">
图书秒杀
</div>
<!-- <i class="iconfont icon-huanyipi"></i> -->
</div>
<div class="seckill-content">
<a href="" class="tip">
<h5>距离结束还有</h5>
<i class="iconfont icon-shandian"></i>
<div class="downcount">
<span class="time">00</span>
<span class="token">:</span>
<span class="time">00</span>
<span class="token">:</span>
<span class="time">00</span>
</div>
</a>
<a href="" class="books-sec">
<img src="static/uploads/congwanqingdaominguo.jpg" alt="">
<p>从晚晴到民国</p>
<div>
<span class="cur-price">¥28.9</span>
<span class="pre-price">¥36.5</span>
</div>
<button>立即购买</button>
</a>
<a href="" class="books-sec">
<img src="static/uploads/cyuyanrumenjingdian.jpg" alt="">
<p>c语言入门经典</p>
<div>
<span class="cur-price">¥55.9</span>
<span class="pre-price">¥68.5</span>
</div>
<button>立即购买</button>
</a>
<a href="" class="books-sec">
<img src="static/uploads/fusang.jpg" alt="">
<p>扶桑</p>
<div>
<span class="cur-price">¥30.9</span>
<span class="pre-price">¥47.5</span>
</div>
<button>立即购买</button>
</a>
<a href="" class="books-sec">
<img src="static/uploads/geihaizideshi.jpg" alt="">
<p>给孩子的诗</p>
<div>
<span class="cur-price">¥18.9</span>
<span class="pre-price">¥25.5</span>
</div>
<button>立即购买</button>
</a>
</ul>
</div>
</div>
</div>
</div>
<div class="bottom">
<div class="w">
<div class="top">
<ul>
<li>
<a href="">
<img src="static/img/bottom1.png" alt="">
<span>大咖级讲师亲自授课</span>
</a>
</li>
<li>
<a href="">
<img src="static/img/bottom.png" alt="">
<span>课程为学员成长持续赋能</span>
</a>
</li>
<li>
<a href="">
<img src="static/img/bottom2.png" alt="">
<span>学员真是情况大公开</span>
</a>
</li>
</ul>
</div>
<div class="content">
<dl>
<dt>关于尚硅谷</dt>
<dd>教育理念</dd>
<!-- <dd>名师团队</dd>
<dd>学员心声</dd> -->
</dl>
<dl>
<dt>资源下载</dt>
<dd>视频下载</dd>
<!-- <dd>资料下载</dd>
<dd>工具下载</dd> -->
</dl>
<dl>
<dt>加入我们</dt>
<dd>招聘岗位</dd>
<!-- <dd>岗位介绍</dd>
<dd>招贤纳师</dd> -->
</dl>
<dl>
<dt>联系我们</dt>
<dd>http://www.com.atguigu.com<dd>
</dl>
</div>
</div>
<div class="down">
尚硅谷书城.Copyright ©2015
</div>
</div>
</div>
<script src="static/script/swiper.min.js"></script>
<script>
var swiper = new Swiper('.swiper-container', {
autoplay: true,
pagination: {
el: '.swiper-pagination',
dynamicBullets: true
},
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev'
}
})
</script>
<script>
const app = new Vue({
el: "#app",
data: {
totalCount: "[[${session.cart==null?0:session.cart.totalCount}]]"
},
methods: {
async addCart(){
//利用event对象获取目标标签的ID属性.
let id = event.target.id
//利用ajax请求实现远程数据获取.
let {data: result} = await axios.get("cart",{params: {method: "addCart",id: id}})
if(result.flag){
alert("新增购物车成功!")
this.totalCount = result.data
//console.log(this.totalCount)
}else{
alert("新增购物车失败!")
}
}
}
})
</script>
</body>
</html>
8.2.2 编辑CartServlet-新增
业务说明: 为了让大家更多的练习面向对象的思想,所有采用Session的方式实现数据存储. 后期大型项目中采用数据库的方式实现购物车数据存储.
package com.atguigu.servlet;
import com.atguigu.bean.Book;
import com.atguigu.bean.Cart;
import com.atguigu.service.BookService;
import com.atguigu.service.impl.BookServiceImpl;
import com.atguigu.vo.SysResult;
import com.fasterxml.jackson.databind.ObjectMapper;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
public class CartServlet extends BaseServlet {
private BookService bookService = new BookServiceImpl();
private static final ObjectMapper MAPPER = new ObjectMapper();
protected void addCart(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.获取图书ID号
int id = Integer.valueOf(request.getParameter("id"));
//2.判断用户是否已经添加该书籍
HttpSession session = request.getSession();
Cart cart = (Cart) request.getSession().getAttribute("cart");
if(cart == null){
//说明: 用户第一次添加购物车信息
cart = new Cart();
session.setAttribute("cart",cart);
}
//根据ID查询图书信息
Book book = bookService.findBookById(id);
//图书添加到购物项中即可
cart.addCart(book);
//获取书的总数量
int totalCount = cart.getTotalCount();
//返回有效数据
String json = MAPPER.writeValueAsString(SysResult.success(totalCount));
response.setContentType("text/html;charset=utf-8");
response.getWriter().write(json);
//检查数据是否添加成功
System.out.println(request.getSession().getAttribute("cart"));
}
}
8.2.3 购物车数量显示
- 业务数据返回
- 数据回显操作
- 编辑页面
8.3 跳转购物车列表页面
8.3.1 编辑页面JS
- 编辑页面超链接
<a href="cart?method=toCartList"
class="cart iconfont icon-gouwuche
"> 购物车
<div class="cart-num" v-text="totalCount"></div>
</a>
8.3.2 编辑CartServlet
protected void toCartList(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.processTemplate("cart/cart",request,response);
}
8.3.3 页面效果展现
8.4 实现购物车列表
8.4.1 编辑购物车列表页面
知识点:
- vue生命周期函数
- ajax请求数据
- Vue中遍历用法
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base th:href="@{/}">
<title>Document</title>
<link rel="stylesheet" href="static/css/minireset.css" />
<link rel="stylesheet" href="static/css/common.css" />
<link rel="stylesheet" href="static/css/cart.css" />
<script src="static/script/vue.js"></script>
<script src="static/script/axios.js"></script>
</head>
<body>
<div id="app">
<div class="header">
<div class="w">
<div class="header-left">
<a href="index.html">
<img src="static/img/logo.gif" alt=""
/></a>
<h1>我的购物车</h1>
</div>
<div class="header-right">
<h3>欢迎<span>张总</span>光临尚硅谷书城</h3>
<div class="order"><a href="order/order.html">我的订单</a></div>
<div class="destory"><a href="index.html">注销</a></div>
<div class="gohome">
<a href="index.html">返回</a>
</div>
</div>
</div>
</div>
<div class="list">
<div class="w">
<table>
<thead>
<tr>
<th>图片</th>
<th>商品名称</th>
<th>数量</th>
<th>单价</th>
<th>金额</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-if="!cartItems.length>0">
<td colspan="6" align="center">
<h3>请添加购物车记录</h3>
</td>
</tr>
<tr v-for="cartItem in cartItems" v-if="cartItems.length>0">
<td>
<img :src="cartItem.book.imgPath" alt="" />
</td>
<td v-text="cartItem.book.title">活着</td>
<td>
<span class="count">-</span>
<input class="count-num" type="text" v-model="cartItem.count"/>
<span class="count">+</span>
</td>
<td v-text="cartItem.book.price">36.8</td>
<td v-text="cartItem.amount">36.8</td>
<td><a href="">删除</a></td>
</tr>
</tbody>
</table>
<div class="footer">
<div class="footer-left">
<a href="#" class="clear-cart">清空购物车</a>
<a href="index.html">继续购物</a>
</div>
<div class="footer-right">
<div>共<span v-text="totalCount"></span>件商品</div>
<div class="total-price">总金额<span v-text="totalAmound"></span>元</div>
<a class="pay" href="checkout.html">去结账</a>
</div>
</div>
</div>
</div>
<div class="bottom">
<div class="w">
<div class="top">
<ul>
<li>
<a href="">
<img src="static/img/bottom1.png" alt="" />
<span>大咖级讲师亲自授课</span>
</a>
</li>
<li>
<a href="">
<img src="static/img/bottom.png" alt="" />
<span>课程为学员成长持续赋能</span>
</a>
</li>
<li>
<a href="">
<img src="static/img/bottom2.png" alt="" />
<span>学员真是情况大公开</span>
</a>
</li>
</ul>
</div>
<div class="content">
<dl>
<dt>关于尚硅谷</dt>
<dd>教育理念</dd>
<!-- <dd>名师团队</dd>
<dd>学员心声</dd> -->
</dl>
<dl>
<dt>资源下载</dt>
<dd>视频下载</dd>
<!-- <dd>资料下载</dd>
<dd>工具下载</dd> -->
</dl>
<dl>
<dt>加入我们</dt>
<dd>招聘岗位</dd>
<!-- <dd>岗位介绍</dd>
<dd>招贤纳师</dd> -->
</dl>
<dl>
<dt>联系我们</dt>
<dd>http://www.com.atguigu.com</dd>
<dd></dd>
</dl>
</div>
</div>
<div class="down">
尚硅谷书城.Copyright ©2015
</div>
</div>
</div>
<script>
const app = new Vue({
el: "#app",
data: {
cartItems: [],
totalCount: 0,
totalAmound: 0
},
methods: {
async getCartList(){
let {data: result} = await axios.get("cart",{params: {method:"getCartList"}})
if(!result.flag){
//如果数据获取失败,则终止数据访问
return
}
if(result.data !== null){
this.cartItems = result.data.cartItems
this.totalCount = result.data.totalCount
this.totalAmound = result.data.totalAmound
}
}
},
created(){
this.getCartList();
}
})
</script>
</body>
</html>
8.4.2 编辑CartServlet
protected void getCartList(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//从session域中获取购物车记录
Cart cart = (Cart) request.getSession().getAttribute("cart");
String json = MAPPER.writeValueAsString(SysResult.success(cart));
response.setContentType("text/html;charset=utf-8");
response.getWriter().write(json);
}
12345678
8.4.3 页面结构展现
8.5 清空购物车功能
8.5.1 编辑页面
<div class="footer-left">
<a href="cart/clearCart" class="clear-cart" @click.prevent="clearCart">清空购物车</a>
<a href="index.html">继续购物</a>
</div>
8.5.2 编辑页面JS
- 重构页面JS
async clearCart(){
let {data: result} = await axios.get("cart",{params:{method:"clearCart"}})
console.log(result)
if(result.flag){
//如果业务执行成功,则更新列表
this.getCartList();
}else{
//否则提示用户清空失败
alert("清空购物车失败!")
}
}
8.6 删除购物项
8.6.1 编辑页面JS
<td><a href="#" @click.prevent="deleteCartItem(cartItem.book.id)">删除</a></td>
async deleteCartItem(id){
let deleteFlag = confirm("确定要删除该数据吗?");
if(!deleteFlag){
//表示用户点击的取消 则终止程序
return
}
let {data: result} = await axios.get("cart?method=deleteCartById&id="+id)
if(result.flag){ //如果删除成功,重新加载数据
this.getCartList();
}else{
//否则:提示用户删除
alert("删除购物项失败!!")
}
}
8.6.2 编辑CartServlet
/**
* 完成购物项删除操作
*/
protected void deleteCartById(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
int id = Integer.valueOf(request.getParameter("id"));
Cart cart = (Cart) request.getSession().getAttribute("cart");
cart.deleteCartItem(id);
//实现购物项删除
response.setContentType("text/html;charset=utf-8");
response.getWriter().write(MAPPER.writeValueAsString(SysResult.success()));
}
8.6.3 编辑Cart对象
public void deleteCartItem(Integer id){
this.map.remove(id);
}
8.7 购物车数量修改
8.7.1 购物车数量修改
<td>
<span class="count" @click="subCartItemNum(cartItem)">-</span>
<input class="count-num" type="text" v-model="cartItem.count" @change="updateCartItemNum(cartItem)"/>
<span class="count" @click="addCartItemNum(cartItem)">+</span>
</td>
编辑页面JS
addCartItemNum(cartItem){
cartItem.count += 1
this.updateCartItemNum(cartItem)
},
subCartItemNum(cartItem){
if(cartItem.count === 1){
this.deleteCartItem(cartItem.book.id)
}else{
cartItem.count -= 1
this.updateCartItemNum(cartItem)
}
},
async updateCartItemNum(cartItem){
let rege = /^[1-9][0-9]*$/
if(!rege.test(cartItem.count)){
alert("请输入大于1的整数")
return
}
//实现购物车数量的操作
let{data: result} = await axios.get("cart",{params: {
method: "updateCartItemNum",
id: cartItem.book.id,
count: cartItem.count
}})
if(result.flag){
//重新刷新列表
this.getCartList();
}else{
alert("修改数量失败")
}
}
8.7.2 编辑CartServlet
//updateCartItemNum
protected void updateCartItemNum(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
int id = Integer.valueOf(request.getParameter("id"));
int count = Integer.valueOf(request.getParameter("count"));
Cart cart = (Cart) request.getSession().getAttribute("cart");
cart.updateCartItemCount(id,count);
//实现购物项数量更新操作
response.setContentType("text/html;charset=utf-8");
response.getWriter().write(MAPPER.writeValueAsString(SysResult.success()));
}
8.7.3 编辑Cart
说明: 为Cart添加修改方法.
public void updateCartItemCount(Integer id, Integer count){
this.map.get(id).setCount(count);
}
8.9 价格精度计算
8.9.1 BigDecimal用法
1.构造方法
BigDecimal(int) 创建一个具有参数所指定整数值的对象。
BigDecimal(double) 创建一个具有参数所指定双精度值的对象。 //不推荐使用 不能保证用户输入数据的精度
BigDecimal(long) 创建一个具有参数所指定长整数值的对象。
BigDecimal(String) 创建一个具有参数所指定以字符串表示的数值的对象。//推荐使用
2.常用方法
- add(BigDecimal) BigDecimal对象中的值相加,然后返回这个对象。
- subtract(BigDecimal) BigDecimal对象中的值相减,然后返回这个对象。
- multiply(BigDecimal) BigDecimal对象中的值相乘,然后返回这个对象。
- divide(BigDecimal) BigDecimal对象中的值相除,然后返回这个对象。
- toString() 将BigDecimal对象的数值转换成字符串。
- doubleValue() 将BigDecimal对象中的值以双精度数返回。
- floatValue() 将BigDecimal对象中的值以单精度数返回。
- longValue() 将BigDecimal对象中的值以长整数返回。
- intValue() 将BigDecimal对象中的值以整数返回。
8.9.2 修改代码中的计算
- 重构CartItem对象
//总价: 书单价*数量
public CartItem(Book book,Integer count){
this.book = book;
this.count = count;
//防止计算进度问题
BigDecimal priceDec = new BigDecimal(book.getPrice()+"");
BigDecimal countDec = new BigDecimal(count+"");
this.amount = priceDec.multiply(countDec).doubleValue();
}
public void setBook(Book book) {
this.book = book;
BigDecimal priceDec = new BigDecimal(book.getPrice()+"");
BigDecimal countDec = new BigDecimal(this.count+"");
//防止计算进度问题
this.amount = priceDec.multiply(countDec).doubleValue();
}
public void setCount(Integer count) {
this.count = count;
BigDecimal priceDec = new BigDecimal(this.book.getPrice()+"");
BigDecimal countDec = new BigDecimal(count+"");
//防止计算进度问题
this.amount = priceDec.multiply(countDec).doubleValue();
}
- 重构Cart对象
//获取总价格
public Double getTotalAmound(){
Collection<CartItem> cartItems = map.values();
BigDecimal totalAmoundDec = new BigDecimal("0");
for (CartItem cartItem : cartItems){
BigDecimal amountDec = new BigDecimal(cartItem.getAmount()+"");
//每项总价: 价格* 数量
totalAmoundDec = totalAmoundDec.add(amountDec);
}
//计算购物车商品总价
this.totalAmound = totalAmoundDec.doubleValue();
return this.totalAmound;
}
9 过滤器
9.1 Filter的概念
Filter:一个实现了特殊接口(Filter)的Java类. 实现对请求资源(jsp,servlet,html,)的过滤的功能. 过滤器是一个运行在服务器的程序, 优先于请求资源(Servlet或者jsp,html)之前执行. 过滤器是javaweb技术中最为实用的技术之一
9.2 Filter的作用
Filter的作用是对目标资源(Servlet,jsp)进行过滤,其应用场景有: 登录权限检查,解决网站乱码,过滤敏感字符等等
9.3 Filter的入门案例
9.3.1 案例目标
实现在请求到达ServletDemo01之前解决请求参数的中文乱码
9.3.2 编辑web.xml
<servlet>
<servlet-name>servletDemo01</servlet-name>
<servlet-class>com.atguigu.ServletDemo01</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servletDemo01</servlet-name>
<url-pattern>/ServletDemo01</url-pattern>
</servlet-mapping>
9.3.2 创建ServletDemo01
package com.atguigu.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class ServletDemo01 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
System.out.println("ServletDemo01接收到了一个请求..."+username);
}
}
9.3.3 前端页面代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<form action="/webday12/demo01" method="post">
用户名<input type="text" name="username"/><br/>
<input type="submit"/>
</form>
</body>
</html>
9.3.4 创建EncodingFilter
- 编辑web.xml
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>com.atguigu.filter.EncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<!--url-pattern表示指定拦截哪些资源-->
<url-pattern>/demo01</url-pattern>
</filter-mapping>
2.编辑过滤器代码
package com.atguigu.filter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* 日期2021-05-18 08:56
* 编写过滤器的步骤:
* 1. 写一个类实现Filter接口,并且重写方法
* 2. 在web.xml中配置该过滤器的拦截路径
*/
public class EncodingFilter implements Filter {
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//解决请求参数的乱码
HttpServletRequest request = (HttpServletRequest) req;
request.setCharacterEncoding("UTF-8");
//每次有请求被当前filter接收到的时候,就会执行doFilter进行过滤处理
System.out.println("EncodingFilter接收到了一个请求...");
//这句代码表示放行
chain.doFilter(req, resp);
}
@Override
public void init(FilterConfig config) throws ServletException {
}
}
9.4 Filter的生命周期
9.4.1 回顾Servlet生命周期
9.4.1.1 Servlet的创建时机
Servlet默认在第一次接收请求的时候创建,我们可以通过<load-on-startup>
标签配置Servlet在服务器启动的时候创建
9.4.1.2 Servlet的销毁时机
Servlet会在服务器关闭或者将项目从服务器上移除的时候销毁
9.4.2 Filter的生命周期和生命周期方法
生命周期阶段 | 执行时机 | 生命周期方法 |
---|---|---|
创建对象 | Web应用启动时 | init方法,通常在该方法中做初始化工作 |
拦截请求 | 接收到匹配的请求 | doFilter方法,通常在该方法中执行拦截过滤 |
销毁 | Web应用卸载前 | destroy方法,通常在该方法中执行资源释放 |
9.5 过滤器匹配规则
9.5.1 过滤器匹配的目的
过滤器匹配的目的是指定当前过滤器要拦截哪些资源
9.5.2 四种匹配规则
9.5.2.1 精确匹配
指定被拦截资源的完整路径:
<!-- 配置Filter要拦截的目标资源 -->
<filter-mapping>
<!-- 指定这个mapping对应的Filter名称 -->
<filter-name>FilterDemo01</filter-name>
<!-- 通过请求地址模式来设置要拦截的资源 -->
<url-pattern>/demo01</url-pattern>
</filter-mapping>
上述例子表示要拦截映射路径为/demo01
的这个资源
9.5.2.2 模糊匹配
相比较精确匹配,使用模糊匹配可以让我们创建一个Filter就能够覆盖很多目标资源,不必专门为每一个目标资源都创建Filter,提高开发效率。
在我们配置了url-pattern为/user/*之后,请求地址只要是/user开头的那么就会被匹配。
<filter-mapping>
<filter-name>Target02Filter</filter-name>
<!-- 模糊匹配:前杠后星 -->
<!--
/user/demo01
/user/demo02
/user/demo03
/demo04
-->
<url-pattern>/user/*</url-pattern>
</filter-mapping>
极端情况:/*匹配所有请求
9.5.2.3 扩展名匹配
<filter>
<filter-name>Target04Filter</filter-name>
<filter-class>com.atguigu.filter.filter.Target04Filter</filter-class>
</filter>
<filter-mapping>
<filter-name>Target04Filter</filter-name>
<url-pattern>*.png</url-pattern>
</filter-mapping>
上述例子表示拦截所有以.png
结尾的请求
9.6 过滤器链
9.6.1 过滤链的概念
一个请求可能被多个过滤器所过滤,只有当所有过滤器都放行,请求才能到达目标资源,如果有某一个过滤器没有放行,那么请求则无法到达后续过滤器以及目标资源,多个过滤器组成的链路就是过滤器链
9.6.2 过滤器链的顺序
过滤器链中每一个Filter执行的 顺序是由web.xml中filter-mapping配置的顺序决定的。如果某个Filter是使用ServletName进行匹配规则的配置,那么这个Filter执行的优先级要更低
9.6.3 过滤器链案例
9.6.3.1 创建ServletDemo01
web.xml代码
<servlet>
<servlet-name>servletDemo01</servlet-name>
<servlet-class>com.atguigu.ServletDemo01</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servletDemo01</servlet-name>
<url-pattern>/ServletDemo01</url-pattern>
</servlet-mapping>
ServletDemo01代码
public class ServletDemo01 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("ServletDemo01接收到了请求...");
}
}
9.6.3.2 创建多个Filter拦截Servlet
<filter-mapping>
<filter-name>TargetChain03Filter</filter-name>
<url-pattern>/Target05Servlet</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>TargetChain02Filter</filter-name>
<url-pattern>/Target05Servlet</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>TargetChain01Filter</filter-name>
<url-pattern>/Target05Servlet</url-pattern>
</filter-mapping>
10 监听器
10.1 监听器介绍
10.1.1 监听器概念
监听器:专门用于对其他对象身上发生的事件或状态改变进行监听和相应处理的对象,当被监视的对象发生情况时,立即采取相应的行动。
Servlet监听器:Servlet规范中定义的一种特殊类,它用于监听Web应用程序中的ServletContext,HttpSession 和HttpServletRequest等域对象的创建与销毁事件,以及监听这些域对象中的属性发生修改的事件。
10.1.2 Servlet监听器的分类(了解)
10.1.2.1 ServletContextListener
作用:监听ServletContext对象的创建与销毁
方法名 | 作用 |
---|---|
contextInitialized(ServletContextEvent sce) | ServletContext创建时调用 |
contextDestroyed(ServletContextEvent sce) | ServletContext销毁时调用 |
ServletContextEvent对象代表从ServletContext对象身上捕获到的事件,通过这个事件对象我们可以获取到ServletContext对象。
10.1.2.2 HttpSessionListener
作用:监听HttpSession对象的创建与销毁
方法名 | 作用 |
---|---|
sessionCreated(HttpSessionEvent hse) | HttpSession对象创建时调用 |
sessionDestroyed(HttpSessionEvent hse) | HttpSession对象销毁时调用 |
HttpSessionEvent对象代表从HttpSession对象身上捕获到的事件,通过这个事件对象我们可以获取到触发事件的HttpSession对象。
10.1.2.3 ServletRequestListener
作用:监听ServletRequest对象的创建与销毁
方法名 | 作用 |
---|---|
requestInitialized(ServletRequestEvent sre) | ServletRequest对象创建时调用 |
requestDestroyed(ServletRequestEvent sre) | ServletRequest对象销毁时调用 |
ServletRequestEvent对象代表从HttpServletRequest对象身上捕获到的事件,通过这个事件对象我们可以获取到触发事件的HttpServletRequest对象。另外还有一个方法可以获取到当前Web应用的ServletContext对象。
10.1.2.4 ServletContextAttributeListener
作用:监听ServletContext中属性的添加、移除和修改
方法名 | 作用 |
---|---|
attributeAdded(ServletContextAttributeEvent scab) | 向ServletContext中添加属性时调用 |
attributeRemoved(ServletContextAttributeEvent scab) | 从ServletContext中移除属性时调用 |
attributeReplaced(ServletContextAttributeEvent scab) | 当ServletContext中的属性被修改时调用 |
ServletContextAttributeEvent对象代表属性变化事件,它包含的方法如下:
方法名 | 作用 |
---|---|
getName() | 获取修改或添加的属性名 |
getValue() | 获取被修改或添加的属性值 |
getServletContext() | 获取ServletContext对象 |
10.1.2.5 HttpSessionAttributeListener
作用:监听HttpSession中属性的添加、移除和修改
方法名 | 作用 |
---|---|
attributeAdded(HttpSessionBindingEvent se) | 向HttpSession中添加属性时调用 |
attributeRemoved(HttpSessionBindingEvent se) | 从HttpSession中移除属性时调用 |
attributeReplaced(HttpSessionBindingEvent se) | 当HttpSession中的属性被修改时调用 |
HttpSessionBindingEvent对象代表属性变化事件,它包含的方法如下:
方法名 | 作用 |
---|---|
getName() | 获取修改或添加的属性名 |
getValue() | 获取被修改或添加的属性值 |
getSession() | 获取触发事件的HttpSession对象 |
10.1.2.6 ServletRequestAttributeListener
作用:监听ServletRequest中属性的添加、移除和修改
方法名 | 作用 |
---|---|
attributeAdded(ServletRequestAttributeEvent srae) | 向ServletRequest中添加属性时调用 |
attributeRemoved(ServletRequestAttributeEvent srae) | 从ServletRequest中移除属性时调用 |
attributeReplaced(ServletRequestAttributeEvent srae) | 当ServletRequest中的属性被修改时调用 |
ServletRequestAttributeEvent对象代表属性变化事件,它包含的方法如下:
方法名 | 作用 |
---|---|
getName() | 获取修改或添加的属性名 |
getValue() | 获取被修改或添加的属性值 |
getServletRequest () | 获取触发事件的ServletRequest对象 |
10.1.2.7 HttpSessionBindingListener
作用:监听某个对象在Session域中的创建与移除
方法名 | 作用 |
---|---|
valueBound(HttpSessionBindingEvent event) | 该类的实例被放到Session域中时调用 |
valueUnbound(HttpSessionBindingEvent event) | 该类的实例从Session中移除时调用 |
HttpSessionBindingEvent对象代表属性变化事件,它包含的方法如下:
方法名 | 作用 |
---|---|
getName() | 获取当前事件涉及的属性名 |
getValue() | 获取当前事件涉及的属性值 |
getSession() | 获取触发事件的HttpSession对象 |
10.2 ServletContextListener的使用
10.2.1 作用
ServletContextListener是监听ServletContext对象的创建和销毁的,因为ServletContext对象是在服务器启动的时候创建、在服务器关闭的时候销毁,所以ServletContextListener也可以监听服务器的启动和关闭
10.2.2 使用场景
将来学习SpringMVC的时候,会用到一个ContextLoaderListener,这个监听器就实现了ServletContextListener接口,表示对ServletContext对象本身的生命周期进行监控。
10.2.3 代码实现
10.2.3.1 创建监听器类
package com.atguigu.listener;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
/**
* 包名:com.atguigu.listener
*
* 日期2021-06-19 10:26
* 编写监听器的步骤:
* 1. 写一个类实现对应的:Listener的接口(我们这里使用的是ServletContextListener),并且实现它里面的方法
* 1.1 contextInitialized()这个方法在ServletContext对象被创建出来的时候执行,也就是说在服务器启动的时候执行
* 1.2 contextDestroyed()这个方法会在ServletContext对象被销毁的时候执行,也就是说在服务器关闭的时候执行
*
* 2. 在web.xml中注册(配置)监听器
*/
public class ContextLoaderListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("在服务器启动的时候,模拟创建SpringMVC的核心容器...");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("在服务器启动的时候,模拟销毁SpringMVC的核心容器...");
}
}
10.2.3.2 注册监听器
<listener>
<listener-class>com.atguigu.listener.ContextLoaderListener</listener-class>
</listener>
11 WEB组件注解用法
11.1 Servlet组件注解
package com.atguigu.servlet;
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;
//name:servlet名称可以省略 默认为类名
//value 表示对应的url路径
@WebServlet(name = "AnnoServlet",value = "/anno")
public class AnnoServlet extends HttpServlet {
@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 {
System.out.println("我是注解方式的Servlet");
}
}
11.2 Filter组件注解
package com.atguigu.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter(filterName = "AnnoFilter",value = "/*")
public class AnnoFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("过滤器初始化");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("过滤器执行前");
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("过滤器执行后");
}
@Override
public void destroy() {
System.out.println("容器对象销毁");
}
}
11.3 Listener组件注解
package com.atguigu.listener;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
@WebListener
public class ContextListerner implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
System.out.println("容器初始化操作");
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
System.out.println("容器销毁操作");
}
}
11.4 全局字符集处理
说明: 利用Filter过滤器 实现全局字符集处理
package com.atguigu.filter;/**
* @className EncodingFilter
* @description TODO
* @author 刘昱江
* @date 2022/4/1 9:23
*/
import javafx.application.Application;
import javafx.stage.Stage;
import javax.naming.Name;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter(filterName = "EncodingFilter",value = "/*")
public class EncodingFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
//解决请求乱码问题
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//解决post请求乱码问题
servletRequest.setCharacterEncoding("utf-8");
servletResponse.setCharacterEncoding("utf-8");
servletResponse.setContentType("text/html;charset=utf-8");
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
}
}
12 订单模块实现
12.1 订单业务
- 订单结构如图所示:
- 结构类比
12.2 订单表设计
12.2.1 创建订单表
CREATE TABLE t_order(
order_id INT PRIMARY KEY AUTO_INCREMENT,
order_sequence VARCHAR(200),
create_time VARCHAR(100),
total_count INT,
total_amount DOUBLE,
order_status INT,
user_id INT
);
字段名 | 字段作用 |
---|---|
order_id | 主键 |
order_sequence | 订单号 |
create_time | 订单创建时间 |
total_count | 订单的总数量 |
total_amount | 订单的总金额 |
order_status | 订单的状态 |
user_id | 下单的用户的id |
- 虽然order_sequence也是一个不重复的数值,但是不使用它作为主键。数据库表的主键要使用没有业务功能的字段来担任。
- 订单的状态
- 待支付(书城项目中暂不考虑)
- 已支付,待发货:0
- 已发货:1
- 确认收货:2
- 发起退款或退货(书城项目中暂不考虑)
- 用户id
- 从逻辑和表结构的角度来说,这其实是一个外键。
- 但是开发过程中建议先不要加外键约束:因为开发过程中数据尚不完整,加了外键约束开发过程中使用测试数据非常不方便,建议项目预发布时添加外键约束测试。
12.2.2 创建订单项
CREATE TABLE t_order_item(
item_id INT PRIMARY KEY AUTO_INCREMENT,
book_name VARCHAR(20),
price DOUBLE,
img_path VARCHAR(50),
item_count INT,
item_amount DOUBLE,
order_id VARCHAR(20)
);
字段名称 | 字段作用 |
---|---|
item_id | 主键 |
book_name | 书名 |
price | 单价 |
item_count | 当前订单项的数量 |
item_amount | 当前订单项的金额 |
order_id | 当前订单项关联的订单表的主键 |
说明:book_name、author、price这三个字段其实属于t_book表,我们把它们加入到t_order_item表中,其实并不符合数据库设计三大范式。这里做不符合规范的操作的原因是:将这几个字段加入当前表就不必在显示数据时和t_book表做关联查询,提高查询的效率,这是一种变通的做法。
12.3 创建实体类
12.3.1 创建Order类
package com.atguigu.bean;
import java.io.Serializable;
/**
* @author 刘昱江
* @className Order
* @description TODO
* @date 2022/4/1 10:56
*/
public class Order implements Serializable {
private Integer orderId; //订单ID主键自增
private String orderSequence; //订单编号
private String createTime; //创建订单时间
private Integer totalCount; //订单总数
private Double totalAmount; //订单总价
private Integer orderStatus; //订单状态
private Integer userId; //用户ID
@Override
public String toString() {
return "Order{" +
"orderId=" + orderId +
", orderSequence='" + orderSequence + '\'' +
", createTime='" + createTime + '\'' +
", totalCount='" + totalCount + '\'' +
", totalAmount='" + totalAmount + '\'' +
", orderStatus=" + orderStatus +
", userId=" + userId +
'}';
}
public Order() {
}
public Order(Integer orderId, String orderSequence, String createTime, Integer totalCount, Double totalAmount, Integer orderStatus, Integer userId) {
this.orderId = orderId;
this.orderSequence = orderSequence;
this.createTime = createTime;
this.totalCount = totalCount;
this.totalAmount = totalAmount;
this.orderStatus = orderStatus;
this.userId = userId;
}
public Integer getOrderId() {
return orderId;
}
public void setOrderId(Integer orderId) {
this.orderId = orderId;
}
public String getOrderSequence() {
return orderSequence;
}
public void setOrderSequence(String orderSequence) {
this.orderSequence = orderSequence;
}
public String getCreateTime() {
return createTime;
}
public void setCreateTime(String createTime) {
this.createTime = createTime;
}
public Integer getTotalCount() {
return totalCount;
}
public void setTotalCount(Integer totalCount) {
this.totalCount = totalCount;
}
public Double getTotalAmount() {
return totalAmount;
}
public void setTotalAmount(Double totalAmount) {
this.totalAmount = totalAmount;
}
public Integer getOrderStatus() {
return orderStatus;
}
public void setOrderStatus(Integer orderStatus) {
this.orderStatus = orderStatus;
}
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
}
12.3.2 创建OrderItem类
package com.atguigu.bean;
import java.io.Serializable;
/**
* @author 刘昱江
* @className OrderItem
* @description TODO
* @date 2022/4/1 10:58
*/
public class OrderItem implements Serializable{
private Integer itemId; //订单编号
private String bookName; //图书名称
private Double price; //图书价格
private String imgPath; //图片地址
private Integer itemCount; //购买数量
private Double itemAmount; //购买金额
private Integer orderId; //订单编号 与Order表保持一致.
@Override
public String toString() {
return "OrderItem{" +
"itemId=" + itemId +
", bookName='" + bookName + '\'' +
", price=" + price +
", imgPath='" + imgPath + '\'' +
", itemCount=" + itemCount +
", itemAmount=" + itemAmount +
", orderId=" + orderId +
'}';
}
public OrderItem() {
}
public OrderItem(Integer itemId, String bookName, Double price, String imgPath, Integer itemCount, Double itemAmount, Integer orderId) {
this.itemId = itemId;
this.bookName = bookName;
this.price = price;
this.imgPath = imgPath;
this.itemCount = itemCount;
this.itemAmount = itemAmount;
this.orderId = orderId;
}
public Integer getItemId() {
return itemId;
}
public void setItemId(Integer itemId) {
this.itemId = itemId;
}
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
public String getImgPath() {
return imgPath;
}
public void setImgPath(String imgPath) {
this.imgPath = imgPath;
}
public Integer getItemCount() {
return itemCount;
}
public void setItemCount(Integer itemCount) {
this.itemCount = itemCount;
}
public Double getItemAmount() {
return itemAmount;
}
public void setItemAmount(Double itemAmount) {
this.itemAmount = itemAmount;
}
public Integer getOrderId() {
return orderId;
}
public void setOrderId(Integer orderId) {
this.orderId = orderId;
}
}
12.3 订单新增业务实现
12.3.1 业务模块说明
业务说明: 如果点击去结算 需要完成如下操作
- 订单数据入库
- 订单商品数据入库
- 图书销量/库存量修改
12.3.2 编辑页面JS
<a class="pay" href="order?method=addOrder">去结账</a>
12.3.3 编辑OrderServlet
完成订单新增操作,返回订单编号即可
@WebServlet(name = "OrderServlet", value = "/order")
public class OrderServlet extends BaseServlet {
private OrderService orderService = new OrderServiceImpl();
//实现订单入库操作
protected void addOrder(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.获取购物车信息
Cart cart = (Cart) request.getSession().getAttribute("cart");
//2.获取当前用户信息
User user = (User) request.getSession().getAttribute("user");
//3.成功之后将页面转向到成功页面,返回order编号
String orderSequence = orderService.addOrder(cart,user);
//4.将购物车清空
request.getSession().removeAttribute("cart");
//5.将数据返回
request.setAttribute("orderSequence",orderSequence);
this.processTemplate("cart/checkout",request,response);
}
}
12.3.4 编辑OrderService
- 编辑OrderService接口 添加新增订单方法
package com.atguigu.service;
import com.atguigu.bean.Cart;
import com.atguigu.bean.Order;
import com.atguigu.bean.User;
import java.util.List;
/**
* @author 刘昱江
* @className OrderService
* @description TODO
* @date 2022/4/1 11:08
*/
public interface OrderService {
String addOrder(Cart cart, User user);
}
- 编辑OrderService实现类 添加新增订单方法
package com.atguigu.service.impl;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleDropDbLinkStatement;
import com.atguigu.bean.*;
import com.atguigu.dao.BookDao;
import com.atguigu.dao.OrderDao;
import com.atguigu.dao.OrderItemDao;
import com.atguigu.dao.impl.BookDaoImpl;
import com.atguigu.dao.impl.OrderDaoImpl;
import com.atguigu.dao.impl.OrderItemDaoImpl;
import com.atguigu.service.OrderService;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.List;
public class OrderServiceImpl implements OrderService {
private OrderDao orderDao = new OrderDaoImpl();
private OrderItemDao orderItemDao = new OrderItemDaoImpl();
private BookDao bookDao = new BookDaoImpl();
@Override
public String addOrder(Cart cart, User user) {
//1.封装Order对象
Order order = new Order();
String sequence = System.currentTimeMillis()+"";
order.setOrderSequence(sequence); //生成当前时间毫秒数
order.setOrderStatus(0); //默认都是未支付
String date = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date());
order.setCreateTime(date); //设定格式化时间
order.setTotalAmount(cart.getTotalAmound()); //设定购物总金额
order.setTotalCount(cart.getTotalCount()); //设定购物总数
order.setUserId(user.getId()); //设定用户ID号
orderDao.addOrder(order); //实现订单新增
//获取入库之后的主键信息
int orderId = orderDao.getOrderIdBySequence(sequence);
//2.实现ItemOrder赋值
Collection<CartItem> cartItems = cart.getCartItems();
for (CartItem cartItem : cartItems){
OrderItem orderItem = new OrderItem();
Book book = cartItem.getBook();
orderItem.setOrderId(orderId);
orderItem.setBookName(book.getTitle());
orderItem.setItemId(book.getId());
orderItem.setPrice(book.getPrice());
orderItem.setImgPath(book.getImgPath());
orderItem.setItemAmount(cartItem.getAmount());
orderItem.setItemCount(cartItem.getCount());
orderItemDao.addOrderItem(orderItem);
//3.更新图书的库存量和销售量 获取用户的购买信息
int num = cartItem.getCount();
book.setSales(book.getSales()+num); //增加销量
book.setStock(book.getStock()-num); //减少库存
bookDao.updateBookById(book);
}
return sequence;
}
}
12.3.5 编辑OrderDao
- 编辑OrderDao接口
- 编辑OrderDaoImpl
package com.atguigu.dao.impl;
import com.atguigu.bean.Order;
import com.atguigu.dao.OrderDao;
import java.util.List;
/**
* @author 刘昱江
* @className OrderDaoImpl
* @description TODO
* @date 2022/4/1 11:09
*/
public class OrderDaoImpl extends BaseDaoImpl implements OrderDao {
@Override
public void addOrder(Order order) {
String sql = "insert into t_order values (null,?,?,?,?,?,?)";
this.update(sql,order.getOrderSequence(),order.getCreateTime(),order.getTotalCount(),order.getTotalAmount(),order.getOrderStatus(),order.getUserId());
}
@Override
public int getOrderIdBySequence(String sequence) {
String sql = "select order_id orderId from t_order where order_sequence=?";
return (int) this.getValue(sql,sequence);
}
}
12.3.6 编辑OrderItemDao
- 编辑接口
- 编辑实现类
package com.atguigu.dao.impl;
import com.atguigu.bean.OrderItem;
import com.atguigu.dao.OrderItemDao;
public class OrderItemDaoImpl extends BaseDaoImpl implements OrderItemDao {
@Override
public void addOrderItem(OrderItem orderItem) {
String sql = "insert into t_order_item values (null,?,?,?,?,?,?)";
this.update(sql,orderItem.getBookName(),
orderItem.getPrice(),
orderItem.getImgPath(),
orderItem.getItemCount(),
orderItem.getItemAmount(),
orderItem.getOrderId());
}
}
12.3.7 编辑页面
说明:跳转到checkout.html
<!DOCTYPE html >
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<base th:href="@{/}">
<title>结算页面</title>
<link type="text/css" rel="stylesheet" href="static/css/style.css" >
<link rel="stylesheet" href="static/css/minireset.css" />
<link rel="stylesheet" href="static/css/common.css" />
<link rel="stylesheet" href="static/css/cart.css" />
<style type="text/css">
h1 {
text-align: center;
margin-top: 200px;
font-size: 26px;
}
.oid{
color: red;
font-weight: bolder;
}
</style>
</head>
<body>
<!-- <div id="header">-->
<!-- <img class="logo_img" alt="" src="static/img/logo.gif" >-->
<!-- <span class="wel_word">结算</span>-->
<!-- <div>-->
<!-- <span>欢迎<span class="um_span">张总</span>光临尚硅谷书城</span>-->
<!-- <a href="order/order.html">我的订单</a>-->
<!-- <a href="index.html">注销</a> -->
<!-- <a href="index.html">返回</a>-->
<!-- </div>-->
<!-- </div>-->
<div class="header">
<div class="w">
<div class="header-left">
<a href="index.html">
<img src="static/img/logo.gif" alt=""
/></a>
<span>我的购物车</span>
</div>
<div class="header-right" th:include="base/loginSuccess::loginSuccess">
<!--<h3>欢迎<span th:text="${session.user.username}">张总</span>光临尚硅谷书城</h3>
<div class="order"><a href="order?method=findOrderList">我的订单</a></div>
<a href="user?method=logout" class="register">注销</a>
<div class="gohome">
<a href="index.html">返回</a>
</div>-->
</div>
</div>
</div>
<div id="main">
<h1>你的订单已结算,订单号为:<span class="oid" th:text="${orderSequence}">546845626455846</span></h1>
</div>
<div id="bottom">
<span>
尚硅谷书城.Copyright ©2015
</span>
</div>
</body>
</html>
12.4 订单列表展现
12.4.1 编辑页面
说明: 当点击我的订单按钮 实现页面跳转
<div class="header-right" th:fragment="loginSuccess">
<h3>欢迎<span th:text="${session.user.username}">张总</span>光临尚硅谷书城</h3>
<div class="order"><a href="order?method=findOrderList">我的订单</a></div>
<div class="destory"><a href="user?method=logout">注销</a></div>
<div class="gohome">
<a href="index.html">返回</a>
</div>
</div>
12.4.2 编辑OrderServlet
业务说明: gen
//实现订单入库操作
protected void findOrderList(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.获取用户信息
User user = (User) request.getSession().getAttribute("user");
List<Order> orderList = orderService.findOrderList(user.getId());
request.setAttribute("orderList",orderList);
this.processTemplate("order/order",request,response);
}
12.4.3 编辑OrderService
- 编辑接口
List<Order> findOrderList(Integer userId);
- 编辑接口实现类
@Override
public List<Order> findOrderList(Integer userId) {
return orderDao.findOrderList(userId);
}
12.4.4 编辑OrderDao
- 编辑OrderDao接口
List<Order> findOrderList(Integer userId);
- 编辑OrderDaoImpl实现类
@Override
public List<Order> findOrderList(Integer userId) {
String sql = "SELECT order_id orderId,order_sequence orderSequence,create_time createTime,total_count totalCount,total_amount totalAmount, order_status orderStatus,user_id userId FROM t_order where user_id=?";
return this.getList(Order.class,sql,userId);
}
12.4.5 编辑页面
核心代码:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base th:href="@{/}">
<title>我的订单</title>
<link rel="stylesheet" href="static/css/minireset.css" />
<link rel="stylesheet" href="static/css/common.css" />
<link rel="stylesheet" href="static/css/cart.css" />
<link rel="stylesheet" href="static/css/bookManger.css" />
<link rel="stylesheet" href="static/css/orderManger.css" />
</head>
<body>
<div class="header">
<div class="w">
<div class="header-left">
<a href="index.html">
<img src="static/img/logo.gif" alt=""
/></a>
<h1>我的订单</h1>
</div>
<div class="header-right" th:include="base/loginSuccess::loginSuccess">
<!--<h3>欢迎<span th:text="${session.user.username}">张总</span>光临尚硅谷书城</h3>
<div class="order"><a href="order/order.html">我的订单</a></div>
<div class="destory"><a href="user?method=logout" class="register">注销</a></div>
<div class="gohome">
<a href="index.html">返回</a>
</div>-->
</div>
</div>
</div>
<div class="list">
<div class="w">
<table>
<thead>
<tr>
<th>订单号</th>
<th>订单日期</th>
<th>订单金额</th>
<th>订单数量</th>
<th>订单状态</th>
<th>订单详情</th>
</tr>
</thead>
<tbody>
<tr th:each="order,status : ${orderList}">
<td th:text="${order.orderId}">12354456895</td>
<td th:text="${order.createTime}">
2015.04.23
</td>
<td th:text="${order.totalAmount}">90.00</td>
<td th:text="${order.totalCount}">88</td>
<td>
<a href="" class="send" th:if="${order.orderStatus==0}">等待发货</a>
<a href="" class="send" th:if="${order.orderStatus==1}">已发货</a>
<a href="" class="send" th:if="${order.orderStatus==2}">确认收货</a>
</td>
<td><a href="">查看详情</a></td>
</tr>
<!--<tr>
<td>12354456895</td>
<td>
2015.04.23
</td>
<td>90.00</td>
<td>88</td>
<td><a href="" class="send">未发货</a></td>
<td><a href="">查看详情</a></td>
</tr>
<tr>
<td>12354456895</td>
<td>
2015.04.23
</td>
<td>90.00</td>
<td>88</td>
<td><a href="" class="send">已发货</a></td>
<td><a href="">查看详情</a></td>
</tr>-->
</tbody>
</table>
<div class="footer">
<div class="footer-right">
<div>首页</div>
<div>上一页</div>
<ul>
<li class="active">1</li>
<li>2</li>
<li>3</li>
</ul>
<div>下一页</div>
<div>末页</div>
<span>共10页</span>
<span>30条记录</span>
<span>到第</span>
<input type="text" />
<span>页</span>
<button>确定</button>
</div>
</div>
</div>
</div>
<div class="bottom">
<div class="w">
<div class="top">
<ul>
<li>
<a href="">
<img src="static/img/bottom1.png" alt="" />
<span>大咖级讲师亲自授课</span>
</a>
</li>
<li>
<a href="">
<img src="static/img/bottom.png" alt="" />
<span>课程为学员成长持续赋能</span>
</a>
</li>
<li>
<a href="">
<img src="static/img/bottom2.png" alt="" />
<span>学员真是情况大公开</span>
</a>
</li>
</ul>
</div>
<div class="content">
<dl>
<dt>关于尚硅谷</dt>
<dd>教育理念</dd>
<!-- <dd>名师团队</dd>
<dd>学员心声</dd> -->
</dl>
<dl>
<dt>资源下载</dt>
<dd>视频下载</dd>
<!-- <dd>资料下载</dd>
<dd>工具下载</dd> -->
</dl>
<dl>
<dt>加入我们</dt>
<dd>招聘岗位</dd>
<!-- <dd>岗位介绍</dd>
<dd>招贤纳师</dd> -->
</dl>
<dl>
<dt>联系我们</dt>
<dd>http://www.com.atguigu.com</dd>
<dd></dd>
</dl>
</div>
</div>
<div class="down">
尚硅谷书城.Copyright ©2015
</div>
</div>
</body>
</html>
页面效果展现:
12.5 用户登录校验
12.5.1 业务说明
说明: 当用户点击我的订单时,如果用户没有登录,.则应该先让用户登录. 使用Filter过滤器实现
12.5.2 编辑loginFilter
package com.atguigu.filter;
import com.atguigu.bean.User;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebFilter(value = "/order")
public class LoginFilter 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;
//1.从session中获取用户信息
User user = (User) request.getSession().getAttribute("user");
if(user==null){
//应该重定向到用户登录页面
response.sendRedirect(request.getContextPath()+"/user?method=toLogin");
}else{
//否则程序放行
filterChain.doFilter(request,response);
}
}
@Override
public void destroy() {
}
}
13 事务控制
13.1 ACID属性
- A:原子性 事务中包含的数据库操作缺一不可,整个事务是不可再分的。
- C:一致性 事务执行之前,数据库中的数据整体是正确的;事务执行之后,数据库中的数据整体仍然是正确的。
- 事务执行成功:提交(commit)
- 事务执行失败:回滚(rollback)
- I:隔离性 数据库系统同时执行很多事务时,各个事务之间基于不同隔离级别能够在一定程度上做到互不干扰。简单说就是:事务在并发执行过程中彼此隔离。
- D:持久性 事务一旦提交,就永久保存到数据库中,不可撤销。
13.2 隔离级别
13.2.1 并发问题
并发问题 | 问题描述 |
---|---|
脏读 | 当前事务读取了其他事务尚未提交的修改 如果那个事务回滚,那么当前事务读取到的修改就是错误的数据 |
不可重复读 | 当前事务中多次读取到的数据的内容不一致(数据行数一致,但是行中的具体内容不一致) |
幻读 | 当前事务中多次读取到的数据行数不一致 |
13.2.2 隔离级别
隔离级别 | 描述 | 能解决的并发问题 |
---|---|---|
读未提交 | 允许当前事务读取其他事务尚未提交的修改 | 啥问题也解决不了 |
读已提交 | 允许当前事务读取其他事务已经提交的修改 | 脏读 |
可重复读 | 当前事务执行时锁定当前记录,不允许其他事务操作 | 脏读、不可重复读 |
串行化 | 当前事务执行时锁定当前表,不允许其他事务操作 | 脏读、不可重复读、幻读 |
13.2.3 JDBC事务控制
13.2.3.1 同一个数据库连接
只有当多次数据库操作是使用的同一个连接的时候,才能够保证这几次数据库操作在同一个事务中执行
13.2.3.2 关闭事务的自动提交
connection.setAutoCommit(false);
1
13.2.3.3 提交事务
connection.commit();
13.2.3.4 回滚事务
connection.rollBack();
13.3 编辑TransactionFilter实现全局事务控制
注意事项: 使用该过滤器实现事务控制,则要求业务代码不能catch异常,要将异常信息抛出即可.
package com.atguigu.filter;
import com.atguigu.util.JDBCTools;
import com.mysql.cj.jdbc.JdbcConnection;
import jdk.nashorn.internal.ir.RuntimeNode;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
/**
* @author 刘昱江
* @className TransactionFilter
* @description TODO
* @date 2022/4/1 16:18
*/
@WebFilter(value = "/*")
public class TransactionFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//1.获取数据库连接
Connection connection = JDBCTools.getConnection();
//2.设置为手动提交
try {
connection.setAutoCommit(false);
//3.指定业务操作
filterChain.doFilter(servletRequest,servletResponse);
//4.如果程序没有异常,则提交事物
connection.commit();
} catch (Exception throwables) {
throwables.printStackTrace();
try {
connection.rollback();
} catch (SQLException e) {
e.printStackTrace();
}
} finally {
//释放数据库连接
JDBCTools.releaseConnection();
}
}
@Override
public void destroy() {
}
}
本文由 liyunfei 创作,采用 知识共享署名4.0
国际许可协议进行许可
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
最后编辑时间为: Jul 26,2022