书城项目

一、创建项目所需要的包

各个层所对应的包名:
web层:
com.bookmall.web/servlet/controller
service层:
com.bookmall.service   Service接口包
com.bookmall.service.impl   Service接口实现类
dao层:
com.bookmall.dao   Dao接口包
com.bookmall.dao.impl   Dao接口实现类
实体bean对象:
com.bookmall.pojo/entity/domain/bean   JavaBean类
测试包:
com.bookmall.test/junit
工具包:
com.bookmall.utils

二、创建需要的数据库和表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
CREATE DATABASE bookmall;

USE bookmall;

CREATE TABLE userinfo(
`id` INT PRIMARY KEY auto_increment,
`username` VARCHAR(20) NOT NULL,
`password` VARCHAR(32) NOT NULL,
`email` VARCHAR(40)
);

INSERT INTO userinfo(`username`,`password`,`email`)

VALUES('admin','123456','admin@qq.com');

SELECT * FROM userinfo;

三、编写数据库表对应的 JavaBean 对象

在com.bookmall.bean包下创建User类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package com.bookmall.bean;

public class User {
private Integer id;
private String username;
private String password;
private String email;

public User() {
}

public User(Integer id, String username, String password, String email) {
this.id = id;
this.username = username;
this.password = password;
this.email = email;
}

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public String getUsername() {
return username;
}

public void setUsername(String username) {
this.username = username;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}

public String getEmail() {
return email;
}

public void setEmail(String email) {
this.email = email;
}

@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", email='" + email + '\'' +
'}';
}
}

四、编写工具类JdbcUtils并测试

1.首先导入所需要的jar包:

2.在src目录下创建jdbc.properties 配置文件:

1
2
3
4
5
6
username=root
password=2824199842
url=jdbc:mysql://localhost:3306/bookmall?serverTimezone=Asia/Shanghai
driverClassName=com.mysql.cj.jdbc.Driver
initialSize=10
maxActive=10

3.在com.bookmall.utils下创建 JdbcUtils 类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package com.bookmall.utils;

public class JdbcUtils {
private static DruidDataSource dataSource;
static{
Properties properties = null;
try {
properties = new Properties();
// 创建输入流 读取jdbc.properties属性配置文件
InputStream inputStream = JdbcUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
// 从流中加载数据
properties.load(inputStream);
// 创建数据库连接池
dataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}

/**
* 获取数据库连接池中的连接
* @return 返回连接 否则返回null
*/
public static Connection getConnection(){
Connection conn = null;
try {
conn = dataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}

/**
* 关闭连接,放回数据库连接池
* @param conn
*/
public static void close(Connection conn){
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}

4.在com.bookmall.test包下创建JdbcUtilsTest类,测试是否连接成功:

1
2
3
4
5
6
7
8
9
10
package com.bookmall.test;

public class JdbcUtilsTest {
@Test
public void testJdbcUtils() {
Connection conn = JdbcUtils.getConnection();
System.out.println(conn);
JdbcUtils.close(conn);
}
}

五、编写BaseDao

1.导入DBUtils 的jar包:

2.在com.bookmall.dao.impl包下创建BaseDao抽象类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package com.bookmall.dao.impl;

public abstract class BaseDao {
// 使用dbutils操作数据库
private QueryRunner queryRunner = new QueryRunner();

/**
* 执行增删改方法
* @param sql 要执行的sql语句
* @param args 要填充的占位符
* @return 执行成功,返回受影响的行数,否则执行失败,返回-1
*/
public int update(String sql,Object ... args){
Connection conn = JdbcUtils.getConnection();
try {
return queryRunner.update(conn,sql,args);
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.close(conn);
}
return -1;
}

/**
* 执行返回一条结果的查询语句
* @param type 返回的对象类型
* @param sql 要执行的sql语句
* @param args 要填充的占位符
* @param <T> 返回的类型的泛型
* @return 执行成功,返回一个指定类型的对象,否则执行失败,返回null
*/
public <T> T queryForOne(Class<T> type,String sql,Object ... args){
Connection conn = JdbcUtils.getConnection();
// BeanHandler是ResultSetHandler接口的一个实现类,用于封装表中的一条记录
try {
return queryRunner.query(conn,sql,new BeanHandler<T>(type),args);
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.close(conn);
}
return null;
}

/**
* 执行返回多条结果的查询语句
* @param type 返回的对象类型
* @param sql 要执行的sql语句
* @param args 要填充的占位符
* @param <T> 返回的类型的泛型
* @return 执行成功,返回一个指定类型的对象的列表,否则执行失败,返回null
*/
public <T> List<T> queryForList(Class<T> type, String sql, Object ... args){
Connection conn = JdbcUtils.getConnection();
try {
return queryRunner.query(conn,sql,new BeanListHandler<T>(type),args);
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.close(conn);
}
return null;
}

/**
* 执行返回特殊值(一行一列)的查询
* @param sql 要执行的sql语句
* @param args 要填充的占位符
* @return 执行成功,返回一个值,否则执行失败,返回null
*/
public Object queryForSingleValue(String sql, Object ... args){
Connection conn = JdbcUtils.getConnection();
// ScalarHandler将单个值封装
try {
return queryRunner.query(conn,sql,new ScalarHandler(),args);
} catch (SQLException e) {
e.printStackTrace();
}finally{
JdbcUtils.close(conn);
}
return null;
}
}

六、编写UserDao接口并测试

1.在com.bookmall.dao包下编写UserDao 接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.bookmall.dao;

public interface UserDao {
/**
* 根据用户名查询用户信息
* @param username 用户名
* @return 执行成功,返回User对象,否则返回null
*/
public User queryUserByUsername(String username);

/**
* 保存用户信息
* @param user User对象
* @return 执行成功,返回受影响的行数,否则返回-1
*/
public int saveUser(User user);

/**
*根据用户名和密码查询用户信息
* @param username
* @param password
* @return 执行成功返回User对象,否则返回null说明用户名或密码错误
*/
public User queryUserByUsernameAndPassword(String username,String password);
}

2.在com.bookmall.dao包下编写UserDao接口实现类UserDaoImpl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.book.dao.impl;

public class UserDaoImpl extends BaseDao implements UserDao {
@Override
public User queryUserByUsername(String username) {
String sql = "select `id`,`username`,`password`,`email` from userinfo where username = ?";
return queryForOne(User.class, sql, username);
}

@Override
public int saveUser(User user) {
String sql = "insert into userinfo(`username`,`password`,`email`)value(?,?,?)";
return update(sql, user.getUsername(), user.getPassword(), user.getEmail());
}

@Override
public User queryUserByUsernameAndPassword(String username, String password) {
String sql = "select `id`,`username`,`password`,`email` from userInfo where username = ? and password = ?";
return queryForOne(User.class, sql, username, password);
}
}

3.在com.bookmall.test包下创建UserDaoImplTest测试类(UserDaoImpl类下按ctrl+shift+t快速创建测试类),测试UserDaoImpl类中的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.bookmall.test;

public class UserDaoImplTest {
UserDao userDao = new UserDaoImpl();
@Test
public void queryUserByUsername() {
if(userDao.queryUserByUsername("admin")!=null){
System.out.println("用户名可用");
}else{
System.out.println("用户名已存在");
}
}

@Test
public void saveUser() {
System.out.println(userDao.saveUser(new User(null,"大司马","123456","dsm@qq.com")));
}

@Test
public void queryUserByUsernameAndPassword() {
if(userDao.queryUserByUsernameAndPassword("admin","123456") == null){
System.out.println("用户名或密码错误,登录失败!");
}else{
System.out.println("登录成功!");
}
}
}

七、编写UserService并测试

1.在com.bookmall.service包下创建UserService接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.bookmall.service;

public interface UserService {
/**
* 用户注册
* @param user
*/
public void registUser(User user);

/**
* 用户登录
* @param user
* @return
*/
public User login(User user);

/**
* 检查用户名是否可用
* @param username
* @return 返回true,表示用户已存在,否则返回false
*/
public boolean existUsername(String username);
}

2.在com.bookmall.service.impl创建UserService接口实现类UserServiceImpl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.bookmall.service.impl;

public class UserServiceImpl implements UserService {
private UserDao userDao = new UserDaoImpl();
@Override
public void registUser(User user) {
userDao.saveUser(user);
}

@Override
public User login(User user) {
return userDao.queryUserByUsernameAndPassword(user.getUsername(),user.getPassword());
}


@Override
public boolean existUsername(String username) {
if(userDao.queryUserByUsername(username)==null){
// 为null表示不存在,表示可以使用
return false;
}
return true;
}
}

3.在com.bookmall.test包下创建测试类UserServiceImplTest,测试UserServiceImpl中的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.bookmall.test;

public class UserServiceImplTest {
UserService userService = new UserServiceImpl();
@Test
public void registUser() {
userService.registUser(new User(null,"张伟","123456","zw@126.com"));
}

@Test
public void login() {
System.out.println(userService.login(new User(null,"zyz","123456",null)));
}

@Test
public void existUsername() {
System.out.println(userService.existUsername("zyz"));// ture

}
}

八、编写Web层

1.实现注册功能

在com.bookmall.web中创建RegistServlet类
在web.xml文件中配置文件路径

1
2
3
4
5
6
7
8
<servlet>
<servlet-name>RegistServlet</servlet-name>
<servlet-class>com.bookmall.web.RegistServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>RegistServlet</servlet-name>
<url-pattern>/registServlet</url-pattern>
</servlet-mapping>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.bookmall.web;

public class RegistServlet extends HttpServlet {
private UserService userService = new UserServiceImpl();
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1.获取请求参数
String username = req.getParameter("username");
String password = req.getParameter("password");
String email = req.getParameter("email");
String code = req.getParameter("code");
// 2.检查验证码是否正确====先写死为qwer
if("qwer".equalsIgnoreCase(code)){
// 验证码正确
// 3.检查用户名是否可用
if(userService.existUsername(username)){
// true 用户名已存在
System.out.println("用户名["+username+"]已存在!");
// 跳转到首页
req.getRequestDispatcher("/pages/user/regist.html").forward(req,resp);
} else{
// false 用户名不存在 可以注册
userService.registUser(new User(null,username,password,email));
// 跳转到注册成功页面
req.getRequestDispatcher("/pages/user/regist_success.html").forward(req,resp);
}
}else{// 验证码错误
System.out.println("验证码["+code+"]错误!");
// 跳转到首页
req.getRequestDispatcher("/pages/user/regist.html").forward(req,resp);
}
}
}

2.实现登录功能

在com.bookmall.web中创建LogintServlet类
在web.xml文件中配置文件路径

1
2
3
4
5
6
7
8
<servlet>
<servlet-name>LoginServlet</servlet-name>
<servlet-class>com.bookmall.web.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LoginServlet</servlet-name>
<url-pattern>/loginServlet</url-pattern>
</servlet-mapping>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.bookmall.web;

public class LoginServlet extends HttpServlet {
private UserService userService = new UserServiceImpl();
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1.获取请求参数
String username = req.getParameter("username");
String password = req.getParameter("password");
User loginUser = userService.login(new User(null,username, password, null));
if(loginUser==null){
System.out.println("登录失败!");
// 跳转到首页
req.getRequestDispatcher("/pages/user/login.html").forward(req,resp);
}else{
System.out.println("登录成功!");
// 跳转到登录成功页面
req.getRequestDispatcher("/pages/user/login_success.html").forward(req,resp);
}
}
}

九、优化代码

1.将多个页面中重复的代码抽取出来:

写入单独的jsp文件中

footer.jsp

1
2
3
4
5
6
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<div id="bottom">
<span>
尚硅谷书城.Copyright &copy;2015
</span>
</div>

header.jsp

1
2
3
4
5
6
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<% String basePath = request.getContextPath()+"/";%>
<!-- base标签永远固定相对路径跳转 -->
<base href="<%=basePath%>">
<link type="text/css" rel="stylesheet" href="static/css/style.css">
<script type="text/javascript" src="static/script/jquery-3.4.1.js"></script>

login_success_menu.jsp

1
2
3
4
5
6
7
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<div>
<span>欢迎<span class="um_span">张总</span>光临尚硅谷书城</span>
<a href="pages/order/order.jsp">我的订单</a>
<a href="index.jsp">注销</a>&nbsp;&nbsp;
<a href="index.jsp">返回</a>
</div>

manage_menu.jsp

1
2
3
4
5
6
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<div>
<a href="book_manager">图书管理</a>
<a href="order_manager">订单管理</a>
<a href="index.jsp">返回商城</a>
</div>

采用静态包含的方式替换重复的代码:

1
2
 <%-- 静态包含 页脚内容--%>
<%@include file="/pages/common/footer.jsp"%>
1
2
<%-- 静态包含 base标签、css样式、jquery文件--%>
<%@include file="/pages/common/head.jsp"%>
1
2
 <%-- 静态包含 成功之后的菜单--%>
<%@include file="/pages/common/login_sucess_menu.jsp"%
1
2
 <%-- 静态包含 管理菜单--%>
<%@include file="/pages/common/manage_menu.jsp"%>

2.登录、注册错误提示以及表单回显

LoginServlet.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class LoginServlet extends HttpServlet {
private UserService userService = new UserServiceImpl();

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1.获取请求参数
String username = req.getParameter("username");
String password = req.getParameter("password");
User loginUser = userService.login(new User(null, username, password, null));
if (loginUser == null) {
// System.out.println("登录失败!");
// 将要回显的错误信息保存到request域中
req.setAttribute("msg","用户名或密码错误!");
req.setAttribute("username",username);
// 跳转到首页
req.getRequestDispatcher("/pages/user/login.jsp").forward(req, resp);
} else {
System.out.println("登录成功!");
// 跳转到登录成功页面
req.getRequestDispatcher("/pages/user/login_success.jsp").forward(req, resp);
}
}
}

login.jsp

1
2
3
4
5
6
7
8
<div class="msg_cont">
<b></b>
<span class="errorMsg">
<%=
request.getAttribute("msg")==null?"请输入用户名和密码":request.getAttribute("msg")
%>
</span>
</div>
1
2
3
<input class="itxt" type="text" placeholder="请输入用户名"
autocomplete="off" tabindex="1" name="username"
value="<%=request.getAttribute("username")==null?"":request.getAttribute("username")%>"/>

RegistServlet.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class RegistServlet extends HttpServlet {
private UserService userService = new UserServiceImpl();
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1.获取请求参数
String username = req.getParameter("username");
String password = req.getParameter("password");
String email = req.getParameter("email");
String code = req.getParameter("code");
// 2.检查验证码是否正确====先写死为qwer
if("qwer".equalsIgnoreCase(code)){
// 验证码正确
// 3.检查用户名是否可用
if(userService.existUsername(username)){
// true 用户名已存在
// System.out.println("用户名["+username+"]已存在!");
// 把回显信息保存到request域中
req.setAttribute("msg","用户名已存在!");
req.setAttribute("username",username);
req.setAttribute("email",email);
// 跳转到首页
req.getRequestDispatcher("/pages/user/regist.jsp").forward(req,resp);
} else{
// false 用户名不存在 可以注册
userService.registUser(new User(null,username,password,email));
// 跳转到注册成功页面
req.getRequestDispatcher("/pages/user/regist_success.jsp").forward(req,resp);
}
}else{// 验证码错误
// System.out.println("验证码["+code+"]错误!");
// 把错误信息和回显信息保存到request域中
req.setAttribute("msg","验证码错误!");
req.setAttribute("username",username);
req.setAttribute("email",email);
// 跳转到首页
req.getRequestDispatcher("/pages/user/regist.jsp").forward(req,resp);
}
}
}

regist.jsp

1
2
3
<span class="errorMsg">
<%=request.getAttribute("msg")==null?"":request.getAttribute("msg")%>
</span>
1
2
3
4
<input class="itxt" type="text" placeholder="请输入用户名"
autocomplete="off" tabindex="1"
name="username" id="username"
value="<%=request.getAttribute("username")==null?"":request.getAttribute("username")%>"/>
1
2
3
<input class="itxt" type="text" placeholder="请输入邮箱地址" autocomplete="off" tabindex="1"
name="email" id="email"
value="<%=request.getAttribute("email")==null?"":request.getAttribute("email")%>"/>

3.将LoginServlet和RegistServlet合并为UserServlet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
public class UserServlet extends HttpServlet {
private UserService userService = new UserServiceImpl();

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取请求类型
String action = req.getParameter("action");
if ("login".equals(action)) {
login(req, resp);
} else if ("regist".equals(action)) {
regist(req, resp);
}
}

/**
* 处理登录的功能
*
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void login(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1.获取请求参数
String username = req.getParameter("username");
String password = req.getParameter("password");
User loginUser = userService.login(new User(null, username, password, null));
if (loginUser == null) {
// System.out.println("登录失败!");
// 将要回显的错误信息保存到request域中
req.setAttribute("msg", "用户名或密码错误!");
req.setAttribute("username", username);
// 跳转到首页
req.getRequestDispatcher("/pages/user/login.jsp").forward(req, resp);
} else {
// System.out.println("登录成功!");
// 跳转到登录成功页面
req.getRequestDispatcher("/pages/user/login_success.jsp").forward(req, resp);
}
}

/**
* 处理注册的功能
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void regist(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1.获取请求参数
String username = req.getParameter("username");
String password = req.getParameter("password");
String email = req.getParameter("email");
String code = req.getParameter("code");
// 2.检查验证码是否正确====先写死为qwer
if ("qwer".equalsIgnoreCase(code)) {
// 验证码正确
// 3.检查用户名是否可用
if (userService.existUsername(username)) {
// true 用户名已存在
// System.out.println("用户名["+username+"]已存在!");
// 把回显信息保存到request域中
req.setAttribute("msg", "用户名已存在!");
req.setAttribute("username", username);
req.setAttribute("email", email);
// 跳转到首页
req.getRequestDispatcher("/pages/user/regist.jsp").forward(req, resp);
} else {
// false 用户名不存在 可以注册
userService.registUser(new User(null, username, password, email));
// 跳转到注册成功页面
req.getRequestDispatcher("/pages/user/regist_success.jsp").forward(req, resp);
}
} else {// 验证码错误
// System.out.println("验证码["+code+"]错误!");
// 把错误信息和回显信息保存到request域中
req.setAttribute("msg", "验证码错误!");
req.setAttribute("username", username);
req.setAttribute("email", email);
// 跳转到首页
req.getRequestDispatcher("/pages/user/regist.jsp").forward(req, resp);
}
}
}

login.jsp

regist.jsp

4.使用反射优化大量else if 代码

1
2
3
4
5
6
7
8
9
10
11
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 通过反射根据action属性值调用相应的方法
String action = req.getParameter("action");
try {
Method method = UserServlet.class.getDeclaredMethod(action, HttpServletRequest.class, HttpServletResponse.class);
method.invoke(this,req, resp);
} catch (Exception e) {
e.printStackTrace();
}
}

5.抽取BaseServlet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.bookmall.web;

public abstract class BaseServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
doPost(req, resp);
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
// 获取请求类型
String action = req.getParameter("action");
// System.out.println(action);
try {// 通过反射根据action属性值调用相应的方法
Method method = this.getClass().getDeclaredMethod(action, HttpServletRequest.class, HttpServletResponse.class);
method.invoke(this, req, resp);
} catch (Exception e) {
e.printStackTrace();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
package com.bookmall.web;

public class UserServlet extends BaseServlet {
private UserService userService = new UserServiceImpl();
protected void login(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// ......
}

protected void regist(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// ......
}
}

6.数据的封装和抽取 BeanUtils的使用

BeanUtils工具类,可以一次性的把所有请求的参数注入导JavaBean中
1、导入需要的jar 包:
commons-beanutils-1.8.0.jar
commons-logging-1.1.1jar
2、使用BeanUtils类方法实现注入

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.bookmall.utils;

public class WebUtils {
public static <T> T copyParamToBean(Map map, T bean){
// 将所有请求的参数封装到bean中
try {
BeanUtils.populate(bean,map);
} catch (Exception e) {
e.printStackTrace();
}
return bean;
}
}

UserServlet.java

1
2
// 将请求参数一次性封装到user对象中
User user = WebUtils.copyParamToBean(req.getParameterMap(),new User());

7.使用EL表达式修改表单回显

login.jsp

1
2
3
4
<%--
<%= request.getAttribute("msg")==null?"请输入用户名和密码":request.getAttribute("msg")%>
--%>
${empty requestScope.msg ? "请输入用户名和密码":requestScope.msg}
1
2
3
4
<%--
value="<%=request.getAttribute("username")==null?"":request.getAttribute("username")%>"
--%>
value="${requestScope.username}"

regist.jsp

1
2
3
4
<%--
<%=request.getAttribute("msg")==null?"":request.getAttribute("msg")%>
--%>
${requestScope.msg}
1
2
3
4
<%--
value="<%=request.getAttribute("username")==null?"":request.getAttribute("username")%>"
--%>
${requestScope.username}
1
2
3
4
<%--
value="<%=request.getAttribute("email")==null?"":request.getAttribute("email")%>"
--%>
${requestScope.email}

十、图书模块

1.创建图书模块的数据库表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
USE bookmall;
CREATE TABLE book(
`id` INT PRIMARY KEY auto_increment,
`name` VARCHAR(50) NOT NULL,
`price` DECIMAL(8,2) NOT NULL,
`author` VARCHAR(50) NOT NULL,
`sales` INT NOT NULL,
`stock` INT NOT NULL,
`img_path` VARCHAR(100) NOT NULL
);
# drop table book;

## 插入初始化测试数据
insert into book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , 'java从入门到放弃' , '国哥' , 80 , 9999 , 9 , 'static/img/default.jpg');

insert into book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , '数据结构与算法' , '严敏君' , 78.5 , 6 , 13 , 'static/img/default.jpg');

insert into book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , '编程珠玑' , 'Tom' , 68, 99999 , 52 , 'static/img/default.jpg');

insert into book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , '从你的全世界路过' , '张嘉加' , 16, 1000 , 50 , 'static/img/cover.jpg');

insert into book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , 'C语言程序设计' , '谭浩强' , 28 , 52 , 74 , 'static/img/default.jpg');

2.编写图书模块的JavaBean对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
package com.bookmall.bean;

public class Book {
private Integer id;
private String name;
private String author;
private BigDecimal price;
private Integer sales;
private Integer stock;
private String imgPath;

public Book() {
}

public Book(Integer id, String name, String author, BigDecimal price, Integer sales, Integer stock, String imgPath) {
this.id = id;
this.name = name;
this.author = author;
this.price = price;
this.sales = sales;
this.stock = stock;
if (imgPath != null && "".equals(imgPath)) {
this.imgPath = imgPath;
}
}

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getAuthor() {
return author;
}

public void setAuthor(String author) {
this.author = author;
}

public BigDecimal getPrice() {
return price;
}

public void setPrice(BigDecimal 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) {
if (imgPath != null && "".equals(imgPath)) {
this.imgPath = imgPath;
}
}

@Override
public String toString() {
return "Book{" +
"id=" + id +
", name='" + name + '\'' +
", author='" + author + '\'' +
", price=" + price +
", sales=" + sales +
", stock=" + stock +
", imgPath='" + imgPath + '\'' +
'}';
}
}

3.编写图书模块的DAO并测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.bookmall.dao.impl;

public class BookDaoImpl extends BaseDao implements BookDao {
@Override
public int addBook(Book book) {
String sql = "insert into book(`name`,`author`,`price`,`sales`,`stock`,`img_path`) values(?,?,?,?,?,?)";
return update(sql, book.getName(),book.getAuthor(),book.getPrice(),book.getSales(),book.getStock(),book.getImgPath());
}

@Override
public int deleteBook(Integer id) {
String sql = "delete from book where id = ?";
return update(sql,id);
}

@Override
public int updateBook(Book book) {
String sql = "update book set `name`=?,`author`=?,`price`=?,`sales`=?,`stock`=?,`img_path`= ? where id = ?";
return update(sql,book.getName(),book.getAuthor(),book.getPrice(),book.getSales(),book.getStock(),book.getImgPath(),book.getId());
}

@Override
public Book queryBookById(Integer id) {
String sql = "select `id`,`name`,`author`,`price`,`sales`,`stock`,`img_path` imgPath from book where id = ?";
return queryForOne(Book.class,sql,id);
}

@Override
public List<Book> queryBooks() {
String sql = "select `id`,`name`,`author`,`price`,`sales`,`stock`,`img_path` imgPath from book";
return queryForList(Book.class,sql);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.bookmall.test;

public class BookDaoImplTest {
private BookDao bookDao = new BookDaoImpl();

@Test
public void addBook() {
bookDao.addBook(new Book(null,"算法与数据结构(java版)","张三三",new BigDecimal(58.80),20,100,"static/img/default.jpg"));
}

@Test
public void deleteBook() {
bookDao.deleteBook(1);
}

@Test
public void updateBook() {
bookDao.updateBook(new Book(21,"算法与数据结构(C++版)","张三三",new BigDecimal(58.80),20,100,"static/img/default.jpg"));
}

@Test
public void queryBookById() {
System.out.println(bookDao.queryBookById(21));
}

@Test
public void queryBooks() {
for(Book book:bookDao.queryBooks()){
System.out.println(book);
}
}
}

4.编写图书模块的Service并测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.bookmall.service.impl;

public class BookServiceImpl implements BookService {
private BookDao bookDao = new BookDaoImpl();

@Override
public void addBook(Book book) {
bookDao.addBook(book);
}

@Override
public void deleteBook(Integer id) {
bookDao.deleteBook(id);
}

@Override
public void updateBook(Book book) {
bookDao.updateBook(book);
}

@Override
public Book queryBookById(Integer id) {
return bookDao.queryBookById(id);
}

@Override
public List<Book> queryBooks() {
return bookDao.queryBooks();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.bookmall.test;

public class BookServiceImplTest {
private BookService bookService = new BookServiceImpl();
@Test
public void addBook() {
bookService.addBook(new Book(null,"肉蛋葱鸡","马老师",new BigDecimal(66.66),666,66,"static/img/cover.jpg"));
}

@Test
public void deleteBook() {
bookService.deleteBook(10);
}

@Test
public void updateBook() {
bookService.updateBook(new Book(7,"红皮烤鸭","马老师",new BigDecimal(99.8),100,2,"static/img/cover.jpg"));
}

@Test
public void queryBookById() {
System.out.println(bookService.queryBookById(7));
}

@Test
public void queryBooks() {
for(Book book :bookService.queryBooks()){
System.out.println(book);
}
}
}

5.编写图书模块的Web层,和页面联调测试

1、实现展示全部图书

在BookServlet.java中添加list()方法用于展示全部图书信息:

1
2
3
4
5
6
7
8
9
private BookService bookService = new BookServiceImpl();
protected void list(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1.通过BookService查询全部图书
List<Book> books = bookService.queryBooks();
// 2.将全部的图书信息保存到request域中
req.setAttribute("books",books);
// 3.请求转发
req.getRequestDispatcher("/pages/manager/book_manager.jsp").forward(req,resp);
}

修改图书管理页面的跳转地址:

利用JSTL标签库遍历图书信息在jsp页面中输出:
1、导入JSTL标签库的jar包
taglibs-standard-impl-1.2.1.jar
taglibs-standard-spec-1.2.1.jar
2、在book_manager.jsp中编写遍历图书信息的代码

1
2
3
4
5
6
7
8
9
10
11
<c:forEach items="${requestScope.books}" var="book">
<tr>
<td>${book.name}</td>
<td>${book.price}</td>
<td>${book.author}</td>
<td>${book.sales}</td>
<td>${book.stock}</td>
<td><a href="book_edit">修改</a></td>
<td><a href="#">删除</a></td>
</tr>
</c:forEach>

2、实现添加图书

在BookServlet中添加add()方法用于添加图书:

1
2
3
4
5
6
7
8
9
10
11
12
protected void add(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1、获取请求的参数==封装成为Book对象
Book book = WebUtils.copyParamToBean(req.getParameterMap(), new Book());
// 2、调用BookService.addBook()保存图书
bookService.addBook(book);
// 3、跳到图书列表页面 /manager/bookServlet?action=list
// 请求转发会造成表单的多次提交
// req.getRequestDispatcher("/manager/bookServlet?action=list").forward(req, resp);

// 请求重定向
resp.sendRedirect(req.getContextPath()+ "/manager/bookServlet?action=list");
}

修改book_edit.jsp:

3、实现删除图书

在BookServlet中添加delete()方法:

1
2
3
4
5
6
7
8
9
protected void delete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1、获取图书id
int id = WebUtils.parseInt(req.getParameter("id"),0);
// 2、调用BookService.deleteById() 删除图书
bookService.deleteBook(id);
// 3、重定向 /manager/bookServlet?action=list
resp.sendRedirect(req.getScheme()+"://"+ req.getServerName()+":"+
req.getServerPort()+req.getContextPath()+ "/manager/bookServlet?action=list");
}

修改book_manager.jsp:

添加确认删除提示:
给a标签添加class属性用于对标签选择
给a标签绑定单击事件:

1
2
3
4
5
6
7
8
9
10
11
 <%-- 给删除按钮绑定单击事件 用于删除操作的确认	--%>
<script type="text/javascript">
// 在事件的function函数中,有一个this对象,表示当前正在响应事件的dom对象
$(function () {
$("a.deleteClass").click(function(){
// 确认返回true 取消返回false
return confirm("你确定删除《" + $(this).parent().parent().find("td:first").text() +"》吗?");
// return false; 阻止元素的默认行为 不提交
});
});
</script>

4、实现修改图书

在bookServlet中添加getBook()用于获取要修改图书的信息:

1
2
3
4
5
6
7
8
9
10
protected void getBook(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1.获取id
int id = WebUtils.parseInt(req.getParameter("id"),0);
// 2.调用BookService.queryBookById() 获取图书信息
Book book = bookService.queryBookById(id);
// 3.将图书信息保存至request域中
req.setAttribute("book",book);
// 4.请求转发到 /pages/manager/book_edit.jsp
req.getRequestDispatcher("/pages/manager/book_edit.jsp").forward(req,resp);
}

修改book_edit.jsp让数据回显:

在BookServlet中添加update()方法:

1
2
3
4
5
6
7
8
protected void update(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1.获取从book_edit.jsp提交的表单数据 将数据封装到book对象中
Book book = WebUtils.copyParamToBean(req.getParameterMap(),new Book());
// 2.调用BookService.update()更新图书
bookService.updateBook(book);
// 3.重定向 manager/bookServlet?action=list 刷新图书列表
resp.sendRedirect(req.getContextPath()+ "/manager/bookServlet?action=list");
}

在book_edit.jsp中添加隐藏域用于回传修改图书所需要的id值:

图书信息并没有发生修改的原因:
book_edit.jsp页面中既要进行添加add操作,又要进行修改update操作,最终进行何种操作是根据一个隐藏域决定的,因此需要动态修改隐藏域:在请求发起时附带上要执行操作的值,并注入隐藏域中。
传入update参数:
传入add参数:
注入隐藏域:

5、实现图书的分页


创建Page对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
package com.bookmall.bean;

import java.util.List;
/**
* Page为分页的模型
* @param <T> 是具体的模块的 JavaBean 对象
*/
public class Page<T> {
public static final Integer PAGE_SIZE = 4;
// 当前页码
private Integer pageNo;
// 总页码
private Integer pageTotal;
// 总记录数
private Integer pageTotalCount;
// 每页显示的数量
private Integer pageSize = PAGE_SIZE;
// 当前页的数据
private List<T> items;

public Page() {
}

public Page(Integer pageNo, Integer pageTotal, Integer pageTotalCount, Integer pageSize, List<T> items) {
this.pageNo = pageNo;
this.pageTotal = pageTotal;
this.pageTotalCount = pageTotalCount;
this.pageSize = pageSize;
this.items = items;
}

public static Integer getPageSize() {
return PAGE_SIZE;
}

public void setPageSize(Integer pageSize) {
this.pageSize = pageSize;
}

public List<T> getItems() {
return items;
}

public void setItems(List<T> items) {
this.items = items;
}

public Integer getPageNo() {
return pageNo;
}

public void setPageNo(Integer pageNo) {
// 对页码进行检查
if(pageNo<1){
pageNo = 1;
}
if (pageNo>pageTotal){
pageNo = pageTotal;
}
this.pageNo = pageNo;
}

public Integer getPageTotal() {
return pageTotal;
}

public void setPageTotal(Integer pageTotal) {
this.pageTotal = pageTotal;
}

public Integer getPageTotalCount() {
return pageTotalCount;
}

public void setPageTotalCount(Integer pageTotalCount) {
this.pageTotalCount = pageTotalCount;
}

@Override
public String toString() {
return "Page{" +
"pageNo=" + pageNo +
", pageTotal=" + pageTotal +
", pageTotalCount=" + pageTotalCount +
", pageSize=" + pageSize +
", items=" + items +
'}';
}
}

在BookServlet中添page()用于处理分页:

1
2
3
4
5
6
7
8
9
10
11
protected void page(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1.获取js页面传来的请求参数 pageNo 和 pageSize
int pageNo = WebUtils.parseInt(req.getParameter("pageNo"),1);
int pageSize = WebUtils.parseInt(req.getParameter("pageSize"), Page.PAGE_SIZE);
// 2.调用BookService.page(pageNo,pageSize) 得到Page对象
Page<Book> page = bookService.page(pageNo, pageSize);
// 3.将Page对象保存到Request域中
req.setAttribute("page",page);
// 4.请求转发到 /pages/manager/book_manager.jsp
req.getRequestDispatcher("/pages/manager/book_manager.jsp").forward(req,resp);
}

在BookService中添加page(pageNo,pageSize) 获取page对象:

1
2
3
4
5
6
7
/**
* 查询分页
* @param pageNo
* @param pageSize
* @return 返回一个Page对象
*/
public Page<Book> page(int pageNo, int pageSize);

在BookServiceImpl中实现page(pageNo,pageSize):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Override
public Page<Book> page(int pageNo, int pageSize) {
Page<Book> bookPage = new Page<>();
//设置每页显示的数量
bookPage.setPageSize(pageSize);
// 设置总记录数
Integer pageTotalCount = bookDao.queryForPageTotalCount();
bookPage.setPageTotalCount(pageTotalCount);
// 设置总页码
Integer pageTotal = pageTotalCount/pageSize;
if (pageTotalCount % pageSize>0){
pageTotal++;
}
bookPage.setPageTotal(pageTotal);
// 设置当前页码
bookPage.setPageNo(pageNo);
// 设置当页数据
int begin = (bookPage.getPageNo()-1)*pageSize;
bookPage.setItems(bookDao.queryForItems(begin,pageSize));
return bookPage;
}

在BookDao中添加queryForPageTotal()和queryForItems()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 查询图书的总记录数
* @return
*/
public Integer queryForPageTotalCount();

/**
* 查询当前页面的图书记录
* @param begin
* @param pageSize
* @return
*/
List<Book> queryForItems(int begin, int pageSize);

在BookDaoImpl中实现queryForPageTotal()和queryForItems()方法:

1
2
3
4
5
6
7
8
9
10
11
12
@Override
public Integer queryForPageTotalCount() {
String sql = "select count(*) from book";
Number count = (Number) queryForSingleValue(sql);
return count.intValue();
}

@Override
public List<Book> queryForItems(int begin, int pageSize) {
String sql = " select `id`, `name`,`author`,`price`,`sales`,`stock`,`img_path` imgPath from book limit ?,?";
return queryForList(Book.class,sql,begin,pageSize);
}

修改manage_menu.jsp页面显示分页后的内容:

修改book_manage.jsp页面,让下方显示页面跳转的功能按键:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<script type="text/javascript">
// 在事件的function函数中,有一个this对象,表示当前正在响应事件的dom对象
$(function () {
<%-- 给删除按钮绑定单击事件 用于删除操作的确认 --%>
$("a.deleteClass").click(function () {
// 确认返回true 取消返回false
return confirm("你确定删除《" + $(this).parent().parent().find("td:first").text() + "》吗?");
// return false; 阻止元素的默认行为 不提交
});

$("#searchPageBtn").click(function () {
var pageNo = $("#pn_input").val();
location.href = "${pageScope.basePath}manager/bookServlet?action=page&pageNo=" + pageNo;
});
});
</script>

修改hesd.jsp 将basePath保存至pageContext域中:供指定页面跳转时使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
<div id="page_nav">
<%-- 大于首页才显示--%>
<c:if test="${requestScope.page.pageNo>1}">
<a href="manager/bookServlet?action=page&pageNo=1">首页</a>
<a href="manager/bookServlet?action=page&pageNo=${requestScope.page.pageNo-1}">上一页</a>
</c:if>

<%-- 页码输出的开始--%>
<c:choose>
<%-- 情况1:如果总页码<=5,页码的范围是1~总页码 --%>
<c:when test="${requestScope.page.pageTotal<=5}">
<c:forEach begin="1" end="${requestScope.page.pageTotal}" var="i">
<c:if test="${requestScope.page.pageNo==i}">
【${i}】
</c:if>
<c:if test="${requestScope.page.pageNo!=i}">
<a href="manager/bookServlet?action=page&pageNo=${i}">${i}</a>
</c:if>
</c:forEach>
</c:when>
<%--情况2:总页码>5 --%>
<c:when test="${requestScope.page.pageTotal>5}">
<c:choose>
<%-- 当前页码为前面3个:页码范围是:1~5--%>
<c:when test="${requestScope.page.pageNo<=3}">
<c:forEach begin="1" end="5" var="i">
<c:if test="${requestScope.page.pageNo==i}">
【${i}】
</c:if>
<c:if test="${requestScope.page.pageNo!=i}">
<a href="manager/bookServlet?action=page&pageNo=${i}">${i}</a>
</c:if>
</c:forEach>
</c:when>
<%-- 当前页码为后面3个:页码范围是:当前页码-2~末页--%>
<c:when test="${requestScope.page.pageNo>=requestScope.page.pageTotal-2}">
<c:forEach begin="${requestScope.page.pageNo-4}" end="${requestScope.page.pageTotal}" var="i">
<c:if test="${requestScope.page.pageNo==i}">
【${i}】
</c:if>
<c:if test="${requestScope.page.pageNo!=i}">
<a href="manager/bookServlet?action=page&pageNo=${i}">${i}</a>
</c:if>
</c:forEach>
</c:when>
<%-- 当前页码为中间3个:页码范围是:当前页码-2~当前页码+2--%>
<c:otherwise>
<c:forEach begin="${requestScope.page.pageNo-2}" end="${requestScope.page.pageNo+2}" var="i">
<c:if test="${requestScope.page.pageNo==i}">
【${i}】
</c:if>
<c:if test="${requestScope.page.pageNo!=i}">
<a href="manager/bookServlet?action=page&pageNo=${i}">${i}</a>
</c:if>
</c:forEach>
</c:otherwise>
</c:choose>
</c:when>
</c:choose>
<%-- 页码输出的结束--%>

<%-- 小于末页才显示--%>
<c:if test="${requestScope.page.pageNo<requestScope.page.pageTotal}">
<a href="manager/bookServlet?action=page&pageNo=${requestScope.page.pageNo+1}">下一页</a>
<a href="manager/bookServlet?action=page&pageNo=${requestScope.page.pageTotal}">末页</a>
</c:if>
共${requestScope.page.pageTotal}页,${requestScope.page.pageTotalCount}条记录
到第<input value="${param.pageNo}" name="pn" id="pn_input"/>
<input id="searchPageBtn" type="button" value="确定">
</div>

修改增删改操作的重定向地址并传入pageNo参数:

在book_manager.jsp传入页码pageNo:

在book_edit.jsp中添加隐藏域用于传递pageNo给BookServlet:

前台图书的分页展示:|

web目录下的index.jsp只负责转发:

1
2
3
4
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<%--只负责请求转发--%>
<jsp:forward page="/client/clientBookServlet?action=page"></jsp:forward>

创建ClientBookServlet用于处理前台的分页请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.bookmall.web;

public class ClientBookServlet extends BaseServlet {

private BookService bookService = new BookServiceImpl();
/**
* 处理图书分页
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void page(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("经过了前台的ClientBookServlet");
// 1.获取js页面传来的请求参数 pageNo 和 pageSize
int pageNo = WebUtils.parseInt(req.getParameter("pageNo"),1);
int pageSize = WebUtils.parseInt(req.getParameter("pageSize"), Page.PAGE_SIZE);
// 2.调用BookService.page(pageNo,pageSize) 得到Page对象
Page<Book> page = bookService.page(pageNo, pageSize);
// 3.将Page对象保存到Request域中
req.setAttribute("page",page);
// 4.请求转发到 /pages/client/index.jsp
req.getRequestDispatcher("/pages/client/index.jsp").forward(req,resp);
}
}

在web目录的/page/client/路径下创建index.jsp用于显示前台图书信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>书城首页</title>
<%-- 静态包含 base标签、css样式、jquery文件 --%>
<%@include file="/pages/common/head.jsp" %>
<script type="text/javascript">
$(function(){
$("#searchPageBtn").click(function(){
var pageNo = $("#pn_input").val();
location.href = "${pageScope.basePath}client/clientBookServlet?action=page&pageNo="+pageNo;
});
})
</script>
</head>
<body>

<div id="header">

<span class="wel_word">网上书城</span>
<div>
<a href="pages/user/login.jsp">登录</a> |
<a href="pages/user/regist.jsp">注册</a> &nbsp;&nbsp;
<a href="pages/cart/cart.jsp">购物车</a>
<a href="pages/manager/manager.jsp">后台管理</a>
</div>
</div>
<div id="main">
<div id="book">
<div class="book_cond">
<form action="" method="get">
价格:<input id="min" type="text" name="min" value=""> 元 -
<input id="max" type="text" name="max" value="">
<input type="submit" value="查询"/>
</form>
</div>
<div style="text-align: center">
<span>您的购物车中有3件商品</span>
<div>
您刚刚将<span style="color: red">时间简史</span>加入到了购物车中
</div>
</div>
<c:forEach items="${requestScope.page.items}" var="book">
<div class="b_list">
<div class="img_div">
<img class="book_img" alt="" src="${book.imgPath}"/>
</div>
<div class="book_info">
<div class="book_name">
<span class="sp1">书名:</span>
<span class="sp2">${book.name}</span>
</div>
<div class="book_author">
<span class="sp1">作者:</span>
<span class="sp2">${book.author}</span>
</div>
<div class="book_price">
<span class="sp1">价格:¥</span>
<span class="sp2">${book.price}</span>
</div>
<div class="book_sales">
<span class="sp1">销量:</span>
<span class="sp2">${book.sales}</span>
</div>
<div class="book_amount">
<span class="sp1">库存:</span>
<span class="sp2">${book.stock}</span>
</div>
<div class="book_add">
<button>加入购物车</button>
</div>
</div>
</div>
</c:forEach>
</div>

<div id="page_nav">
<%-- 大于首页才显示--%>
<c:if test="${requestScope.page.pageNo>1}">
<a href="client/clientBookServlet?action=page&pageNo=1">首页</a>
<a href="client/clientBookServlet?action=page&pageNo=${requestScope.page.pageNo-1}">上一页</a>
</c:if>

<%-- 页码输出的开始--%>
<c:choose>
<%-- 情况1:如果总页码<=5,页码的范围是1~总页码 --%>
<c:when test="${requestScope.page.pageTotal<=5}">
<c:forEach begin="1" end="${requestScope.page.pageTotal}" var="i">
<c:if test="${requestScope.page.pageNo==i}">
【${i}】
</c:if>
<c:if test="${requestScope.page.pageNo!=i}">
<a href="client/clientBookServlet?action=page&pageNo=${i}">${i}</a>
</c:if>
</c:forEach>
</c:when>
<%--情况2:总页码>5 --%>
<c:when test="${requestScope.page.pageTotal>5}">
<c:choose>
<%-- 当前页码为前面3个:页码范围是:1~5--%>
<c:when test="${requestScope.page.pageNo<=3}">
<c:forEach begin="1" end="5" var="i">
<c:if test="${requestScope.page.pageNo==i}">
【${i}】
</c:if>
<c:if test="${requestScope.page.pageNo!=i}">
<a href="client/clientBookServlet?action=page&pageNo=${i}">${i}</a>
</c:if>
</c:forEach>
</c:when>
<%-- 当前页码为后面3个:页码范围是:当前页码-2~末页--%>
<c:when test="${requestScope.page.pageNo>=requestScope.page.pageTotal-2}">
<c:forEach begin="${requestScope.page.pageNo-4}" end="${requestScope.page.pageTotal}" var="i">
<c:if test="${requestScope.page.pageNo==i}">
【${i}】
</c:if>
<c:if test="${requestScope.page.pageNo!=i}">
<a href="client/clientBookServlet?action=page&pageNo=${i}">${i}</a>
</c:if>
</c:forEach>
</c:when>
<%-- 当前页码为中间3个:页码范围是:当前页码-2~当前页码+2--%>
<c:otherwise>
<c:forEach begin="${requestScope.page.pageNo-2}" end="${requestScope.page.pageNo+2}" var="i">
<c:if test="${requestScope.page.pageNo==i}">
【${i}】
</c:if>
<c:if test="${requestScope.page.pageNo!=i}">
<a href="client/clientBookServlet?action=page&pageNo=${i}">${i}</a>
</c:if>
</c:forEach>
</c:otherwise>
</c:choose>
</c:when>
</c:choose>
<%-- 页码输出的结束--%>

<%-- 小于末页才显示--%>
<c:if test="${requestScope.page.pageNo<requestScope.page.pageTotal}">
<a href="client/clientBookServlet?action=page&pageNo=${requestScope.page.pageNo+1}">下一页</a>
<a href="client/clientBookServlet?action=page&pageNo=${requestScope.page.pageTotal}">末页</a>
</c:if>
共${requestScope.page.pageTotal}页,${requestScope.page.pageTotalCount}条记录
到第<input value="${param.pageNo}" name="pn" id="pn_input"/>
<input id="searchPageBtn" type="button" value="确定">
</div>
</div>

<%@include file="/pages/common/footer.jsp" %>
</body>
</html>

抽取分页:
在Page对象中添加url属性以及相应的get 和set方法:

分别在BookServlet和ClientBookServlet中设置url属性值:

分别将index.jsp和book_manager.jsp中出现的相应的地址值替换为${requestScope.page.url}

在/pages/common/下创建page_nav.jsp用于提取分页代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<div id="page_nav">
<%-- 大于首页才显示--%>
<c:if test="${requestScope.page.pageNo>1}">
<a href="${requestScope.page.url}&pageNo=1">首页</a>
<a href="${requestScope.page.url}&pageNo=${requestScope.page.pageNo-1}">上一页</a>
</c:if>

<%-- 页码输出的开始--%>
<c:choose>
<%-- 情况1:如果总页码<=5,页码的范围是1~总页码 --%>
<c:when test="${requestScope.page.pageTotal<=5}">
<c:forEach begin="1" end="${requestScope.page.pageTotal}" var="i">
<c:if test="${requestScope.page.pageNo==i}">
【${i}】
</c:if>
<c:if test="${requestScope.page.pageNo!=i}">
<a href="${requestScope.page.url}&pageNo=${i}">${i}</a>
</c:if>
</c:forEach>
</c:when>
<%--情况2:总页码>5 --%>
<c:when test="${requestScope.page.pageTotal>5}">
<c:choose>
<%-- 当前页码为前面3个:页码范围是:1~5--%>
<c:when test="${requestScope.page.pageNo<=3}">
<c:forEach begin="1" end="5" var="i">
<c:if test="${requestScope.page.pageNo==i}">
【${i}】
</c:if>
<c:if test="${requestScope.page.pageNo!=i}">
<a href="${requestScope.page.url}&pageNo=${i}">${i}</a>
</c:if>
</c:forEach>
</c:when>
<%-- 当前页码为后面3个:页码范围是:当前页码-2~末页--%>
<c:when test="${requestScope.page.pageNo>=requestScope.page.pageTotal-2}">
<c:forEach begin="${requestScope.page.pageNo-4}" end="${requestScope.page.pageTotal}" var="i">
<c:if test="${requestScope.page.pageNo==i}">
【${i}】
</c:if>
<c:if test="${requestScope.page.pageNo!=i}">
<a href="${requestScope.page.url}&pageNo=${i}">${i}</a>
</c:if>
</c:forEach>
</c:when>
<%-- 当前页码为中间3个:页码范围是:当前页码-2~当前页码+2--%>
<c:otherwise>
<c:forEach begin="${requestScope.page.pageNo-2}" end="${requestScope.page.pageNo+2}" var="i">
<c:if test="${requestScope.page.pageNo==i}">
【${i}】
</c:if>
<c:if test="${requestScope.page.pageNo!=i}">
<a href="${requestScope.page.url}&pageNo=${i}">${i}</a>
</c:if>
</c:forEach>
</c:otherwise>
</c:choose>
</c:when>
</c:choose>
<%-- 页码输出的结束--%>

<%-- 小于末页才显示--%>
<c:if test="${requestScope.page.pageNo<requestScope.page.pageTotal}">
<a href="${requestScope.page.url}&pageNo=${requestScope.page.pageNo+1}">下一页</a>
<a href="${requestScope.page.url}&pageNo=${requestScope.page.pageTotal}">末页</a>
</c:if>
共${requestScope.page.pageTotal}页,${requestScope.page.pageTotalCount}条记录
到第<input value="${param.pageNo}" name="pn" id="pn_input"/>
<input id="searchPageBtn" type="button" value="确定">
</div>

在index.jsp和book_mansger.jsp中的分页代码替换成静态包含代码:

1
<%@include file="/pages/common/page_nav.jsp"%>

6、实现价格区间的搜索功能:

在ClientBookClient中添加pageByPrice()方法用于处理搜索请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
protected void pageByPrice(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1.获取请求参数 pageNo,pageSize,min,max
int pageNo = WebUtils.parseInt(req.getParameter("pageNo"),1);
int pageSize = WebUtils.parseInt(req.getParameter("pageSize"),Page.PAGE_SIZE);
int min = WebUtils.parseInt(req.getParameter("min"),0);
int max = WebUtils.parseInt(req.getParameter("max"),Integer.MAX_VALUE);
// 2.调用bookService.pageByPrice(pageNo,pageSize,min,max) 返回page对象
Page<Book> page = bookService.pageByPrice(pageNo,pageSize,min,max);
//设置请求地址 分页时需要传递价格区间
StringBuilder stringBuilder = new StringBuilder("client/clientBookServlet?action=pageByPrice");
// 对价格区间进行检查
if(req.getParameter("min")!=null){
stringBuilder.append("&min=").append(req.getParameter("min"));
}
if(req.getParameter("max")!=null){
stringBuilder.append("&max=").append(req.getParameter("max"));
}
page.setUrl(stringBuilder.toString());

// 3.将Page对象保存到Request域中
req.setAttribute("page",page);
// 4.请求转发到
req.getRequestDispatcher("/pages/client/index.jsp").forward(req,resp);
}

在BookService中添加pageByPrice(pageNo,pageSize,min,max)方法:

1
2
3
4
5
6
7
8
9
/**
* 根据价格区间获取page对象
* @param pageNo
* @param pageSize
* @param min
* @param max
* @return
*/
Page<Book> pageByPrice(int pageNo, int pageSize, int min, int max);

在BookServiceImpl中实现pageByPrice(pageNo,pageSize,min,max)方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Override
public Page<Book> pageByPrice(int pageNo, int pageSize, int min, int max) {
Page<Book> bookPage = new Page<>();

//设置每页显示的数量
bookPage.setPageSize(pageSize);
// 设置总记录数
Integer pageTotalCount = bookDao.queryForPageTotalCountByPrice(min,max);
bookPage.setPageTotalCount(pageTotalCount);
// 设置总页码
Integer pageTotal = pageTotalCount/pageSize;
if (pageTotalCount % pageSize>0){
pageTotal++;
}
bookPage.setPageTotal(pageTotal);
// 设置当前页码
bookPage.setPageNo(pageNo);
// 设置当页数据
int begin = (bookPage.getPageNo()-1)*pageSize;
bookPage.setItems(bookDao.queryForItemsByPrice(begin,pageSize,min,max));
return bookPage;
}

在BookDao中添加queryForPageTotalCountByPrice(int min, int max)和queryForItemsByPrice(int begin, int pageSize, int min, int max)方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 /**
* 根据价格区间查询总记录数
* @param min
* @param max
* @return
*/
Integer queryForPageTotalCountByPrice(int min, int max);

/**
* 根据价格区间查询所有记录
* @param begin
* @param pageSize
* @param min
* @param max
* @return
*/
List<Book> queryForItemsByPrice(int begin, int pageSize, int min, int max);
}

在BookDaoImpl中实现这两种方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
public Integer queryForPageTotalCountByPrice(int min, int max) {
String sql = "select count(*) from book where price between ? and ?";
Number count = (Number) queryForSingleValue(sql,min,max);
return count.intValue();
}

@Override
public List<Book> queryForItemsByPrice(int begin, int pageSize, int min, int max) {
String sql = " select `id`, `name`,`author`,`price`,`sales`,`stock`,`img_path` imgPath from book" +
" where price between ? and ? order by price limit ?,?";
return queryForList(Book.class,sql,min,max,begin,pageSize);
}

修改index.jsp向服务器提交搜索请求:

1
2
3
4
5
6
7
8
<div class="book_cond">
<form action="client/clientBookServlet" method="get">
<input type="hidden" name="action" value="pageByPrice"/>
价格:<input id="min" type="text" name="min" value="${param.min}"> 元 -
<input id="max" type="text" name="max" value="${param.max}">
<input type="submit" value="查询"/>
</form>
</div>

十一、保存用户登录之后的信息

在UserServlet添加保存用户登录信息到Session域中的代码:

在login_success_menu.jsp中 显示用户姓名:

修改index.jsp的菜单显示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div>
<%-- 如果用户还没有登录,显示登录和注册菜单 --%>
<c:if test="${empty sessionScope.user}">
<a href="pages/user/login.jsp">登录</a>/
<a href="pages/user/regist.jsp">注册</a>
</c:if>

<%-- 如果用户已经登录,则显示登录成功之后用户的信息 --%>
<c:if test="${not empty sessionScope.user}">
<span>欢迎<span class="um_span">${sessionScope.user.username}</span>光临书城</span>
<a href="pages/order/order.jsp">我的订单</a>
<a href="pages/cart/cart.jsp">购物车</a>
<a href="pages/manager/manager.jsp">后台管理</a>
<a href="index.jsp">注销</a>&nbsp;
</c:if>
</div>

注意:
固定相对路径跳转使用的地址必须是相对路径,否则在进行跳转时Tomcat会创建一个新的Session,造成Session会话信息丢失!

十二、注销登录

在userServlet中添加logout()方法用于处理注销请求:

1
2
3
4
5
6
protected void logout(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1.销毁 Session
req.getSession().invalidate();
// 2.重定向到首页
resp.sendRedirect(req.getContextPath());
}

修改jsp页面的跳转地址:

十三、使用验证码解决表单的重复提交

表单重复提交有三种常见的情况:
一:提交完表单。服务器使用请求转来进行页面跳转。这个时候,用户按下功能键F5,就会发起最后一次的请求。造成表单重复提交问题。解决方法:使用重定向来进行跳转。
二:用户正常提交服务器,但是由于网络延迟等原因,迟迟未收到服务器的响应,这个时候,用户以为提交失败,
就会着急,然后多点了几次提交操作,也会造成表单重复提交。
三:用户正常提交服务器。服务器也没有延迟,但是提交完成后,用户回退浏览器。重新提交。也会造成表单重复
提交。

1.导入谷歌验证码的jar包:kaptcha-2.3.2.jar
2.在web.xml中配置用于访问生成验证码的Servlet程序的地址

1
2
3
4
5
6
7
8
<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>/kaptcha.jpg</url-pattern>
</servlet-mapping>

3.在表单中使用img标签显示验证码图片

1
2
3
4
5
6
<label>验证码:</label>
<input class="itxt" type="text" style="width: 150px;" name="code" id="code"/>
<img id="code_img" alt="" src="kaptcha.jpg" style="float: right; height: 40px; width:120px">
<br/>
<span style="float: right;font-size: 80%">看不清?点击图片刷新</span>
<br/>

4.在服务端中获取生成的验证码并于与客户端发送过来的验证码比较

5.给验证码图片绑定单击事件用于刷新验证码:

1
2
3
4
5
// 给验证码图片绑定单击事件
$("#code_img").click(function () {
// 添加一个随机数避免请求地址相同,因为浏览器缓存造成不刷新
this.src = "${basePath}kaptcha.jpg?n="+ Math.random();
});

十四、购物车模块

1、创建购物车对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
public class Cart {
// private Integer totalCount;// 商品总数
// private BigDecimal totalPrice;// 总商品金额
private Map<Integer, CartItem> items = new LinkedHashMap<Integer, CartItem>();

public Integer getTotalCount() {
Integer totalCount = 0;
for (Map.Entry<Integer, CartItem> entry : items.entrySet()) {
totalCount += entry.getValue().getCount();
}
return totalCount;
}

public BigDecimal getTotalPrice() {
BigDecimal totalPrice = new BigDecimal(0);
for (Map.Entry<Integer, CartItem> entry : items.entrySet()) {
totalPrice = totalPrice.add(entry.getValue().getTotalPrice());
}
return totalPrice;
}

public Map<Integer, CartItem> getItems() {
return items;
}

public void setItems(Map<Integer, CartItem> items) {
this.items = items;
}

@Override
public String toString() {
return "Cart{" +
"totalCount=" + getTotalCount() +
", totalPrice=" + getTotalPrice() +
", items=" + items +
'}';
}

// 添加商品项
public void addItem(CartItem cartItem) {
// 判断商品是否已经添加
CartItem item = items.get(cartItem.getId());
if (item == null) {
items.put(cartItem.getId(), cartItem);
} else {
item.setCount(item.getCount() + 1);
item.setTotalPrice(item.getPrice().multiply(new BigDecimal(item.getCount())));
}
}

// 删除商品项
public void deleteItem(Integer id) {
items.remove(id);
}

// 清空购物车
public void clean() {
items.clear();
}

// 修改商品数量
public void updateCount(Integer id, Integer count) {
CartItem item = items.get(id);
if (item != null) {
item.setCount(count);
item.setTotalPrice(item.getPrice().multiply(new BigDecimal(item.getCount())));
}
}
}

2、创建购物车商品项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
public class CartItem {
private Integer id;
private String name;
private Integer count; // 数量
private BigDecimal price; // 单价
private BigDecimal totalPrice;// 商品总价

public CartItem() {
}

public CartItem(Integer id, String name, Integer count, BigDecimal price, BigDecimal totalPrice) {
this.id = id;
this.name = name;
this.count = count;
this.price = price;
this.totalPrice = totalPrice;
}

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Integer getCount() {
return count;
}

public void setCount(Integer count) {
this.count = count;
}

public BigDecimal getPrice() {
return price;
}

public void setPrice(BigDecimal price) {
this.price = price;
}

public BigDecimal getTotalPrice() {
return totalPrice;
}

public void setTotalPrice(BigDecimal totalPrice) {
this.totalPrice = totalPrice;
}

@Override
public String toString() {
return "CartItem{" +
"id=" + id +
", name='" + name + '\'' +
", count=" + count +
", price=" + price +
", totalPrice=" + totalPrice +
'}';
}
}

3、CartServlet用于处理商品的添加、删除、清空和修改数量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
public class CartServlet extends BaseServlet {
private BookService bookService = new BookServiceImpl();

protected void addItem(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// System.out.println("加入购物车");
// 获取请求参数 商品编号
int id = WebUtils.parseInt(req.getParameter("id"),0);
// 调用bookService.queryBookById(id) 得到图书信息
Book book = bookService.queryBookById(id);
// 将图书信息转换位CartItem商品项
CartItem cartItem = new CartItem(book.getId(),book.getName(),1,book.getPrice(),book.getPrice());
// 调用cart.add(CartItem)添加至购物车
Cart cart = (Cart) req.getSession().getAttribute("cart");
if(cart == null){
cart = new Cart();
req.getSession().setAttribute("cart",cart);
}
cart.addItem(cartItem);
// System.out.println(cart);
// 重定向到原来商品所在的页面
resp.sendRedirect(req.getHeader("Referer"));
}

protected void deleteItem(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取请求参数 id
int id = WebUtils.parseInt(req.getParameter("id"),0);
// 调用cart.deleteItem(id)
Cart cart = (Cart) req.getSession().getAttribute("cart");
if(cart != null){
cart.deleteItem(id);
}
resp.sendRedirect(req.getHeader("Referer"));
}

protected void clear(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取购物车对象
Cart cart = (Cart) req.getSession().getAttribute("cart");
// 调用cart.clear()
if(cart!=null){
cart.clear();
}
resp.sendRedirect(req.getHeader("Referer"));
}

protected void updateCount(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取请求的参数 商品编号 商品数量
int id = WebUtils.parseInt(req.getParameter("id"),0);
int count = WebUtils.parseInt(req.getParameter("count"), 1);
// 获取Cart对象
Cart cart = (Cart) req.getSession().getAttribute("cart");
if(cart!=null){
// 调用updateCount方法
cart.updateCount(id,count);
}
resp.sendRedirect(req.getHeader("Referer"));
}
}

4、购物车页面的展示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
<script type="text/javascript">
$(function () {
// 给删除商品绑定单击事件
$("a.deleteItem").click(function () {
return confirm("确定从购物车中删除《"+$(this).parent().parent().find("td:first").text()+"》吗?")
});
// 给清空购物车绑定单击事件
$("#clearCart").click(function(){
return confirm("确定清空购物车?");
});
// 给修改商品数量绑定失去焦点事件 --- onchange事件判断内容是否改变
$(".updateCount").change(function(){
var id = $(this).attr("bookId");
var count = this.value;
if(confirm("确定修改商品的数量为 "+count+" 吗?")&&count>0){
location.href="${PageScope.basePath}cartServlet?action=updateCount&count="+count+"&id="+id;
}else{
this.value = this.defaultValue;// 恢复到原来的数据
}
});
})
</script>
<body>
<div id="header">
<span class="wel_word">购物车</span>
<%-- 静态包含,登录成功之后的菜单 --%>
<%@include file="/pages/common/login_sucess_menu.jsp" %>
</div>
<div id="main">
<table>
<tr>
<td>商品名称</td>
<td>数量</td>
<td>单价</td>
<td>金额</td>
<td>操作</td>
</tr>
<c:if test="${empty sessionScope.cart.items}">
<tr>
<td colspan="5"><a href="index.jsp">当前购物车为空!点击浏览商品</a></td>
</tr>
</c:if>

<c:if test="${not empty sessionScope.cart.items}">
<%-- 购物车非空才输出--%>
<c:forEach items="${sessionScope.cart.items}" var="entry">
<tr>
<td>${entry.value.name}</td>
<td>
<input class="updateCount"
style="width: 80px;text-align: center"
bookId="${entry.value.id}"
type="number" value="${entry.value.count}">
</td>
<td>${entry.value.price}</td>
<td>${entry.value.totalPrice}</td>
<td><a class="deleteItem" href="cartServlet?action=deleteItem&id=${entry.value.id}">删除</a></td>
</tr>
</c:forEach>
</c:if>
</table>
<c:if test="${not empty sessionScope.cart.items}">
<div class="cart_info">
<span class="cart_span">购物车中共有<span class="b_count">${sessionScope.cart.totalCount}</span>件商品</span>
<span class="cart_span">总金额<span class="b_price">${sessionScope.cart.totalPrice}</span></span>
<span class="cart_span"><a id="clearCart" href="cartServlet?action=clear">清空购物车</a></span>
<span class="cart_span"><a href="pages/cart/checkout">去结账</a></span>
</div>
</c:if>
</div>

5、修改首页

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 添加到购物车
$("button.addToCart").click(function(){
/**
* 在事件响应的function函数中 this表示正在响应的dom对象 $(this)表示经过jquery封装后的dom对象
* @type {jQuery}
*/
// 判断用户是否登录
if(${sessionScope.user == null}){
alert("您还没有登录,请先登录或注册!")
location.href ="${pageScope.basePath}pages/user/login.jsp";
} else{
var bookid =$(this).attr("bookid");// 获取选中元素的属性值
location.href="${pageScope.basePath}cartServlet?action=addItem&id="+bookid;
}
});
1
2
3
<div class="book_add">
<button bookid="${book.id}" class="addToCart">加入购物车</button>
</div>

6、首页购物车数据回显

在addItem()中将添加的图书信息保存到session域中:

在首页显示需要的信息:

1
2
3
4
5
6
7
8
9
10
<span>您的购物车中有<span style="color: blue">${sessionScope.cart.totalCount}</span>件商品</span>
<span>
<%-- 购物车为空--%>
<c:if test="${empty sessionScope.cart.items}">
<span style="color: red">当前购物车为空~</span>
</c:if>
<c:if test="${not empty sessionScope.cart.items}">
您刚刚将<span style="color: red">${sessionScope.itemName}</span>加入到了购物车中
</c:if>
</span>

十五、订单模块

1、创建Order 与OrderItem对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
public class Order {
private String orderId;// 订单号
private Date createTime;// 下单时间
private BigDecimal price;// 订单价格
private Integer status = 0;// 0 未发货 1 已发货 2 已签收
private Integer userId;

public Order() {
}

public Order(String orderId, Date createTime, BigDecimal price, Integer status, Integer userId) {
this.orderId = orderId;
this.createTime = createTime;
this.price = price;
this.status = status;
this.userId = userId;
}

public String getOrderId() {
return orderId;
}

public void setOrderId(String orderId) {
this.orderId = orderId;
}

public Date getCreateTime() {
return createTime;
}

public void setCreateTime(Date createTime) {
this.createTime = createTime;
}

public BigDecimal getPrice() {
return price;
}

public void setPrice(BigDecimal price) {
this.price = price;
}

public Integer getStatus() {
return status;
}

public void setStatus(Integer status) {
this.status = status;
}

public Integer getUserId() {
return userId;
}

public void setUserId(Integer userId) {
this.userId = userId;
}

@Override
public String toString() {
return "Order{" +
"orderId=" + orderId +
", createTime=" + createTime +
", price=" + price +
", status=" + status +
", userId=" + userId +
'}';
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
public class OrderItem {
private Integer id;// 商品编号
private String name;// 商品名称
private Integer count;// 商品数量
private BigDecimal price;// 商品总价格
private BigDecimal totalPrice;// 商品总价格
private String orderId;// 订单号

public OrderItem() {
}

public OrderItem(Integer id, String name, Integer count, BigDecimal price, BigDecimal totalPrice, String orderId) {
this.id = id;
this.name = name;
this.count = count;
this.price = price;
this.totalPrice = totalPrice;
this.orderId = orderId;
}

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Integer getCount() {
return count;
}

public void setCount(Integer count) {
this.count = count;
}

public BigDecimal getTotalPrice() {
return totalPrice;
}

public void setTotalPrice(BigDecimal totalPrice) {
this.totalPrice = totalPrice;
}

public String getOrderId() {
return orderId;
}

public void setOrderId(String orderId) {
this.orderId = orderId;
}

public BigDecimal getPrice() {
return price;
}

public void setPrice(BigDecimal price) {
this.price = price;
}

@Override
public String toString() {
return "OrderItem{" +
"id=" + id +
", name='" + name + '\'' +
", count=" + count +
", price=" + price +
", totalPrice=" + totalPrice +
", orderId=" + orderId +
'}';
}
}

2、创建OrderDao接口并实现接口中的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public interface OrderDao {
/**
* 保存订单
* @param order
* @return
*/
public int saveOrder(Order order);

/**
* 查询全部订单
* @return
*/
public List<Order> queryOrders();

/**
* 修改订单状态
* @param orderId
* @param Status
* @return
*/
public int changeOrderStatus(String orderId,Integer status);

/**
* 根据用户编号查看订单信息
* @param UserId
* @return
*/
public List<Order> queryOrderByUserId(Integer userId);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class OrderDaoImpl extends BaseDao implements OrderDao {
@Override
public int saveOrder(Order order) {
String sql = "insert into bookorder(`order_id`,`create_time`,`price`,`status`,`user_id`)values(?,?,?,?,?)";
return update(sql,order.getOrderId(),order.getCreateTime(),order.getPrice(),order.getStatus(),order.getUserId());
}

@Override
public List<Order> queryOrders() {
String sql = "select `order_id` orderId ,`create_time` createTime,`price` ,`status`,`user_id` userId from bookorder";
return queryForList(Order.class,sql);
}

@Override
public int changeOrderStatus(String orderId, Integer status) {
String sql = "update bookorder set status = ? where order_id = ?";
return update(sql,status,orderId);
}

@Override
public List<Order> queryOrderByUserId(Integer userId) {
String sql = "select `order_id` orderId ,`create_time` createTime,`price` ,`status`,`user_id` userId from bookorder where user_id=?";
return queryForList(Order.class,sql,userId);
}
}

3、创建 OrderItemDao接口并实现其方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public interface OrderItemDao {
/**
* 保存订单项
* @param item
* @return
*/
public int saveOrderItem(OrderItem item);

/**
* 更具订单号查询订单详情
* @param orderId
* @return
*/
public OrderItem queryOrderItemsByOrderId(String orderId);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
public class OrderItemDaoImpl extends BaseDao implements OrderItemDao {
@Override
public int saveOrderItem(OrderItem item) {
String sql = "insert into bookorder_item(`name`,`count`,`price`,`totalprice`,`order_id`)values(?,?,?,?,?)";
return update(sql,item.getName(),item.getCount(),item.getPrice(),item.getTotalPrice(),item.getOrderId());
}

@Override
public OrderItem queryOrderItemsByOrderId(String orderId) {
String sql = "select `name`,`count`,`price`,`totalprice` totalPrice,`order_id` orderId from bookorder_item where order_id = ?";
return queryForOne(OrderItem.class,sql,orderId);
}
}

4、创建OrderService并实现其中的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public interface OrderService {
/**
* 生成订单
* @param cart
* @param userId
* @return 订单号
*/
public String createOrder(Cart cart,Integer userId);

/**
* 查询全部订单
* @return
*/
public List<Order> showAllOrder();

/**
* 发货
* @param orderId
*/
public void sendOrder(String orderId);

/**
* 查看订单详情
* @param orderId
* @return
*/
public OrderItem showOrderDetailById(String orderId);

/**
* 用户查看订单
* @param userId
* @return
*/
public List<Order> showMyOrder(Integer userId);

/**
* 签收订单
* @param orderId
*/
public void receiverOrder(String orderId);

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
public class OrderServiceImpl implements OrderService {

private OrderDao orderDao = new OrderDaoImpl();
private OrderItemDao orderItemDao = new OrderItemDaoImpl();
private BookDao bookDao = new BookDaoImpl();

@Override
public String createOrder(Cart cart, Integer userId) {
// 订单号唯一
String orderId = System.currentTimeMillis() + "" + userId;
// 创建一个订单对象
Order order = new Order(orderId, new Date(), cart.getTotalPrice(), 0, userId);
// 保存 订单到数据库
orderDao.saveOrder(order);
// 保存订单项
// 创建订单项
// 遍历购物车中的每一个商品项转换为订单商品项
for (Map.Entry<Integer, CartItem> entry : cart.getItems().entrySet()) {
CartItem cartItem = entry.getValue();
OrderItem orderItem = new OrderItem(null, cartItem.getName(), cartItem.getCount(), cartItem.getPrice(), cartItem.getTotalPrice(), orderId);
orderItemDao.saveOrderItem(orderItem);

// 修改图书库存和销量
// 获取book对象
Book book = bookDao.queryBookById(cartItem.getId());
book.setSales(book.getSales() + cartItem.getCount());
book.setStock(book.getStock() - cartItem.getCount());
bookDao.updateBook(book);
}
// 清空购物车
cart.clear();
return orderId;
}

@Override
public List<Order> showAllOrder() {
return orderDao.queryOrders();
}

@Override
public void sendOrder(String orderId) {
orderDao.changeOrderStatus(orderId,1);
}

@Override
public OrderItem showOrderDetailById(String orderId) {
return orderItemDao.queryOrderItemsByOrderId(orderId);
}

@Override
public List<Order> showMyOrder(Integer userId) {
return orderDao.queryOrderByUserId(userId);
}

@Override
public void receiverOrder(String orderId) {
orderDao.changeOrderStatus(orderId,2);
}
}

5、创建OrderServlet并添加相应的功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
public class OrderServlet extends BaseServlet {

private OrderService orderService = new OrderServiceImpl();
/**
* 生成订单
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void createOrder(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取Cart对象
Cart cart = (Cart) req.getSession().getAttribute("cart");
// 获取userId
User user = (User) req.getSession().getAttribute("user");
Integer userId = user.getId();
// 调用orderService
String orderId = orderService.createOrder(cart, userId);
req.getSession().setAttribute("orderId",orderId);
// 重定向结算成功页面
resp.sendRedirect(req.getContextPath()+"/pages/cart/checkout.jsp");
}

/**
* 查询我的订单
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void showMyOrders(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取userId
User user = (User) req.getSession().getAttribute("user");
Integer userId = user.getId();
// 得到order对象
List<Order> orders= orderService.showMyOrder(userId);
// 保存至request中
req.setAttribute("orders",orders);
// 请求转发到我的订单页面
req.getRequestDispatcher("/pages/order/order.jsp").forward(req,resp);
}

/**
* 查看商品详情
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void showOrderDetail(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取orderId
String orderId = req.getParameter("orderId");
// System.out.println(orderId);
// 创建OrderItem对象
// 调用oderService.showOrderDetailById()
OrderItem orderItem = orderService.showOrderDetailById(orderId);
req.setAttribute("orderItem",orderItem);
req.getRequestDispatcher("/pages/order/order_detail.jsp").forward(req,resp);
}

/**
* 显示所有订单
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void showAllOrders(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
List<Order> allOrders = orderService.showAllOrder();
req.setAttribute("allOrders",allOrders);
req.getRequestDispatcher("/pages/manager/order_manager.jsp").forward(req,resp);
}

/**
* 发货
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void sendOrder(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取orderId
String orderId = req.getParameter("orderId");
orderService.sendOrder(orderId);
resp.sendRedirect(req.getContextPath()+"/orderServlet?action=showAllOrders");
}

/**
* 确认收货
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void receiverOrder(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取orderId
String orderId = req.getParameter("orderId");
orderService.receiverOrder(orderId);
resp.sendRedirect(req.getContextPath()+"/orderServlet?action=showMyOrders");
}
}

5、调整jsp页面

1.修改购物车页面中去结算的跳转地址

2.在order.jsp中显示当前用户的订单信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<table>
<tr>
<td>日期</td>
<td>金额</td>
<td>状态</td>
<td>详情</td>
<td>确认收货</td>
</tr>
<c:if test="${not empty requestScope.orders}">
<c:forEach items="${requestScope.orders}" var="order">
<tr>
<td>${order.createTime}</td>
<td>${order.price}</td>
<td>
<c:choose>
<c:when test="${order.status==0}">未发货</c:when>
<c:when test="${order.status==1}">已发货</c:when>
<c:otherwise>已签收</c:otherwise>
</c:choose>
</td>
<td><a href="orderServlet?action=showOrderDetail&orderId=${order.orderId}">查看详情</a></td>
<td><c:choose>
<c:when test="${order.status==0}"><span style="color: red">还没有发货哦~</span></c:when>
<c:when test="${order.status==1}"><a href="orderServlet?action=receiverOrder&orderId=${order.orderId}">确认收货</a></c:when>
<c:otherwise><span style="color: red">已经签收了~</span></c:otherwise>
</c:choose>
</td>
</tr>
</c:forEach>
</c:if>
<c:if test="${empty requestScope.orders}">
<td colspan="4"><a href="pages/cart/cart.jsp">您暂时没有订单,快去购物车下单吧~</a></td>
</c:if>
</table>

4.在order_detail.jsp中显示订单的详细信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<table>
<tr>
<td>商品名称</td>
<td>数量</td>
<td>单价</td>
<td>总价</td>
<td>订单号</td>
</tr>
<tr>
<td>${requestScope.orderItem.name}</td>
<td>${requestScope.orderItem.count}</td>
<td>${requestScope.orderItem.price}</td>
<td>${requestScope.orderItem.totalPrice}</td>
<td>${requestScope.orderItem.orderId}</td>
</tr>
</table>

5.修改首页的后台管理中订单管理的跳转地址

6.在订单管理中显示所有的订单信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<table>
<tr>
<td>日期</td>
<td>金额</td>
<td>详情</td>
<td>状态</td>
<td>发货</td>
</tr>
<c:if test="${not empty requestScope.allOrders}">
<c:forEach items="${requestScope.allOrders}" var="order">
<tr>
<td>${order.createTime}</td>
<td>${order.price}</td>
<td><a href="orderServlet?action=showOrderDetail&orderId=${order.orderId}">查看详情</a></td>
<td>
<c:choose>
<c:when test="${order.status==0}">未发货</c:when>
<c:when test="${order.status==1}">已发货</c:when>
<c:otherwise>已签收</c:otherwise>
</c:choose>
</td>
<td>
<c:choose>
<c:when test="${order.status==0}">
<a href="orderServlet?action=sendOrder&orderId=${order.orderId}">发货</a>
</c:when>
<c:when test="${order.status==1}"><span style="color: red">已经发货了~</span></c:when>
<c:otherwise><span style="color: red">已经签收了~</span></c:otherwise>
</c:choose>
</td>
</tr>
</c:forEach>
</c:if>
<c:if test="${empty requestScope.allOrders}">
<td colspan="5">暂时没有订单</td>
</c:if>
</table>

十六、使用Filter实现权限检查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.bookmall.filter;

public class ManagerFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {

}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
User user = (User) httpServletRequest.getSession().getAttribute("user");
if(user.getStatus()!=1){
httpServletRequest.getRequestDispatcher("/pages/user/login.jsp").forward(request,response);
}else{
chain.doFilter(request,response);
}
}

@Override
public void destroy() {

}
}
1
2
3
4
5
6
7
8
9
<filter>
<filter-name>ManagerFilter</filter-name>
<filter-class>com.bookmall.filter.ManagerFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ManagerFilter</filter-name>
<url-pattern>/pages/manager/*</url-pattern>
<url-pattern>/manager/bookServlet</url-pattern>
</filter-mapping>

十七、使用Filter和TheadLocal组合管理事务

修改JdbcUtils:
创建一个ThreadLocal对象,用于将获取的连接与当前线程关联:

修改getConnection():

1
2
3
4
5
6
7
8
9
10
11
12
13
public static Connection getConnection() {
Connection conn = conns.get();
if (conn == null) {
try {
conn = dataSource.getConnection();// 从数据库连接池中获取连接
conns.set(conn);// 将连接保存到ThreadLocal对象中,供后面的jdbc操作使用,保证执行事务时是同一个连接
conn.setAutoCommit(false);// 设置为不自动提交事务
} catch (SQLException e) {
e.printStackTrace();
}
}
return conn;
}

添加提交并关闭连接的方法以及回滚并关闭连接的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
 /**
* 提交事务并关闭连接
*/
public static void commitAndClose(){
Connection conn = conns.get();
if(conn!=null){
try {
conn.commit();// 提交事务
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
try {
conn.close();// 关闭连接
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
// 移除线程
conns.remove();
}

/**
* 回滚事务并关闭连接
*/
public static void rollbackAndClose(){
Connection conn = conns.get();
if(conn !=null){
try {
conn.rollback();
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}

修改BaseDao,BaseServlet:
1、将所有方法中的关闭连接操作删除,用于保证所用操作都使用同一个连接;
2、在每个方法中都添加抛出异常的语句,提供给执行相应操作的Servlet程序捕获,用于回滚。
使用Filter过滤器为所用的Service方法添加try-catch:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.bookmall.filter;

public class TransactionFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {

}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
try {
chain.doFilter(request,response);
// 提交事务
JdbcUtils.commitAndClose();
} catch (Exception e) {
// 回滚事务
JdbcUtils.rollbackAndClose();
// 将异常抛给服务器
throw new RuntimeException(e);
}
}

@Override
public void destroy() {

}
}
1
2
3
4
5
6
7
8
9
<filter>
<filter-name>TransactionFilter</filter-name>
<filter-class>com.bookmall.filter.TransactionFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>TransactionFilter</filter-name>
<!-- 当前工程下的所有请求 -->
<url-pattern>/*</url-pattern>
</filter-mapping>

将所有异常同一交给Tomcat,让Tomcat展示友好的错误信息页面:
配置web.xml

1
2
3
4
5
6
7
8
9
10
11
<!-- 配置服务器出错后,自动跳转的页面-->
<error-page>
<!-- 错误类型-->
<error-code>404</error-code>
<!-- 要跳转去的页面路径-->
<location>/pages/error/error404.jsp</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/pages/error/error500.jsp</location>
</error-page>

十八、使用Ajax判断用户名是否可用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 给用户名输入框绑定失去焦点事件
$("#username").blur(function () {
// 获取用户名
var username = this.value;
$.getJSON(
"${pageScope.basePath}userServlet",
"action=ajaxExistUsername&username=" + username,
function (data) {
if (data.existUsername) {
$("span.errorMsg").css({color:"#dd0000"});
$("span.errorMsg").text("用户名已存在!");
} else {
$("span.errorMsg").css({color:"#00dd00"});
$("span.errorMsg").text("用户名可用~");
}
});
});

在UserServlet中添加ajaxExisUsername()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 验证用户名是否已经注册
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void ajaxExistUsername(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{
// 获取请求参数
String username = req.getParameter("username");
// 调用userService
boolean existUsername = userService.existUsername(username);
// 把返回的结果封装成Map对象
Map<String,Object> resultMap = new HashMap<String, Object>();
resultMap.put("existUsername",existUsername);
// 将Map对象转换为json字符串
Gson gson = new Gson();
String json = gson.toJson(resultMap);
resp.getWriter().write(json);
}

十九、使用Ajax修改把商品添加到购物车

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/**
* 使用Ajax添加图书到购物车
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void ajaxAddItem(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// System.out.println("加入购物车");
// 获取请求参数 商品编号
int id = WebUtils.parseInt(req.getParameter("id"),0);
// 调用bookService.queryBookById(id) 得到图书信息
Book book = bookService.queryBookById(id);
// 将图书信息转换为CartItem商品项
CartItem cartItem = new CartItem(book.getId(),book.getName(),1,book.getPrice(),book.getPrice());
// 调用cart.add(CartItem)添加至购物车
Cart cart = (Cart) req.getSession().getAttribute("cart");
if(cart == null){
cart = new Cart();
req.getSession().setAttribute("cart",cart);
}
cart.addItem(cartItem);
// 将添加的商品添加到session域中
req.getSession().setAttribute("itemName",cartItem.getName());
// 将需要在页面刷新的信息封装到Map中
Map<String,Object> resultMap = new HashMap<String, Object>();
// 购物车中总的商品数量
resultMap.put("totalCount",cart.getTotalCount());
// 最后一个添加到购物车的商品名称
resultMap.put("lastName",cartItem.getName());
// 将Map转换为json字符串
Gson gson = new Gson();
String json = gson.toJson(resultMap);
// 回传给客户端
resp.getWriter().write(json);
}
1
2
3
4
5
6
7
8
9
10
var bookid = $(this).attr("bookid");// 获取选中元素的属性值
// location.href="${pageScope.basePath}cartServlet?action=addItem&id="+bookid;
// 使用ajax发送请求
$.getJSON(
"${pageScope.basePath}cartServlet",
"action=ajaxAddItem&id=" + bookid,
function (data) {
$("#cartTotalCount").text(data.totalCount);
$("#cartlastName").text(data.lastName);
})
1
2
3
4
<span>您的购物车中有<span id="cartTotalCount" style="color:red">${sessionScope.cart.totalCount}</span>件商品</span>
<span>
您刚刚将<span id="cartlastName"style="color:red">${sessionScope.itemName}</span>加入到了购物车中
</span>