书城项目第一阶段:表单验证

需求:

        验证用户名:必须由字母,数字下划线组成,并且长度为 5 到 12 位
        验证密码:必须由字母,数字下划线组成,并且长度为 5 到 12 位
        验证确认密码:和密码相同
        邮箱验证:xxxxx@xxx.com
        验证码:现在只需要验证用户已输入。

代码:

// 页面加载完成之后
$(function () {// 给注册绑定单击事件$("#sub_btn").click(function () {// 验证用户名:必须由字母,数字下划线组成,并且长度为 5 到 12 位// 1 获取用户名输入框里的内容var username = $("#username").val();// 2 创建正则表达式对象var usernamePatt = /^\w{5,12}$/;// 3 使用 test 方法验证if (!usernamePatt.test(username)) {// 4 提示用户结果$("span.errorMsg").text("用户名不合法!");return false;}// 验证密码:必须由字母,数字下划线组成,并且长度为 5 到 12 位// 1 获取用户密码输入框里的内容var password = $("#password").val();// 2 创建正则表达式对象var passwordPatt = /^\w{5,12}$/;// 3 使用 test 方法验证if (!passwordPatt.test(password)) {// 4 提示用户结果$("span.errorMsg").text("密码不合法!");return false;}// 验证确认密码:和密码相同// 1 获取正确密码输入框里的内容var repwd = $("#repwd").val();// 2 与密码相比较if (repwd != password) {// 4 提示用户$("span.errorMsg").text("确认密码与密码不一致!");return false;}// 邮箱验证:xxxxx@xxx.com// 1 获取邮箱里的内容var email = $("#email").val();// 2 创建正则表达式对象var emailPatt = /^[a-z\d]+(\.[a-z\d]+)*@([\da-z](-[\da-z])?)+(\.{1,2}[a-z]+)+$/;// 3 用test方法验证if (!emailPatt.test(email)) {$("span.errorMsg").text("邮箱格式不正确!");return false;}// 验证码:现在只需要验证用户已输入。因为还没讲到服务器。验证码生成。// 1.获取验证码里面的内容var code = $("#code").val();// 2.去掉验证码前后空格code = $.trim(code);//3.判断全掉空格后的验证码是否为空if (code == null || code == "") {// 提示用户$("span.errorMsg").text("验证码不能为空!");return false;}});// 给验证码绑定单击事件$("#code_img").click(function () {// 在事件响应的 function 函数中有一个 this 对象。//  这个 this 对象,是当前正在响应事件的 dom 对象//  src 属性表示验证码 img 标签的 图片路径。它可读,可写this.src="kaptcha.jpg?id=" + new Date();});
});

书城第二阶段——用户注册和登陆

JavaEE 项目的三层架构

分层的目的是为了解耦。解耦就是为了降低代码的耦合度。方便项目后期的维护和升级。

包结构

web 层
        com.chenyixin.web/servlet/controller
service 层
        com.chenyixin.service Service 接口包
        com.chenyixin.service.impl Service 接口实现类
dao 持久层
        com.chenyixin.dao Dao 接口包
        com.chenyixin.dao.impl Dao 接口实现类
实体 bean 对象         
        com.chenyixin.pojo/entity/domain/bean         JavaBean 类
测试包         
        com.chenyixin.test/junit
工具类        
        com.chenyixin.util

1. 先创建书城需要的数据库和表。

思路:

        1.创建数据库book

        2.切换数据库到book

        3.创建表t_user,用于存储用户信息

4.插入一条数据进行测试

代码:

CREATE DATABASE book;
USE book;CREATE TABLE t_user(id INT PRIMARY KEY AUTO_INCREMENT,`username` VARCHAR(25) NOT NULL UNIQUE,`password` VARCHAR(32) NOT NULL,email VARCHAR(200)
);INSERT INTO t_user(`username`,`password`,email)
VALUES('admin','admin','admin@123');SELECT * FROM t_user;

2. 编写数据库表对应的 JavaBean 对象。

思路:在pojo中编写一个User类

代码:(构造器与set、get、toString略)

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

3. 编写工具类 JdbcUtils

3.1 导入需要的 jar 包(数据库和连接池需要)

druid-1.1.9.jar
mysql-connector-java-8.0.27.jar
以下是测试需要:
hamcrest-core-1.3.jar
junit-4.12.jar

3.2 在 src 源码目录下编写 jdbc.properties 属性配置文件

username=root
password=root
url=jdbc:mysql://localhost:3306/book
driverClassName=com.mysql.jdbc.Driver
initialSize=5
maxActive=10

3.3、编写 JdbcUtils 工具类

思路:

        1.在utils包中创建JdbcUtils类,在其静态代码块中创建数据库连接

        2. 编写 获取数据库连接的方法

        3.编写 关闭数据可连接的方法

代码:

public class JdbcUtils {private static DruidDataSource dataSource;// 在静态代码块中创建数据库连接static {try {// 获取配置文件jdbc.properties对象Properties 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,说明获取连接失败<br/>有值就是获取连接成功*/public static Connection getConnection() {Connection conn = null;try {conn = dataSource.getConnection();} catch (SQLException e) {e.printStackTrace();}return conn;}/*** 关闭连接,放回数据库连接池** @param connection 放入要关闭的连接对象*/public static void close(Connection connection) {try {if (connection != null) {connection.close();}} catch (SQLException e) {e.printStackTrace();}}
}

3.4 JdbcUtils 测试

在tset包中创建JdbcUtilsTest 进行测试

public class JdbcUtilsTest {public static void main(String[] args) {for (int i = 0; i < 100; i++) {Connection conn = JdbcUtils.getConnection();System.out.println(conn);JdbcUtils.close(conn);}}
}

4. 编写 BaseDao

4.1 导入 DBUtils 的 jar 包

        commons-dbutils-1.3.jar

4.2  编写 BaseDao

思路:

        1.在dao包中创建BaseDao,并在其类中创建DbUtils 操作数据库的对象

        2.编写update方法用于对数据库的增删改

        3.创建 查询返回一个 javaBean 的 sql 语句 的方法

        4.创建 查询返回多个 javaBean 的 sql 语句 的方法

        5.创建 执行返回一行一列的 sql 语句 的方法

代码:
public abstract class BaseDao {private final QueryRunner queryRunner = new QueryRunner();/*** 用来执行:Insert\Update\Delete 语句** @param sql  传入要执行的sql语句* @param args 参数列表* @return 返回-1表示update失败,否则成功*/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;}/*** 查询返回一个 javaBean 的 sql 语句* @param type 返回的对象类型* @param sql 执行的 sql 语句* @param args sql 对应的参数值* @param <T> 返回的类型的泛型* @return 若返回为null,则查看失败*/public <T> T queryForOne(Class<T> type, String sql, Object... args) {Connection conn = JdbcUtils.getConnection();try {return queryRunner.query(conn, sql, new BeanHandler<T>(type), args);} catch (SQLException e) {e.printStackTrace();} finally {JdbcUtils.close(conn);}return null;}/*** 查询返回多个 javaBean 的 sql 语句** @param type 返回的对象类型* @param sql  执行的 sql 语句* @param args sql 对应的参数值* @param <T>  返回的类型的泛型* @return 若返回为null, 则查看失败*/public <T> List<T> queryForList(Class<T> type, String sql, Object... args) {Connection conn = JdbcUtils.getConnection();try {queryRunner.query(conn, sql, new BeanListHandler<>(type), args);} catch (SQLException e) {e.printStackTrace();} finally {JdbcUtils.close(conn);}return null;}/*** 执行返回一行一列的 sql 语句* @param sql 执行的 sql 语句* @param args sql对应的参数* @return 若返回为null, 则查看失败*/public Object queryForSingleValue(String sql,Object... args) {Connection conn = JdbcUtils.getConnection();try {queryRunner.query(conn,sql,new ScalarHandler(),args);} catch (SQLException e) {e.printStackTrace();} finally {JdbcUtils.close(conn);}return null;}
}

5. 编写 UserDao 和测试

思路:

        1.在Dao包中编写UserDao接口

        2.在UserDao接口中编写queryUserByUsername,

                queryUserByUsernameAndPassword,saveUser抽象方法

        3.在Dao.impl包中编写UserDaoImpl类,并继承BaseDao,实现UserDao

        4.在UserDaoImpl中编写重写UserDao接口中的方法

        5.在test包中创建UserDaoTest进行测试

BaseDao 接口:

public abstract class BaseDao {private final QueryRunner queryRunner = new QueryRunner();/*** 用来执行:Insert\Update\Delete 语句** @param sql  传入要执行的sql语句* @param args 参数列表* @return 返回-1表示update失败,否则成功*/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;}/*** 查询返回一个 javaBean 的 sql 语句* @param type 返回的对象类型* @param sql 执行的 sql 语句* @param args sql 对应的参数值* @param <T> 返回的类型的泛型* @return 若返回为null,则查看失败*/public <T> T queryForOne(Class<T> type, String sql, Object... args) {Connection conn = JdbcUtils.getConnection();try {return queryRunner.query(conn, sql, new BeanHandler<T>(type), args);} catch (SQLException e) {e.printStackTrace();} finally {JdbcUtils.close(conn);}return null;}/*** 查询返回多个 javaBean 的 sql 语句** @param type 返回的对象类型* @param sql  执行的 sql 语句* @param args sql 对应的参数值* @param <T>  返回的类型的泛型* @return 若返回为null, 则查看失败*/public <T> List<T> queryForList(Class<T> type, String sql, Object... args) {Connection conn = JdbcUtils.getConnection();try {queryRunner.query(conn, sql, new BeanListHandler<>(type), args);} catch (SQLException e) {e.printStackTrace();} finally {JdbcUtils.close(conn);}return null;}/*** 执行返回一行一列的 sql 语句* @param sql 执行的 sql 语句* @param args sql对应的参数* @return 若返回为null, 则查看失败*/public Object queryForSingleValue(String sql,Object... args) {Connection conn = JdbcUtils.getConnection();try {queryRunner.query(conn,sql,new ScalarHandler(),args);} catch (SQLException e) {e.printStackTrace();} finally {JdbcUtils.close(conn);}return null;}
}

UserDaoImpl 类:

public class UserDaoImpl extends BaseDao implements UserDao {@Overridepublic User queryUserByUsername(String username) {String sql = "select username,password,email from t_user where username=?";return queryForOne(User.class,sql,username);}@Overridepublic User queryUserByUsernameAndPassword(String username, String password) {String sql = "select username,password,email from t_user where username=? and password = ?";return queryForOne(User.class,sql,username,password);}@Overridepublic int saveUser(User user) {String sql = "insert into t_user(username,password,email) values(?,?,?)";return update(sql,user.getUsername(),user.getPassword(),user.getEmail());}
}

测试类:

public class UserDaoTest {UserDao userDao = new UserDaoImpl();@Testpublic void queryUserByUsername() {if (userDao.queryUserByUsername("admin134") == null) {System.out.println("用户名可用!");} else {System.out.println("用户名已存在!");}}@Testpublic void queryUserByUsernameAndPassword() {if (userDao.queryUserByUsernameAndPassword("admin", "admin1234") == null) {System.out.println("用户名或密码错误,登录失败");} else {System.out.println("查询成功");}}@Testpublic void saveUser() {System.out.println( userDao.saveUser(new User(null,"zhangsan", "123456", "zhangsan@qq.com")));}
}

6. 编写 UserService 和测试

思路:

        1.在service包中创建UserService 接口,该接口含有:

                login                           登录方法

                regist                          注册方法

                existsUsername         检查用户名是否存在的方法

        2.在service.impl包中UserServiceImpl类,并实现UserService 接口的所有方法

        3.在test包中创建UserServiceTest类进行测试

                

UserService 接口:

public interface UserService {/*** 登录* @param user 要登陆的用户* @return 如果返回null,说明登录失败,返回有值,则登录成功*/public User login(User user);/*** 注册* @param user 注册的用户*/public void regist(User user);/*** 检查用户名是否存在* @param username 用户名* @return 返回true表示用户名已经存在,返回false表示用户名可用*/public boolean existsUsername(String username);
}

UserServiceImpl类:

public class UserServiceImpl implements UserService {UserDao userDao = new UserDaoImpl();@Overridepublic User login(User user) {return userDao.queryUserByUsernameAndPassword(user.getUsername(), user.getPassword());}@Overridepublic void regist(User user) {userDao.saveUser(user);}@Overridepublic boolean existsUsername(String username) {return userDao.queryUserByUsername(username) != null;}
}

UserServiceTest类测试:


public class UserServiceTest {UserService userService = new UserServiceImpl();@Testpublic void login() {System.out.println(userService.login(new User(null, "aaa", "666666", null)));}@Testpublic void regist() {userService.regist(new User(null, "aaa", "666666", "bbj168@qq.com"));}@Testpublic void existsUsername() {if (userService.existsUsername("aaa")) {System.out.println("用户名已存在!");} else {System.out.println("用户名可用!");}}
}

7. 编写 web 层

7.1 实现用户注册的功能

修改 regist.html 和 regist_success.html 页面
1、添加 base 标签
        <!--写 base 标签,永远固定相对路径跳转的结果-->
        <base href="http://localhost:8080/book/">

2、修改 base 标签对页面中所有相对路径的影响(浏览器 F12,哪个报红,改哪个)
以下是几个修改的示例:

        <link type="text/css" rel="stylesheet" href="static/css/style.css" >
        <script type="text/javascript" src="static/script/jquery-1.7.2.js"></script>

3、修改注册表单的提交地址和请求方式

需求:

1)访问注册页面
2)填写注册信息,提交给服务器
3)服务器应该保存用户
4)当用户已经存在----提示用户注册 失败,用户名已存在
5)当用户不存在-----注册成功

分析:

1.在web包中编写RegistServlet类 并继承 HttpServlet
2、获取请求的参数
3、检查 验证码是否正确 
        正确
                4、检查 用户名是否可用
                        可用
                                调用 Service 保存到数据库
                        不可用
                                跳回注册页面
        不正确
                跳回注册页面

代码:

public class RegistServlet extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {UserService userService = new UserServiceImpl();// 获取请求的参数String username = request.getParameter("username");String password = request.getParameter("password");String email = request.getParameter("email");String code = request.getParameter("code");// 检查 验证码是否正确if (code.equalsIgnoreCase("abcde")) {// 正确 检查 用户名是否可用if (userService.existsUsername(username)) {// 不可用 跳回注册页面request.getRequestDispatcher("/pages/user/regist.html").forward(request,response);}else{// 可用 跳转到注册成功页面userService.regist(new User(null, username, password, email));
request.getRequestDispatcher("/pages/user/regist_success.html").forward(request,response);}}else{// 不正确 跳回注册页面System.out.println("验证码[" + code + "]错误");request.getRequestDispatcher("/pages/user/regist.html").forward(request,response);}}
}

xml代码略

7.2 用户登录功能的实现

修改 login.html 页面和 login_success.html 页面
1、添加 base 标签
        <!--写 base 标签,永远固定相对路径跳转的结果-->
        <base href="http://localhost:8080/book/">

2、修改 base 标签对页面中所有相对路径的影响(浏览器 F12,哪个报红,改哪个)
以下是几个修改的示例:
        <link type="text/css" rel="stylesheet" href="static/css/style.css" >
        <script type="text/javascript" src="static/script/jquery-1.7.2.js"></script>
3、修改 login.html 表单的提交地址和请求方式需求如下:
需求:
        1)访问登陆页面
        2)填写用户名密码后提交
        3)服务器判断用户是否存在
        4)如果登陆失败 --->>>> 返回用户名或者密码错误信息
        5)如果登录成功 --->>>> 返回登陆成功 信息

分析:

1.  在web包中编写LoginServlet类 并继承 HttpServlet
2、获取请求的参数
3、调用 userService.login()登录处理业务(检查用户名密码是否正确)
        正确
               跳到成功页面 login_success.html
        不正确
                跳回登录页面

代码:
public class LoginServlet extends HttpServlet {private UserService userService = new UserServiceImpl();@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 获取请求的参数String username = request.getParameter("username");String password = request.getParameter("password");// 调用 userService.login()登录处理业务User loginUser = userService.login(new User(null,username, password,null));if (loginUser == null) {// 不正确 跳回登录页面request.getRequestDispatcher("/pages/user/login.html").forward(request, response);} else {// 正确 跳转到登录成功页面request.getRequestDispatcher("/pages/user/login_success.html").forward(request, response);}}}

书城第三阶段 -- 页面动态话,代码优化

1. 页面 jsp 动态化

        1、在 html 页面顶行添加 page 指令。
        2、修改文件后缀名为:.jsp
        3、使用 IDEA 搜索替换.html 为.jsp(快捷键:Ctrl+Shift+R)

2. 抽取页面中相同的内容

2.1 head 中 css、jquery、base 标签

<%String basePath = request.getScheme() + "://" +request.getServerName() + ":" +request.getServerPort() +request.getContextPath() + "/";String basePath2 = request.getScheme() + "://" +request.getServerName() + ":" +request.getServerPort() +request.getContextPath() + "/";
%>
<html>
<head><title>Title</title><base href="<%=basePath%>"><link type="text/css" rel="stylesheet" href="static/css/style.css"><script type="text/javascript" src="static/js/script/jquery-1.7.2.min.js"></script>
</head>

2.2 每个页面的页脚

<body>
<div id="bottom"><span>尚硅谷书城.Copyright &copy;2015</span>
</div>
</body>

2.3 登录成功后的菜单

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

2.4 manager 模块的菜单

<div><a href="pages/manager/book_manager.jsp">图书管理</a><a href="pages/manager/order_manager.jsp">订单管理</a><a href="index.jsp">返回商城</a>
</div>

3. 登录,注册错误提示,及表单回显

以登录回显为示例:
Servlet 程序端需要添加回显信息到 Request 域中

表单要回显信息:

4. BaseServlet 的抽取

在实际的项目开发中,一个模块,一般只使用一个 Servlet 程序。

4.1 代码优化1:合并 LoginServlet 和 RegistServlet 程序为 UserServlet 程序

 第一步:在注册和登录界面中编写隐藏域并修改请求地址

给 login.jsp 添加隐藏域和修改请求地址

给 tegist.jsp 页面添加隐藏域 action,和修改请求地址

第二步:创建UserServlet类,并将LoginServlet类与RegistSerlet类里的代码复制进去

@WebServlet(name = "UserServlet", value = "/userServlet")
public class UserServlet extends HttpServlet {private UserService userService = new UserServiceImpl();@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {String action = request.getParameter("action");if ("login".equals(action)) {login(request, response);} else if ("regist".equals(action)) {regist(request, response);}}/*** 处理登录功能** @param request* @param response* @throws ServletException* @throws IOException*/protected void login(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 获取请求的参数String username = request.getParameter("username");String password = request.getParameter("password");// 调用 userService.login()登录处理业务User loginUser = userService.login(new User(null, username, password, null));if (loginUser == null) {// 不正确// 把错误信息,和回显的表单项信息,保存到Request域中request.setAttribute("msg", "用户名或密码错误!");request.setAttribute("username", username);// 跳回登录页面request.getRequestDispatcher("/pages/user/login.jsp").forward(request, response);} else {// 正确 跳转到登录成功页面request.getRequestDispatcher("/pages/user/login_success.jsp").forward(request, response);}}/*** 处理注册功能** @param request* @param response* @throws ServletException* @throws IOException*/protected void regist(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {UserService userService = new UserServiceImpl();// 获取请求的参数String username = request.getParameter("username");String password = request.getParameter("password");String email = request.getParameter("email");String code = request.getParameter("code");// 检查 验证码是否正确if (code.equalsIgnoreCase("abcde")) {// 正确 检查 用户名是否可用if (userService.existsUsername(username)) {// 不可用request.setAttribute("msg", "用户名已存在!");request.setAttribute("email", email);// 跳回注册页面request.getRequestDispatcher("/pages/user/regist.jsp").forward(request, response);} else {// 可用 跳转到注册成功页面userService.regist(new User(null, username, password, email));request.getRequestDispatcher("/pages/user/regist_success.jsp").forward(request, response);}} else {// 不正确request.setAttribute("msg", "验证码错误!");request.setAttribute("username", username);request.setAttribute("email", email);// 跳回注册页面request.getRequestDispatcher("/pages/user/regist.jsp").forward(request, response);}}}

4.2 优化代码2:使用反射优化大量 else if 代码

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {String action = request.getParameter("action");// 获取 action 业务鉴别字符串,获取相应的业务 方法反射对象try {Method method = this.getClass().getDeclaredMethod(action, HttpServletRequest.class, HttpServletResponse.class);// 调用目标业务 方法method.invoke(this, request, response);} catch (Exception e) {e.printStackTrace();}}

4.3 代码优化3:抽取 BaseServlet 程序。

 创建BaseServlet抽象类,并继承UserServlet类等,将子类的doPost写到父类中,使其子类可以直接调用,不用再写一遍

@WebServlet(name = "BaseServlet", value = "/BaseServlet")
public abstract class BaseServlet extends HttpServlet {private UserService userService = new UserServiceImpl();@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {String action = request.getParameter("action");// 获取 action 业务鉴别字符串,获取相应的业务 方法反射对象try {Method method = this.getClass().getDeclaredMethod(action, HttpServletRequest.class, HttpServletResponse.class);// 调用目标业务 方法method.invoke(this, request, response);} catch (Exception e) {e.printStackTrace();}}
}
修改 UserServlet 程序继承 BaseServlet 程序。

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

        BeanUtils 工具类,它可以一次性的把所有请求的参数注入到 JavaBean 中。
        BeanUtils 工具类,经常用于把 Map 中的值注入到 JavaBean 中,或者是对象属性值的拷贝操作。
        BeanUtils 它不是 Jdk 的类。而是第三方的工具类。所以需要导包。
                1、导入需要的 jar 包:
                        commons-beanutils-1.8.0.jar
                        commons-logging-1.1.1.jar
                2、编写 WebUtils 工具类使用:
WebUtils 工具类:
public class WebUtils {public static <T> T copyParamToBean(Map value, T bean) {try {// 把所有请求的参数都注入到 user 对象中 BeanUtils.populate(bean,value);} catch (Exception e) {e.printStackTrace();}return bean;}
}

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

以登录为示例:

书城第四阶段

1. 图书模块

1.1 编写图书模块的数据库表

CREATE TABLE t_book(`id` INT PRIMARY KEY AUTO_INCREMENT,`name` VARCHAR(100),`price` DECIMAL(11,2),`author` VARCHAR(100),`sales` INT,`stock` INT,`img_path` VARCHAR(200)
);

测试表:

## 插入初始化测试数据
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , 'java从入门到放弃' , '国哥' , 80 , 9999 , 9 , 'static/img/default.jpg');insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , '数据结构与算法' , '严敏君' , 78.5 , 6 , 13 , 'static/img/default.jpg');insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , '怎样拐跑别人的媳妇' , '龙伍' , 68, 99999 , 52 , 'static/img/default.jpg');insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , '木虚肉盖饭' , '小胖' , 16, 1000 , 50 , 'static/img/default.jpg');insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , 'C++编程思想' , '刚哥' , 45.5 , 14 , 95 , 'static/img/default.jpg');insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , '蛋炒饭' , '周星星' , 9.9, 12 , 53 , 'static/img/default.jpg');insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , '赌神' , '龙伍' , 66.5, 125 , 535 , 'static/img/default.jpg');insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , 'Java编程思想' , '阳哥' , 99.5 , 47 , 36 , 'static/img/default.jpg');insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , 'JavaScript从入门到精通' , '婷姐' , 9.9 , 85 , 95 , 'static/img/default.jpg');insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , 'cocos2d-x游戏编程入门' , '国哥' , 49, 52 , 62 , 'static/img/default.jpg');insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , 'C语言程序设计' , '谭浩强' , 28 , 52 , 74 , 'static/img/default.jpg');insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , 'Lua语言程序设计' , '雷丰阳' , 51.5 , 48 , 82 , 'static/img/default.jpg');insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , '西游记' , '罗贯中' , 12, 19 , 9999 , 'static/img/default.jpg');insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , '水浒传' , '华仔' , 33.05 , 22 , 88 , 'static/img/default.jpg');insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , '操作系统原理' , '刘优' , 133.05 , 122 , 188 , 'static/img/default.jpg');insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , '数据结构 java版' , '封大神' , 173.15 , 21 , 81 , 'static/img/default.jpg');insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , 'UNIX高级环境编程' , '乐天' , 99.15 , 210 , 810 , 'static/img/default.jpg');insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , 'javaScript高级编程' , '国哥' , 69.15 , 210 , 810 , 'static/img/default.jpg');insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , '大话设计模式' , '国哥' , 89.15 , 20 , 10 , 'static/img/default.jpg');insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , '人月神话' , '刚哥' , 88.15 , 20 , 80 , 'static/img/default.jpg');## 查看表内容
select id,name,author,price,sales,stock,img_path from t_book;

1.2 编写图书模块的 JavaBean

public class Book {private Integer id;private String name;private String author;private BigDecimal price;private Integer sales;private Integer stock;private String img_path = "static/img/default.jpg";public Book() {}public Book(Integer id, String name, String author, BigDecimal price, Integer sales, Integer stock, String img_path) {this.id = id;this.name = name;this.author = author;this.price = price;this.sales = sales;this.stock = stock;if (img_path != null && "".equals(img_path)) {this.img_path = img_path;}}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 getImg_path() {return img_path;}public void setImg_path(String img_path) {if (img_path != null && "".equals(img_path)) {this.img_path = img_path;}}@Overridepublic String toString() {return "Book{" +"id=" + id +", name='" + name + '\'' +", author='" + author + '\'' +", price=" + price +", sales=" + sales +", stock=" + stock +", img_path='" + img_path + '\'' +'}';}
}

1.3、编写图书模块的 Dao 和测试 Dao

BookDao接口:
public interface BookDao {int addBook();int deleteBook();int update();Book queryBookById();List<Book> queryBooks();
}

BookDaoImpl实现类:

public class BookDaoImpl extends BaseDao implements BookDao {@Overridepublic int addBook(Book book) {String sql = "insert into t_book(`name`,`author`,`price`,`sales`,`stock`,`img_path`) values(?,?,?,?,?,?)";return update(sql, book.getName(), book.getAuthor(), book.getPrice(),book.getSales(), book.getStock(), book.getImg_path());}@Overridepublic int deleteBookById(Integer id) {String sql = "delete from t_book where id = ?";return update(sql,id);}@Overridepublic int updateBook(Book book) {String sql = "update t_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.getImg_path(),book.getId());}@Overridepublic Book queryBookById(Integer id) {String sql = "select `id`,`name`,`author`,`price`,`sales`,`stock`,`img_path` from t_book where id = ?";return queryForOne(Book.class,sql,id);}@Overridepublic List<Book> queryBooks() {String sql = "select `id`,`name`,`author`,`price`,`sales`,`stock`,`img_path` from t_book";return queryForList(Book.class,sql);}
}

测试:

public class BookDaoTest {private BookDao bookDao = new BookDaoImpl();@Testpublic void addBook() {bookDao.addBook(new Book(null,"强哥好帅","chenyixin",new BigDecimal(123),999,999,null));System.out.println("添加成功");}@Testpublic void deleteBookById() {bookDao.deleteBookById(21);}@Testpublic void updateBook() {bookDao.updateBook(new Book(21, "大家都好帅", "佚名", new BigDecimal(123), 999, 999, null));}@Testpublic void queryBookById() {System.out.println(bookDao.queryBookById(21));}@Testpublic void queryBooks() {for (Book queryBook : bookDao.queryBooks()) {System.out.println(queryBook);}}
}

1.4、编写图书模块的 Service 和测试 Service

BookService 接口:

public interface BookService {public void addBook(Book book);public void delete(Integer id);public void update(Book book);public Book queryBookById(Integer id);public List<Book> queryBooks();
}

BookServiceImpl 实现类:

public class BookServiceImpl implements BookService {private BookDao bookDao = new BookDaoImpl();@Overridepublic void addBook(Book book) {bookDao.addBook(book);}@Overridepublic void delete(Integer id) {bookDao.deleteBookById(id);}@Overridepublic void update(Book book) {bookDao.updateBook(book);}@Overridepublic Book queryBookById(Integer id) {return bookDao.queryBookById(id);}@Overridepublic List<Book> queryBooks() {return bookDao.queryBooks();}
}

测试:


public class BookServiceTest {private BookService bookService = new BookServiceImpl();@Testpublic void addBook() {bookService.addBook(new Book(null,"强哥好帅","chenyixin",new BigDecimal(123),999,999,null));System.out.println("修改成功");}@Testpublic void delete() {bookService.delete(22);}@Testpublic void update() {bookService.update(new Book(22, "大家都好帅", "佚名", new BigDecimal(123), 999, 999, null));}@Testpublic void queryBookById() {System.out.println( bookService.queryBookById(22));}@Testpublic void queryBooks() {for (Book queryBook : bookService.queryBooks()) {System.out.println(queryBook);}}
}   

2. 编写图书模块的 Web 层

前后台的简单介绍

2.1 图书列表功能的实现

图解列表功能流程:

思路流程:

        1.在manager_menu.jsp页面中修改【图书管理】请求地址
        2. 在web包中创建BookServlet类,并添加list方法
        3.修改 pages/manager/book_manager.jsp 页面的数据遍历输出

1.在manager_menu.jsp页面中修改【图书管理】请求地址

action=list 表示调用list方法 (反射)

2. 创建BookServlet 程序中,并添加 list 方法:

@WebServlet(name = "BookServlet", value = "/manager/bookServlet")
public class BookServlet extends BaseServlet {private BookService bookService = new BookServiceImpl();protected void list(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//1 通过 BookService 查询全部图书System.out.println("进来了");List<Book> books = bookService.queryBooks();//2 把全部图书保存到 Request 域中request.setAttribute("books", books);//3、请求转发到/pages/manager/book_manager.jsp 页面request.getRequestDispatcher("/pages/manager/book_manager.jsp").forward(request,response);}}

注意:a标签是get请求,所以要在BaseSerlet中写doGet方法:

 protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {doPost(req, resp);}

3.修改 pages/manager/book_manager.jsp 页面的数据遍历输出

<%@ 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><%@include file="/pages/common/head.jsp" %>
</head>
<body><div id="header"><img class="logo_img" alt="" src="static/img/logo.gif"><span class="wel_word">图书管理系统</span><%@include file="/pages/common/manager_menu.jsp" %>
</div><div id="main"><table><tr><td>名称</td><td>价格</td><td>作者</td><td>销量</td><td>库存</td><td colspan="2">操作</td></tr><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="pages/manager/book_edit.jsp">修改</a></td><td><a href="#">删除</a></td></tr></c:forEach><tr><td></td><td></td><td></td><td></td><td></td><td></td><td><a href="book_edit.jsp">添加图书</a></td></tr></table>
</div><%@include file="/pages/common/footer.jsp" %>
</body>
</html>

2.2 添加图书功能的实现

问题说明:表单重复提交:

        当用户提交完请求,浏览器会记录下最后一次请求的全部信息。当用户按下功能键 F5,就会发起浏览器记录的最后一次请求。
        解决:使用重定向。

思路步骤:

        1.在book_edit.jsp 页面添加隐藏域并修改form标签的action属性

        2.在BookServlet中编写add方法

1.在book_edit.jsp 页面添加隐藏域并修改form标签的action属性

2.在BookServlet中编写add方法

protected void add(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 1.获取请求的参数 封装成为Book对象Book book = WebUtils.copyParamToBean(request.getParameterMap(), new Book());// 2.调用BookServlet.addBook()保存图书bookService.addBook(book);// 3.使用重定向跳转到图书列表界面response.sendRedirect(request.getContextPath() + "/manager/bookServlet?action=list");}

2.3 删除图书功能的实现

图解删除流程:

步骤:

        1. 修改删除的连接地址

        2. 编写BookServlet 程序中的 delete 方法

        3. 给删除添加确认提示操作

1. 修改删除的连接地址

 2. 编写BookServlet 程序中的 delete 方法
protected void delete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 1.获取请求参数的id,图书编号String id = request.getParameter("id");int i = WebUtils.parseInt(id, 0);// 2.调用bookServlet.delete方法,删除图书bookService.delete(i);// 3.重定向回图书列表管理页面response.sendRedirect(request.getContextPath() + "/manager/bookServlet?action=list");}
WebUtils 工具类添加转换 int 类型的工具方法
public static int parseInt(String strId, int defaultValue) {try {return Integer.parseInt(strId);} catch (NumberFormatException e) {e.printStackTrace();}return defaultValue;}
3. 给删除添加确认提示操作:

<script type="text/javascript">$(function () {// 给删除的 a 标签绑定单击事件,用于删除的确认提示操作$("a.deleteClass").click(function () {// 在事件的 function 函数中,有一个 this 对象。这个 this 对象,是当前正在响应事件的 dom 对象。/*** confirm 是确认提示框函数* 参数是它的提示内容* 它有两个按钮,一个确认,一个是取消。* 返回 true 表示点击了,确认,返回 false 表示点击取消。*/return confirm("你确定要删除【" + $(this).parent().parent().find("td:first").text() + "】?");// return false// 阻止元素的默认行为===不提交请求});});</script>

2.4 修改图书功能的实现

图解修改图书细节:(流程)
1. 更新【修改】的请求地址:
2. BookServlet 程序中添加 getBook 方法:
protected void getBook(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 1.获取图书编号String id = request.getParameter("id");int i = WebUtils.parseInt(id, 0);// 2.条用BookServlet.queryBookById(id),得到要修改的图书信息(book对象)Book book = bookService.queryBookById(i);// 3.把图书保存到Request域中request.setAttribute("book", book);// 4.把请求妆发到/pages/manager/book_edit.jsp页面request.getRequestDispatcher("/pages/manager/book_edit.jsp").forward(request,response);}

3. 在 book_edit.jsp 页面中显示修改的数据

<div id="main"><form action="manager/bookServlet"><input type="hidden" name="action" value="add"/><table><tr><td>名称</td><td>价格</td><td>作者</td><td>销量</td><td>库存</td><td colspan="2">操作</td></tr><tr><td><input name="name" type="text" value="${requestScope.book.name}"/></td><td><input name="price" type="text" value="${requestScope.book.price}"/></td><td><input name="author" type="text" value="${requestScope.book.author}"/></td><td><input name="sales" type="text" value="${requestScope.book.sales}"/></td><td><input name="stock" type="text" value="${requestScope.book.stock}"/></td><td><input type="submit" value="提交"/></td></tr></table></form></div>
4. 在 BookServlet 程序中添加 update 方法
protected void update(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 1.获取请求参数,并封装成Book对象Book book = WebUtils.copyParamToBean(request.getParameterMap(), new Book());// 2.调用BookServlet.update(book)方法,修改图书bookService.update(book);// 3.重定向返回图书列表管理页面response.sendRedirect(request.getContextPath()+"/manager/bookServlet?action=list");}

5. 解决 book_edit.jsp 页面,即要实现添加,又要实现修改操作。

3. 图书分页

3.1 分页模块的分析

3.2 分页的初步实现

步骤:

        1.修改manager_menu.jsp页面中 图书管理地址

        2.编写page类

        3.编写Bookservlet程序

        4. 编写BookService程序

        5. 编写BookDao程序

        6. 测试BookDao程序   

        7. 测试BookService程序

        8. 修改book_manager.jsp 页面

   

1.修改manager_menu.jsp页面中 图书管理地址
2.编写page类

/*** Page 是分页的模型对象* @param <T> 是具体的模块的 javaBean 类*/
public class Page<T> {public static final Integer PAGE_SIZE = 4;// 当前页码private Integer pageNo;// 总页码private Integer pageTotal;// 总记录数private Integer pateTotalCount;// 每页显示数量private Integer pateSize = PAGE_SIZE;//当前页数据private List<T> items;
3.编写Bookservlet程序
protected void page(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 1.获取请求参数 pageNo 和 pageSizeint pageNo = WebUtils.parseInt(request.getParameter("pageNo"), 1);int pageSize = WebUtils.parseInt(request.getParameter("pageSize"), Page.PAGE_SIZE);// 2.调用BookServlet.page(pageNo,pageSize),获得page对象Page<Book> page = bookService.page(pageNo, pageSize);// 3.将Page对象保存到Request域中request.setAttribute("page", page);// 4.请求转发到 /pages/manager/book_manager.jsprequest.getRequestDispatcher("/pages/manager/book_manager.jsp").forward(request, response);}
4. 编写BookService程序
BookService接口中的page方法:
Page<Book> page(int pageNo, int pageSize);

BookServiceImpl实现类中的page方法:

public Page<Book> page(int pageNo, int pageSize) {Page<Book> page = new Page<>();// 设置 当前页码page.setPageNo(pageNo);// 设置 当前每页显示数量page.setPateSize(pageSize);// 求总记录数Integer pageTotalCount = bookDao.queryForPageTotalCount();// 设置总记录数page.setPateTotalCount(pageTotalCount);// 求总页码Integer pageTotal = pageTotalCount / pageSize;if ((pageTotalCount % pageSize) > 0) {pageTotal++;}// 设置总页码page.setPageTotal(pageTotal);// 求当前页面数据// 求页面开始索引Integer begin = (pageNo - 1) * pageSize;List<Book> items = bookDao.queryForPageItems(begin, pageSize);// 设置当前页面数据page.setItems(items);return page;}

5. 编写BookDao程序

Bookdao接口:

Integer queryForPageTotalCount();List<Book> queryForPageItems(Integer begin, int pageSize);

BookdaoImpl实现类:

 @Overridepublic Integer queryForPageTotalCount() {String sql = "select count(*) from t_book";Number number = (Number) queryForSingleValue(sql);return number.intValue();}@Overridepublic List<Book> queryForPageItems(Integer begin, int pageSize) {String sql = "select `id`,`name`,`author`,`price`,`sales`,`stock`,`img_path` from t_book limit ?,?";return queryForList(Book.class ,sql,begin,pageSize);}

6. 测试BookDao程序

 @Testpublic void queryForPageTotalCount() {System.out.println(bookDao.queryForPageTotalCount());}@Testpublic void queryForPageItems() {for (Book item : bookDao.queryForPageItems(8,4)) {System.out.println(item);}}

7. 测试BookService程序

@Test
public void page() {System.out.println(bookService.page(1,4));}

8. 修改book_manager.jsp 页面

<%@ 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><%@include file="/pages/common/head.jsp" %>
</head>
<body><div id="header"><img class="logo_img" alt="" src="static/img/logo.gif"><span class="wel_word">图书管理系统</span><%@include file="/pages/common/manager_menu.jsp" %></div><div id="main"><table><tr><td>名称</td><td>价格</td><td>作者</td><td>销量</td><td>库存</td><td colspan="2">操作</td></tr><c:forEach items="${requestScope.page.items}" 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="manager/bookServlet?action=getBook&id=${book.id}">修改</a></td><td><a class="deleteClass" href="manager/bookServlet?action=delete&id=${book.id}">删除</a></td></tr></c:forEach><tr><td></td><td></td><td></td><td></td><td></td><td></td><td><a href="pages/manager/book_edit.jsp">添加图书</a></td></tr></table><div id="page_nav"><a href="#">首页</a><a href="#">上一页</a><a href="#">3</a>【${requestScope.page.pageNo}】<a href="#">5</a><a href="#">下一页</a><a href="#">末页</a>共${requestScope.page.pageTotal}页,${requestScope.page.pageTotalCount}条记录到第<input value="4" name="pn" id="pn_input"/>页<input type="button" value="确定"></div>
</div><%@include file="/pages/common/footer.jsp" %>
</body>
</html>

3.3 首页、上一页、下一页、末页实现

<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><a href="#">3</a>【${requestScope.page.pageNo}】<a href="#">5</a><%-- 如果已经 是最后一页,则不显示下一页,末页 --%><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="4" name="pn" id="pn_input"/>页<input type="button" value="确定"></div>

3.4 分页模块中跳转到指定页数功能实现

1.对按钮绑定事件
 // 给按钮绑定点击事件$("#searchPageBtn").click(function () {var pageNo = $("#pn_input").val();// javaScript 语言中提供了一个 location 地址栏对象// 它有一个属性叫 href.它可以获取浏览器地址栏中的地址// href 属性可读,可写location.href = "${requestScope.basePath}manager/bookServlet?action=page&pageNo=" + pageNo;});

2. 将<bash>标签中的bashPath路径存放到Request域中

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%String basePath = request.getScheme() + "://" +request.getServerName() + ":" +request.getServerPort() +request.getContextPath() + "/";request.setAttribute("basePath",basePath);
%>
<html>
<head><title>Title</title><base href="<%=basePath%>"><link type="text/css" rel="stylesheet" href="static/css/style.css"><script type="text/javascript" src="static/js/script/jquery-1.7.2.min.js"></script>
</head>

3. BookService 中 page 方法中添加数据边界的有效检查:

public Page<Book> page(int pageNo, int pageSize) {Page<Book> page = new Page<>();// 设置 当前每页显示数量page.setPageSize(pageSize);// 求总记录数Integer pageTotalCount = bookDao.queryForPageTotalCount();// 设置总记录数page.setPageTotalCount(pageTotalCount);// 求总页码Integer pageTotal = pageTotalCount / pageSize;if ((pageTotalCount % pageSize) > 0) {pageTotal++;}// 设置总页码page.setPageTotal(pageTotal);// 设置 当前页码/* 数据边界的有效检查 */if (pageNo < 1) {pageNo = 1;}if (pageNo > pageTotal) {pageNo = pageTotal;}page.setPageNo(pageNo);// 求当前页面数据// 求页面开始索引Integer begin = (pageNo - 1) * pageSize;List<Book> items = bookDao.queryForPageItems(begin, pageSize);// 设置当前页面数据page.setItems(items);return page;}

3.5 分页模块中的页码显示

需求:显示 5 个连续的页码,而且当前页码在中间。除了当前页码之外,每个页码都可以点击跳到指定页。
分析
情况 1:如果总页码小于等于 5 的情况,页码的范围是:1-总页码
        1 页         1
        2 页         1,2
        3 页         1,2,3
        4 页         1,2,3,4
        5 页         1,2,3,4,5
情况 2:总页码大于 5 的情况。假设一共 10
小情况 1:当前页码为前面 3 个:123 的情况,页码范围是:1-5.
        【1】2,3,4,5
            1【2】3,4,5
            1,2【3】4,5
小情况 2:当前页码为最后 3 个,8910,页码范围是:总页码减 4 - 总页码
        6,7【8】9,10
        6,7,8【9】10
        6,7,8,9【10】
小情况 34567,页码范围是:当前页码减 2 - 当前页码加 2
        2,3,【4】,5,6
        3,4,【5】,6,7
        4,5,【6】,7,8
        5,6,【7】,8,9

代码:
<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:set var="begin" value="1"/><c:set var="end" value="${requestScope.page.pageTotal}"/></c:when><%--情况 2:总页码大于 5 的情况--%><c:when test="${requestScope.page.pageTotal > 5}"><c:choose><%--小情况 1:当前页码为前面 3 个:1,2,3 的情况,页码范围是:1-5.--%><c:when test="${requestScope.page.pageNo <= 3}"><c:set var="begin" value="1"/><c:set var="end" value="5"/></c:when><%--小情况 2:当前页码为最后 3 个,8,9,10,页码范围是:总页码减 4 - 总页码--%><c:when test="${requestScope.page.pageNo > requestScope.page.pageTotal-3}"><c:set var="begin" value="${requestScope.page.pageTotal - 4}"/><c:set var="end" value="${requestScope.page.pageTotal}"/></c:when><%--小情况 3:4,5,6,7,页码范围是:当前页码减 2 - 当前页码加 2--%><c:otherwise><c:set var="begin" value="${requestScope.page.pageNo - 2}"/><c:set var="end" value="${requestScope.page.pageNo + 2}"/></c:otherwise></c:choose></c:when></c:choose><c:forEach begin="${begin}" end="${end}" 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: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="${requestScope.page.pageNo}" name="pn" id="pn_input"/>页<input id="searchPageBtn" type="button" value="确定"></div>

3.6 修改分页后,增加,删除,修改图书信息的回显页面

以修改图书为示例:
1、在修改的请求地址上追加当前页码参数:
2、在 book_edit.jsp 页面中使用隐藏域记录下 pageNo 参数

 3、在服务器重定向的时候,获取当前页码追加上进行跳转

protected void update(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 1.获取请求参数,并封装成Book对象Book book = WebUtils.copyParamToBean(request.getParameterMap(), new Book());// 2.调用BookServlet.update(book)方法,修改图书bookService.update(book);// 3.重定向返回图书列表管理页面response.sendRedirect(request.getContextPath() + "/manager/bookServlet?action=page&pageNo=" + request.getParameter("pageNo"));}

4.首页 index.jsp 的跳转

4.1 思路图解

步骤: 

        1.复制以分index到pages/client中

        2.编写ClientBookServlet程序,并继承BaseServlet父类

        3.将原来的index.jsp内容全删,改成去求转发

        4.修改复制后的index

4.2 代码实现

1.复制以分index到pages/client中

 2.编写ClientBookServlet程序,并继承BaseServlet父类

protected void page(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 1.获取请求参数 pageNo 和 pageSizeint pageNo = WebUtils.parseInt(request.getParameter("pageNo"), 1);int pageSize = WebUtils.parseInt(request.getParameter("pageSize"), Page.PAGE_SIZE);// 2.调用BookServlet.page(pageNo,pageSize),获得page对象Page<Book> page = bookService.page(pageNo, pageSize);// 3.将Page对象保存到Request域中request.setAttribute("page", page);// 4.请求转发到 /pages/manager/book_manager.jsprequest.getRequestDispatcher("/pages/manager/book_manager.jsp").forward(request, response);}

3. 将原来的index.jsp内容全删,改成去求转发

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<jsp:forward page="/client/bookServlet?action=page"></jsp:forward>

4.修改复制后的index

<%@ 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><%@include file="/pages/common/head.jsp"%>
</head>
<body><div id="header"><img class="logo_img" alt="" src="static/img/logo.gif"><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.img_path}"/></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/bookServlet?action=page&pageNo=1">首页</a><a href="client/bookServlet?action=page&pageNo=${requestScope.page.pageNo - 1}">上一页</a></c:if><%--页码输出的开始--%><c:choose><%--情况 1:如果总页码小于等于 5 的情况,页码的范围是:1-总页码--%><c:when test="${requestScope.page.pageTotal <= 5}"><c:set var="begin" value="1"/><c:set var="end" value="${requestScope.page.pageTotal}"/></c:when><%--情况 2:总页码大于 5 的情况--%><c:when test="${requestScope.page.pageTotal > 5}"><c:choose><%--小情况 1:当前页码为前面 3 个:1,2,3 的情况,页码范围是:1-5.--%><c:when test="${requestScope.page.pageNo <= 3}"><c:set var="begin" value="1"/><c:set var="end" value="5"/></c:when><%--小情况 2:当前页码为最后 3 个,8,9,10,页码范围是:总页码减 4 - 总页码--%><c:when test="${requestScope.page.pageNo > requestScope.page.pageTotal-3}"><c:set var="begin" value="${requestScope.page.pageTotal - 4}"/><c:set var="end" value="${requestScope.page.pageTotal}"/></c:when><%--小情况 3:4,5,6,7,页码范围是:当前页码减 2 - 当前页码加 2--%><c:otherwise><c:set var="begin" value="${requestScope.page.pageNo - 2}"/><c:set var="end" value="${requestScope.page.pageNo + 2}"/></c:otherwise></c:choose></c:when></c:choose><c:forEach begin="${begin}" end="${end}" var="i"><c:if test="${requestScope.page.pageNo == i}">【${i}】</c:if><c:if test="${requestScope.page.pageNo != i}"><a href="client/bookServlet?action=page&pageNo=${i}">${i}</a></c:if></c:forEach><%--页码输出的结束--%><%-- 如果已经 是最后一页,则不显示下一页,末页 --%><c:if test="${requestScope.page.pageNo < requestScope.page.pageTotal}"><a href="client/bookServlet?action=page&pageNo=${requestScope.page.pageNo + 1}">下一页</a><a href="client/bookServlet?action=page&pageNo=${requestScope.page.pageTotal}">末页</a></c:if>共${requestScope.page.pageTotal}页,${requestScope.page.pageTotalCount}条记录到第<input value="${requestScope.page.pageNo}" name="pn" id="pn_input"/>页<input id="searchPageBtn" type="button" value="确定"></div>
<script type="text/javascript">$(function (){// 给按钮绑定点击事件$("#searchPageBtn").click(function () {var pageNo = $("#pn_input").val();// javaScript 语言中提供了一个 location 地址栏对象// 它有一个属性叫 href.它可以获取浏览器地址栏中的地址// href 属性可读,可写location.href = "${requestScope.basePath}manager/bookServlet?action=page&pageNo=" + pageNo;});});</script><%--  分页条的结束  --%></div>
<%@include file="/pages/common/footer.jsp"%>
</body>
</html>

4.3 抽取分页条

步骤:

        1. page 对象中添加 url 属性

        2.在 Servlet 程序的 page 分页方法中设置 url 的分页请求地址

        3.修改分页条中请求地址为 url 变量输出,并抽取一个单独的 jsp 页面

1. page 对象中添加 url 属性

2.在 Servlet 程序的 page 分页方法中设置 url 的分页请求地址

在BookServlet类中:

在ClientBookServlet类中

3.修改分页条中请求地址为 url 变量输出,并抽取一个单独的 jsp 页面
<%--Created by IntelliJ IDEA.User: pcDate: 2022/7/28Time: 21:37To change this template use File | Settings | File Templates.
--%>
<%@ 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:set var="begin" value="1"/><c:set var="end" value="${requestScope.page.pageTotal}"/></c:when><%--情况 2:总页码大于 5 的情况--%><c:when test="${requestScope.page.pageTotal > 5}"><c:choose><%--小情况 1:当前页码为前面 3 个:1,2,3 的情况,页码范围是:1-5.--%><c:when test="${requestScope.page.pageNo <= 3}"><c:set var="begin" value="1"/><c:set var="end" value="5"/></c:when><%--小情况 2:当前页码为最后 3 个,8,9,10,页码范围是:总页码减 4 - 总页码--%><c:when test="${requestScope.page.pageNo > requestScope.page.pageTotal-3}"><c:set var="begin" value="${requestScope.page.pageTotal - 4}"/><c:set var="end" value="${requestScope.page.pageTotal}"/></c:when><%--小情况 3:4,5,6,7,页码范围是:当前页码减 2 - 当前页码加 2--%><c:otherwise><c:set var="begin" value="${requestScope.page.pageNo - 2}"/><c:set var="end" value="${requestScope.page.pageNo + 2}"/></c:otherwise></c:choose></c:when></c:choose><c:forEach begin="${begin}" end="${end}" 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: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="${requestScope.page.pageNo}" name="pn" id="pn_input"/>页<input id="searchPageBtn" type="button" value="确定">
</div>
<script type="text/javascript">$(function (){// 给按钮绑定点击事件$("#searchPageBtn").click(function () {var pageNo = $("#pn_input").val();// javaScript 语言中提供了一个 location 地址栏对象// 它有一个属性叫 href.它可以获取浏览器地址栏中的地址// href 属性可读,可写location.href = "${requestScope.basePath}${requestScope.page.url}&pageNo=" + pageNo;});});
</script>
<%--  分页条的结束  --%>

5. 首页价格搜索

思路流程图

1.修改表单的发送地址,接添加隐藏域 ,并优化部分代码

 2.在ClientBookServlet程序中编写pageByPrice方法

protected void pageByPrice(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("经过了前台");// 1.获取请求参数 pageNo 和 pageSizeint pageNo = WebUtils.parseInt(request.getParameter("pageNo"), 1);int pageSize = WebUtils.parseInt(request.getParameter("pageSize"), Page.PAGE_SIZE);int min = WebUtils.parseInt(request.getParameter("min"), 0);int max = WebUtils.parseInt(request.getParameter("max"), Integer.MAX_VALUE);// 2.调用BookServlet.page(pageNo,pageSize),获得page对象Page<Book> page = bookService.pageByPrice(pageNo, pageSize,min,max);StringBuilder sb = new StringBuilder("client/bookServlet?action=pageByPrice");if (request.getParameter("min") != null) {sb.append("&min=").append(request.getParameter("min"));}if (request.getParameter("min") != null) {sb.append("&max=").append(request.getParameter("max"));}page.setUrl(sb.toString());// 3.将Page对象保存到Request域中request.setAttribute("page", page);// 4.请求转发到 /pages/client/index.jsrequest.getRequestDispatcher("/pages/client/index.jsp").forward(request, response);}

3.编写BookServlet程序

public Page<Book> pageByPrice(int pageNo, int pageSize, int min, int max) {Page<Book> page = new Page<>();// 设置 当前每页显示数量page.setPageSize(pageSize);// 求总记录数Integer pageTotalCount = bookDao.queryForPageTotalCountByPrice(min,max);// 设置总记录数page.setPageTotalCount(pageTotalCount);// 求总页码Integer pageTotal = pageTotalCount / pageSize;if ((pageTotalCount % pageSize) > 0) {pageTotal++;}// 设置总页码page.setPageTotal(pageTotal);// 设置 当前页码/* 数据边界的有效检查 */if (pageNo < 1) {pageNo = 1;}if (pageNo > pageTotal) {pageNo = pageTotal;}page.setPageNo(pageNo);// 求当前页面数据// 求页面开始索引Integer begin = (pageNo - 1) * pageSize;List<Book> items = bookDao.queryForPageItemsByPrice(begin, pageSize,min,max);// 设置当前页面数据page.setItems(items);return page;}

4.编写Bookdao程序

@Overridepublic Integer queryForPageTotalCountByPrice(int min, int max) {String sql = "select count(*) from t_book where price between ? and ?";Number number = (Number) queryForSingleValue(sql, min, max);return number.intValue();}@Overridepublic List<Book> queryForPageItemsByPrice(Integer begin, int pageSize, int min, int max) {String sql = "select `id`,`name`,`author`,`price`,`sales`,`stock`,`img_path` " +"from t_book where price between ? and ? order by price asc limit ?,?";return queryForList(Book.class, sql, min, max, begin, pageSize);}

5.测试Bookdao程序

@Testpublic void queryForPageTotalCountByPrice() {System.out.println(bookDao.queryForPageTotalCountByPrice(10,50));}@Testpublic void queryForPageItemsByPrice() {for (Book item : bookDao.queryForPageItemsByPrice(0,4,10,50)) {System.out.println(item);}}

6.测试BookService程序

@Testpublic void pageByPrice() {System.out.println(bookService.pageByPrice(1,4,10,50));}

项目第五阶段 -- 完善登录/注册功能

1. 登陆---显示用户名

需求:在用户登录成功后显示用户名欢迎菜单

步骤:
1.UserServlet 程序中保存用户登录的信息
        2.修改 login_succuess_menu.jsp 
        3.还要修改首页 index.jsp 页面的菜单
        (没登陆时显示登录/注册,若已登录显示用户欢迎菜单)
1.UserServlet 程序中保存用户登录的信息
/*** 处理登录功能** @param request* @param response* @throws ServletException* @throws IOException*/protected void login(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 获取请求的参数User user = WebUtils.copyParamToBean(request.getParameterMap(), new User());// 调用 userService.login()登录处理业务User loginUser = userService.login(user);if (loginUser == null) {// 不正确// 把错误信息,和回显的表单项信息,保存到Request域中request.setAttribute("msg", "用户名或密码错误!");request.setAttribute("username", user.getUsername());// 跳回登录页面request.getRequestDispatcher("/pages/user/login.jsp").forward(request, response);} else {// 正确// 用户信息保存到Session对象中request.getSession().setAttribute("loginUser", loginUser);// 跳转到登录成功页面request.getRequestDispatcher("/pages/user/login_success.jsp").forward(request, response);}}

2.修改 login_succuess_menu.jsp 

3.还要修改首页 index.jsp 页面的菜单
<div id="header"><img class="logo_img" alt="" src="static/img/logo.gif"><span class="wel_word">网上书城</span><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="index.jsp">注销</a></c:if>&nbsp;&nbsp;<a href="pages/cart/cart.jsp">购物车</a><a href="pages/manager/manager.jsp">后台管理</a></div>
</div>

2. 登出---注销用户(即退出登录)

步骤:
        1.UserServlet 程序中添加 logout 方法
                1. 销毁 Session 中用户登录的信息(或者销毁 Session)
                2. 重定向到首页(或登录页面)。
        2. 修改【注销】的菜单地址
1.UserServlet 程序中添加 logout 方法
/*** 注销登录功能** @param request* @param response* @throws ServletException* @throws IOException*/protected void logout(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 1、销毁 Session 中用户登录的信息(或者销毁 Session)request.getSession().invalidate();// 2、重定向到首页response.sendRedirect(request.getContextPath());}

2. 修改【注销】的菜单地址

3. 表单重复提交之-----验证码

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

4. 谷歌 kaptcha 图片验证码的使用

谷歌验证码 kaptcha 使用步骤如下:
        1、导入谷歌验证码的 jar 包
                kaptcha-2.3.2.jar
        2、在 web.xml 中去配置用于生成验证码的 Servlet 程序
        3、在表单中使用 img 标签去显示验证码图片并使用它
        4、在服务器获取谷歌生成的验证码和客户端发送过来的验证码比较使用。

1、导入谷歌验证码的 jar 包

 2、在 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>/kaptcha.jpg</url-pattern></servlet-mapping>

 3、在表单中使用 img 标签去显示验证码图片并使用它

4、在服务器获取谷歌生成的验证码和客户端发送过来的验证码比较使用。

/*** 处理注册功能** @param request* @param response* @throws ServletException* @throws IOException*/protected void regist(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {UserService userService = new UserServiceImpl();// 获取请求的参数User user = WebUtils.copyParamToBean(request.getParameterMap(), new User());String code = request.getParameter("code");// 获取 Session 中的验证码HttpSession session = request.getSession();// KAPTCHA_SESSION_KEY 可以当作是验证码值的keyString key = (String) session.getAttribute(KAPTCHA_SESSION_KEY);// 删除 Session 中的验证码// session.removeAttribute(KAPTCHA_SESSION_KEY);session.invalidate();// 检查 验证码是否正确if (code.equalsIgnoreCase(key)) {// 正确 检查 用户名是否可用if (userService.existsUsername(user.getUsername())) {// 不可用request.setAttribute("msg", "用户名已存在!");request.setAttribute("email", user.getEmail());// 跳回注册页面request.getRequestDispatcher("/pages/user/regist.jsp").forward(request, response);} else {// 可用 跳转到注册成功页面userService.regist(new User(user.getId(), user.getUsername(), user.getPassword(), user.getEmail()));request.getRequestDispatcher("/pages/user/regist_success.jsp").forward(request, response);}} else {// 不正确request.setAttribute("msg", "验证码错误!");request.setAttribute("username", user.getUsername());request.setAttribute("email", user.getEmail());// 跳回注册页面request.getRequestDispatcher("/pages/user/regist.jsp").forward(request, response);}}

5.切换验证码

给验证码绑定单机事件

<script type="text/javascript" >$(function (){// 给验证码绑定单击事件$("#code_img").click(function () {// 在事件响应的 function 函数中有一个 this 对象。//  这个 this 对象,是当前正在响应事件的 dom 对象//  src 属性表示验证码 img 标签的 图片路径。它可读,可写this.src="kaptcha.jpg?id=" + new Date();});});
</script>

项目第六阶段:购物车

1. 购物车模块分析

2. 购物车模型编写

2.1 编写CartItem类(生成方法略)

/***  购物车的商品项*/
public class CartItem {private Integer id;private String name;private Integer count;private BigDecimal price;private BigDecimal totalPrice;
}

2.2 编写Cart类

/*** 购物车对象*/
public class Cart {// private Integer totalCount;// private BigDecimal totalPrice;/*** key 是商品编号,* value,是商品信息*/private Map<Integer, CartItem> items = new LinkedHashMap<>();/*** 添加商品项** @param cartItem*/public void addItem(CartItem cartItem) {// 先查看购物车中是否已经添加过此商品,// 如果已添加,则数量累加,总金额更新,如果没有添加过,直接放到 集合中即可CartItem item = items.get(cartItem.getId());if (item == null) {// 之前没添加过此商品items.put(cartItem.getId(), cartItem);} else {// 之前已添加过此商品item.setCount(item.getCount() + 1); // 数量item.setTotalPrice(item.getPrice().multiply(new BigDecimal(item.getCount())));}}/*** 删除商品项** @param id*/public void deleteItem(Integer id) {items.remove(id);}/*** 清空购物车*/public void clear() {items.clear();}/*** 修改商品数量** @param id* @param count*/public void updateCount(Integer id, Integer count) {// 先查看购物车中是否有此商品。如果有,修改商品数量,更新总金额CartItem item = items.get(id);if (item != null) {// 修改商品数量item.setCount(count);// 修改商品总金额item.setTotalPrice(item.getPrice().multiply(new BigDecimal(count)));}}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;}@Overridepublic String toString() {return "Cart{" +"totalCount=" + getTotalCount() +", totalPrice=" + getTotalPrice() +", items=" + items +'}';}
}

2.3 测试Cart类


public class CartTest {Cart cart = new Cart();@Testpublic void addItem() {cart.addItem(new CartItem(1, "java从入门到放弃", 1, new BigDecimal(1000), new BigDecimal(2000)));cart.addItem(new CartItem(1, "java从入门到放弃", 1, new BigDecimal(1000), new BigDecimal(2000)));cart.addItem(new CartItem(2, "java从入门到入土", 1, new BigDecimal(100), new BigDecimal(100)));System.out.println(cart);}@Testpublic void deleteItem() {cart.addItem(new CartItem(1, "java从入门到放弃", 1, new BigDecimal(1000), new BigDecimal(2000)));cart.addItem(new CartItem(1, "java从入门到放弃", 1, new BigDecimal(1000), new BigDecimal(2000)));cart.addItem(new CartItem(2, "java从入门到入土", 1, new BigDecimal(100), new BigDecimal(100)));cart.deleteItem(1);System.out.println(cart);}@Testpublic void clear() {cart.addItem(new CartItem(1, "java从入门到放弃", 1, new BigDecimal(1000), new BigDecimal(2000)));cart.addItem(new CartItem(1, "java从入门到放弃", 1, new BigDecimal(1000), new BigDecimal(2000)));cart.addItem(new CartItem(2, "java从入门到入土", 1, new BigDecimal(100), new BigDecimal(100)));cart.deleteItem(1);cart.clear();System.out.println(cart);}@Testpublic void updateCount() {cart.addItem(new CartItem(1, "java从入门到放弃", 1, new BigDecimal(1000), new BigDecimal(2000)));cart.addItem(new CartItem(1, "java从入门到放弃", 1, new BigDecimal(1000), new BigDecimal(2000)));cart.addItem(new CartItem(2, "java从入门到入土", 1, new BigDecimal(100), new BigDecimal(100)));cart.updateCount(2,20);System.out.println(cart);}
}

3. 加入购物车功能的实现

步骤:
    1.在index.jsp 页面 给加入 购物车按钮添加绑定事件,使点击时可以跳到CartServlet程序
        2.编写 CartServlet程序
                1. 获取请求的参数 商品编号
                2. 调用 bookService.queryBookById(id):Book 得到图书的信息
                3. 把图书信息,转换成为 CartItem 商品项
                4. 调用 Cart.addItem(CartItem);添加商品项
                5. 重定向回原来商品所在的地址页面

1.在index.jsp 页面 给加入 购物车按钮添加绑定事件,使点击时可以跳到CartServlet程序
<script type="text/javascript">$(function () {$(".addToCart").click(function () {/*** 在事件响应的 function 函数 中,有一个 this 对象,这个 this 对象,是当前正在响应事件的 dom 对象* attr()    可以设置和获取属性的值* location.href: 返回当前页面的完整的URL地址;*/var bookId = $(this).attr("bookId");location.href = "cartServlet?action=addItem&id=" + bookId;});});</script>

2.编写 CartServlet程序

/*** 加入购物车* @param request* @param response* @throws ServletException* @throws IOException*/protected void addItem(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("进来了");// 1. 获取请求的参数 商品编号int id = WebUtils.parseInt(request.getParameter("id"), 0);// 2. 调用 bookService.queryBookById(id):Book 得到图书的信息Book book = bookService.queryBookById(id);// 3. 把图书信息,转换成为 CartItem 商品项CartItem cartItem = new CartItem(book.getId(), book.getName(), 1, book.getPrice(), book.getPrice());// 4. 调用 Cart.addItem(CartItem);添加商品项Cart cart = (Cart) request.getSession().getAttribute("cart");if (cart == null) {cart = new Cart();request.getSession().setAttribute("cart",cart);}cart.addItem(cartItem);// 5. 重定向回原来商品所在的地址页面// Referer 可以获取当前页面地址System.out.println(cart);System.out.println("当前页面地址:" + request.getHeader("Referer"));response.sendRedirect(request.getHeader("Referer"));}

4. 购物车的展示

<%@ 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><%@include file="/pages/common/head.jsp" %>
</head>
<body><div id="header"><img class="logo_img" alt="" src="static/img/logo.gif"><span class="wel_word">购物车</span><%@include file="/pages/common/success_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="item"><tr><td>${item.value.name}</td><td>${item.value.count}</td><td>${item.value.price}</td><td>${item.value.totalPrice}</td><td><a href="#">删除</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 href="#">清空购物车</a></span><span class="cart_span"><a href="pages/cart/checkout.jsp">去结账</a></span></div></c:if></div><%@include file="/pages/common/footer.jsp" %>
</body>
</html>

5. 删除购物车商品项

步骤:

        1 .给删除添加请求地址

        2. 编写cartServlet程序

        3. 给删除添加确认提示操作

1 .给删除添加请求地址

2. 编写cartServlet程序

 /*** 删除商品项* @param request* @param response* @throws ServletException* @throws IOException*/protected void deleteItem(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 获取商品项编号int id = WebUtils.parseInt(request.getParameter("id"), 0);// 获取购物车对象Cart cart = (Cart) request.getSession().getAttribute("cart");if (cart != null) {// 删除 了购物车商品项cart.deleteItem(id);// 重定向回原来商品所在的地址页面response.sendRedirect(request.getHeader("Referer"));}}

  3. 给删除添加确认提示操作

    <script type="text/javascript">$(function () {$("a.deleteItem").click(function () {return confirm("你确定要删除【" + $(this).parent().parent().find("td:first").text() + "】吗?");});});</script>

6. 清空购物车

步骤:

        1 .给清除功能添加请求地址,并添加id属性

        2. 编写cartServlet程序

        3. 给删除添加确认提示操作

1 .给清除功能添加请求地址,并添加id属性

2. 编写cartServlet程序
/*** 清空购物车* @param request* @param response* @throws ServletException* @throws IOException*/protected void clear(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//获取购物车对象Cart cart = (Cart) request.getSession().getAttribute("cart");if (cart != null) {//清空购物车cart.clear();// 重定向回原来商品所在的地址页面response.sendRedirect(request.getHeader("referer"));}}

3. 给删除添加确认提示操作

// 给清空购物车绑定事件$(function (){$("#clearCart").click(function (){return confirm("你确定要清空购物车吗?");});});

7. 修改购物车商品数量

思路:

步骤:

        1 .在 数量列 添加文本框,并绑定事件

        2. 编写cartServlet程序

1 .在 数量列 添加文本框,并绑定事件

// 给修改数量绑定事件$(".updateCount").change(function () {// 获取商品idvar id = $(this).attr("bookId");// 获取商品名称let name = $(this).parent().parent().find(":first").text();// 获取商品数量let count = this.value;if (confirm("你确定要将【" + name + "】的数量修改成:" + count + "吗")) {//发起请求。给服务器保存修改location.href = "cartServlet?action=updateCount&id=" + id + "&count=" + count;} else {// defaultValue 属性是表单项 Dom 对象的属性。它表示默认的 value 属性值。this.value = this.defaultValue;}});

2. 编写cartServlet程序

/*** 修改商品数量** @param request* @param response* @throws ServletException* @throws IOException*/protected void updateCount(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 获取请求的参数 商品编号 、商品数量int id = WebUtils.parseInt(request.getParameter("id"), 0);int count = WebUtils.parseInt(request.getParameter("count"), 1);// 获取购物车对象Cart cart = (Cart) request.getSession().getAttribute("cart");if (cart != null) {// 修改商品数量cart.updateCount(id, count);// 重定向返回商品原来的地址response.sendRedirect(request.getHeader("Referer"));}}

8. 首页,购物车数据回显

步骤:

        1.在添加商品到购物车的时候,保存最后一个添加的商品名称

        2.在 pages/client/index.jsp 页面中输出购物车信息

 1.在添加商品到购物车的时候,保存最后一个添加的商品名称

2.在 pages/client/index.jsp 页面中输出购物车信息

    <c:if test="${empty sessionScope.cart.items}"><div style="text-align: center"><span> </span><div><span style="color: red">您当前的购物车里没有商品哦</span></div></div></c:if><c:if test="${not empty sessionScope.cart.items}"><div style="text-align: center"><span>您的购物车中有${sessionScope.cart.totalCount}件商品</span><div>您刚刚将<span style="color: red">${sessionScope.lastName}</span>加入到了购物车中</div></div></c:if>

书城项目第七阶段 -- 订单

1. 订单模块的分析

2. 创建订单模块的数据库表

USE book;
CREATE TABLE t_order(order_id VARCHAR(50) PRIMARY KEY,create_time DATETIME ,price DECIMAL(11,2),`status` INT,user_id INT,FOREIGN KEY (user_id) REFERENCES t_book(id)
);CREATE TABLE t_order_item(id INT PRIMARY KEY AUTO_INCREMENT,name VARCHAR(100),count INT,price DECIMAL(11,2),total_price DECIMAL(11,2),order_id VARCHAR(50),FOREIGN KEY (order_id) REFERENCES t_order(order_id)
);

3. 创建订单模块的数据模型

public class Order {private String orderId;private Date createTime;private BigDecimal price;// 0 未发货,1 已发货,2 表示已签收private Integer status = 0;private Integer userId;
}
public class OrderItem {private Integer id;private String name;private Integer count;private BigDecimal price;private BigDecimal totalPrice;private String orderId;
}

4. 编写订单模块的 Dao 程序和测试

4.1 OrderDao 接口

public interface OrderDao {/*** 保存订单* @param order* @return*/public int saveOrder(Order order);
}

4.2 OrderDao 实现

public class OrderDaoImpl extends BaseDao implements OrderDao {@Overridepublic int saveOrder(Order order) {String sql = "insert into t_order(order_id,create_time,price,`status`,user_id) values(?,?,?,?,?)";return update(sql, order.getOrderId(), order.getCreateTime(), order.getPrice(), order.getStatus(), order.getUserId());}}

4.3 OrderItemDao 接口

public interface OrderItemDao {/*** 保存订单项* @param orderItem* @return*/public int saveOrderItem(OrderItem orderItem);}

4.4 OrderItemDao 实现

public class OrderItemDaoImpl extends BaseDao implements OrderItemDao {@Overridepublic int saveOrderItem(OrderItem orderItem) {String sql = "insert into t_order_item(name,count,price,total_price,order_id) values (?,?,?,?,?)";return update(sql, orderItem.getName(), orderItem.getCount(), orderItem.getPrice(), orderItem.getTotalPrice(), orderItem.getOrderId());}}

4.5 测试

public class OrderDaoTest {private OrderDao orderDao = new OrderDaoImpl();@Testpublic void saveOrder() {orderDao.saveOrder(new Order("1234567891",new Date(),new BigDecimal(100),0, 1));orderDao.saveOrder(new Order("12345678912",new Date(),new BigDecimal(100),0, 1));orderDao.saveOrder(new Order("1234567893",new Date(),new BigDecimal(100),0, 1));}
}
public class OrderItemDaoTest {private OrderItemDao orderItemDao = new OrderItemDaoImpl();@Testpublic void saveOrderItem() {orderItemDao.saveOrderItem(new OrderItem(null,"java 从入门到精通", 1,new BigDecimal(100),new BigDecimal(100),"1234567891"));orderItemDao.saveOrderItem(new OrderItem(null,"java 从入门到精通11", 1,new BigDecimal(1000),new BigDecimal(100),"12345678912"));orderItemDao.saveOrderItem(new OrderItem(null,"java 从入门到精通22", 1,new BigDecimal(10000),new BigDecimal(100),"1234567893"));}
}

5. 编写订单模块的 Service 和测试

5.1 OrderService接口

public interface OrderService {public String createOrder(Cart cart, Integer userId);
}

5.2 OrderServiceImpl 编写

public class OrderServiceImpl extends BaseDao implements OrderService {private OrderDao orderDao = new OrderDaoImpl();private OrderItemDao orderItemDao = new OrderItemDaoImpl();@Overridepublic 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> item : cart.getItems().entrySet()) {// 获取每一个购物车中的商品项CartItem cartItem = item.getValue();// 转换为每一个订单项OrderItem orderItem = new OrderItem(null, cartItem.getName(),cartItem.getCount(), cartItem.getPrice(), cartItem.getTotalPrice(), orderId);// 保存订单项到数据库orderItemDao.saveOrderItem(orderItem);}// 清空购物车cart.clear();// 返回订单号return orderId;}
}

5.3 测试

public class OrderServiceTest {private OrderService orderService = new OrderServiceImpl();@Testpublic void createOrder() {Cart cart = new Cart();cart.addItem(new CartItem(1, "java从入门到精通", 1, new BigDecimal(1000), new BigDecimal(1000)));cart.addItem(new CartItem(1, "java从入门到精通", 1, new BigDecimal(1000), new BigDecimal(1000)));cart.addItem(new CartItem(2, "数据结构与算法", 1, new BigDecimal(100), new BigDecimal(100)));OrderService orderService = new OrderServiceImpl();System.out.println("订单号是:" + orderService.createOrder(cart, 1));}
}

6. 编写订单模块的 web 层和页面联调

6.1 生成订单功能

步骤:
        1. 编写OrderServlet 程序
        2. 修改 pages/cart/cart.jsp 页面,结账的请求地址
        3. 修改 pages/cart/checkout.jsp 页面,输出订单号
1. 编写OrderServlet 程序
@WebServlet(name = "OrderServlet ", value = "/orderServlet ")
public class OrderServlet extends BaseServlet {private OrderService orderService = new OrderServiceImpl();protected void createOrder(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 先获取 Cart 购物车对象Cart cart = (Cart) request.getSession().getAttribute("cart");// 获取 UseridUser loginUser = (User) request.getSession().getAttribute("user");// 判断用户是否存在,若不存在,则转发到登录界面if (loginUser == null) {request.getRequestDispatcher("/pages/user/login.jsp").forward(request, response);return;}// 调用 orderService.createOrder(Cart,Userid);生成订单 并返回订单号// 获取用户idInteger id = loginUser.getId();System.out.println(id);System.out.println(loginUser);String orderId = orderService.createOrder(cart, id);// 将订单号保存到Session域中request.getSession().setAttribute("orderId", orderId);// 将页面请求重定向到checkout.jsp界面response.sendRedirect(request.getContextPath() + "/pages/cart/checkout.jsp");}}

 2. 修改 pages/cart/cart.jsp 页面,结账的请求地址

3. 修改 pages/cart/checkout.jsp 页面,输出订单号

6.2 获取所有订单功能(管理员)

步骤:
        1. 编写OrderServlet 程序
        2. 修改 manager_menu.jsp 页面,订单管理的请求地址
        3. 修改 pages/manager/order_manager.jsp 页面,遍历订单

1. 编写OrderServlet 程序
/*** 查看所有订单** @param request* @param response* @throws ServletException* @throws IOException*/protected void showAllOrders(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 1 通过 orderService 查询全部订单List<Order> orders = orderService.showAllOrders();// 2 把全部订单保存到 Request 域中request.setAttribute("orders", orders);// 3、请求转发到/pages/order/order.jsp 页面request.getRequestDispatcher("/pages/manager/order_manager.jsp").forward(request, response);}

2. 修改 manager_menu.jsp 页面,订单管理的请求地址

3. 修改 pages/manager/order_manager.jsp 页面,遍历订单
<%@ 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><%@include file="/pages/common/head.jsp"%>
</head>
<body><div id="header"><img class="logo_img" alt="" src="static/img/logo.gif"><span class="wel_word">订单管理系统</span><%@include file="/pages/common/manager_menu.jsp"%>
</div>
<div id="main"><table><tr><td>日期</td><td>金额</td><td>详情</td><td>发货</td></tr><c:forEach items="${requestScope.orders}" var="order"><tr><td>${order.createTime}</td><td>${order.price}</td><td><a href="#">查看详情</a></td><c:if test="${order.status == 0}"><td><a href="orderServlet?action=sendOrder&orderId=${order.orderId}">点击发货</a></td></c:if><c:if test="${order.status == 1}"><td>等待收货</td></c:if><c:if test="${order.status == 2}"><td>已完成</td></c:if></tr></c:forEach></table>
</div><%@include file="/pages/common/footer.jsp"%>
</body>
</html>

6.3 管理员点击发货功能

步骤:
        1. 修改 pages/manager/order_manager.jsp 页面,点击发货的请求地址
        2. 编写OrderServlet 程序
1. 修改 pages/manager/order_manager.jsp 页面,点击发送的v

2. 编写OrderServlet 程序

/*** 管理员发货功能* @param request* @param response* @throws ServletException* @throws IOException*/protected void sendOrder(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 获取参数 orderIdString orderId = request.getParameter("orderId");orderService.sendOrder(orderId);response.sendRedirect(request.getHeader("Referer"));}

6.4 查看订单详情功能

步骤:
        1. 修改order_manager.jsp 和 order.jsp页,给查看详情添加请求地址
        2. 编写OrderServlet 程序 
        3. 修改order_detail页面,遍历数据

1.修改order_manager.jsp 和 order.jsp页,给查看详情添加请求地址

2. 编写OrderServlet 程序 

/*** 查看订单详情** @param request* @param response* @throws ServletException* @throws IOException*/protected void showOrderDetail(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 获取参数 orderIdString orderId = request.getParameter("orderId");// 获取订单的详情信息List<OrderItem> orderItems = orderService.showOrderDetail(orderId);// 将订单的详情信息保存到Request域中request.setAttribute("orderItems", orderItems);// 请求转发到订单详情页面中request.getRequestDispatcher("/pages/order/order_detail.jsp").forward(request, response);}

3. 修改order_detail页面,遍历数据

<%@ 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><%@include file="/pages/common/head.jsp" %>
</head>
<body><div id="header"><img class="logo_img" alt="" src="static/img/logo.gif"><span class="wel_word">订单详情</span><%@include file="/pages/common/success_menu.jsp" %>
</div><div id="main"><table><tr><td>商品名称</td><td>数量</td><td>单价</td><td>金额</td></tr><c:if test="${empty requestScope.orderItems}"><tr><td colspan="5"><a href="index.jsp">暂无订单</a></td></tr></c:if><c:if test="${not empty requestScope.orderItems}"><c:forEach items="${requestScope.orderItems}" var="item"><tr><td>${item.name}</td><td>${item.count}</td><td>${item.price}</td><td>${item.totalPrice}</td></tr></c:forEach></c:if></table></div><%@include file="/pages/common/footer.jsp" %>
</body>
</html>

6.5 查看我的订单功能

步骤:
        1. 修改success_menu.jsp和index.jsp页面,给 我的订单 添加请求地址
        2. 编写OrderServlet 程序 
        3. 修改order页面,遍历数据
1. 修改success_menu.jsp和index.jsp页面,给 我的订单 添加请求地址
2. 编写OrderServlet 程序 
/*** 查看我的订单** @param request* @param response* @throws ServletException* @throws IOException*/protected void showMyOrders(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 获取参数 orderIdint userId = WebUtils.parseInt(request.getParameter("userId"), 0);if (userId != 0) {// 获取订单的详情信息List<Order> myOrders = orderService.showMyOrders(userId);// 将订单信息保存到Request域中request.setAttribute("myOrders",myOrders);// 请求转发到我的订单页面中request.getRequestDispatcher("/pages/order/order.jsp").forward(request,response);}}

3. 修改order页面,遍历数据

<%@ 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><%@include file="/pages/common/head.jsp" %><style type="text/css">h1 {text-align: center;margin-top: 200px;}</style>
</head>
<body><div id="header"><img class="logo_img" alt="" src="static/img/logo.gif"><span class="wel_word">我的订单</span><%@include file="/pages/common/success_menu.jsp" %>
</div>
<div id="main"><table><tr><td>订单编号</td><td>日期</td><td>金额</td><td>状态</td><td>详情</td></tr><c:forEach items="${requestScope.myOrders}" var="order"><tr><td>${order.orderId}</td><td>${order.createTime}</td><td>${order.price}</td><c:if test="${order.status == 0}"><td>未发货</td></c:if><c:if test="${order.status == 1}"><td><a href="orderServlet?action=receiverOrder&orderId=${order.orderId}">确认收货</a> </td></c:if><c:if test="${order.status == 2}"><td>已完成</td></c:if><td><a href="orderServlet?action=showOrderDetail&orderId=${order.orderId}">查看详情</a></td></tr></c:forEach></table>
</div><%@include file="/pages/common/footer.jsp" %>
</body>
</html>

6.6 签收订单功能

步骤:
        1. 修改order.jsp页面,给 确认收货 添加a标签及请求地址
        2. 编写OrderServlet 程序 
1. 修改order.jsp页面,给 确认收货 添加a标签及请求地址

2. 编写OrderServlet 程序 
/*** 签收订单功能* @param request* @param response* @throws ServletException* @throws IOException*/protected void receiverOrder(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 获取参数 orderIdString orderId = request.getParameter("orderId");orderService.receiverOrder(orderId);response.sendRedirect(request.getHeader("Referer"));}

书城项目第八阶段

1.使用 Filter 过滤器拦截/pages/manager/所有内容,实现权限检查


@WebFilter(filterName = "ManagerFilter")
public class ManagerFilter implements Filter {public void init(FilterConfig config) throws ServletException {}public void destroy() {}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {HttpServletRequest req = (HttpServletRequest) request;Object user = req.getSession().getAttribute("user");if (user == null) {request.getRequestDispatcher("/pages/user/login.jsp").forward(request, response);return;}else{chain.doFilter(request, response);}}
}

xml代码:

    <filter><filter-name>ManagerFilter</filter-name><filter-class>com.chenyixin.book.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>

2. 使用 Filter 和 ThreadLocal 组合管理事务

2.1 使用 ThreadLocal 来确保所有 dao 操作都在同一个 Connection 连接对象中完

原理示意图

JdbcUtils 工具类的修改:

public class JdbcUtils {private static DruidDataSource dataSource;private static ThreadLocal<Connection> threadLocal = new ThreadLocal<>();// 在静态代码块中创建数据库连接static {try {// 获取配置文件jdbc.properties对象Properties 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,说明获取连接失败<br/>有值就是获取连接成功*/public static Connection getConnection() {Connection conn = threadLocal.get();if (conn == null) {try {conn = dataSource.getConnection(); //从数据库连接池中获取连接threadLocal.set(conn);  // 保存到 ThreadLocal 对象中,供后面的 jdbc 操作使用conn.setAutoCommit(false); // 设置手动管理事务} catch (SQLException e) {throw new RuntimeException(e);}}return conn;}/*** 提交事务,并关闭释放连接*/public static void commitAndClose(){Connection conn = threadLocal.get();if (conn != null) { // 如果不等于 null,说明 之前使用过连接,操作过数据库try {conn.commit(); //提交事务} catch (SQLException e) {e.printStackTrace();}finally {try {conn.close(); // 关闭资源} catch (SQLException e) {e.printStackTrace();}}}// 一定要执行 remove 操作,否则就会出错。(因为 Tomcat 服务器底层使用了线程池技术)threadLocal.remove();}/*** 回滚事务,并关闭释放连接*/public static void rollbackAndClose(){Connection conn = threadLocal.get();if (conn != null) { // 如果不等于 null,说明 之前使用过连接,操作过数据库try {conn.commit(); //回滚事务} catch (SQLException e) {e.printStackTrace();}finally {try {conn.close(); // 关闭资源} catch (SQLException e) {e.printStackTrace();}}}// 一定要执行 remove 操作,否则就会出错。(因为 Tomcat 服务器底层使用了线程池技术)threadLocal.remove();}/*** 关闭连接,放回数据库连接池** @param connection 放入要关闭的连接对象*/// public static void close(Connection connection) {//     try {//         if (connection != null) {//             connection.close();//         }//     } catch (SQLException e) {//         e.printStackTrace();//     }// }
}
修改 BaseDao


public abstract class BaseDao {private final QueryRunner queryRunner = new QueryRunner();/*** 用来执行:Insert\Update\Delete 语句** @param sql  传入要执行的sql语句* @param args 参数列表* @return 返回-1表示update失败,否则成功*/public int update(String sql, Object... args) {Connection conn = JdbcUtils.getConnection();try {return queryRunner.update(conn, sql, args);} catch (SQLException e) {throw new RuntimeException(e);}}/*** 查询返回一个 javaBean 的 sql 语句* @param type 返回的对象类型* @param sql 执行的 sql 语句* @param args sql 对应的参数值* @param <T> 返回的类型的泛型* @return 若返回为null,则查看失败*/public <T> T queryForOne(Class<T> type, String sql, Object... args) {Connection conn = JdbcUtils.getConnection();try {return queryRunner.query(conn, sql, new BeanHandler<T>(type), args);} catch (SQLException e) {throw new RuntimeException(e);}}/*** 查询返回多个 javaBean 的 sql 语句** @param type 返回的对象类型* @param sql  执行的 sql 语句* @param args sql 对应的参数值* @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) {throw new RuntimeException(e);}}/*** 执行返回一行一列的 sql 语句* @param sql 执行的 sql 语句* @param args sql对应的参数* @return 若返回为null, 则查看失败*/public Object queryForSingleValue(String sql,Object... args) {Connection conn = JdbcUtils.getConnection();try {return queryRunner.query(conn,sql,new ScalarHandler(),args);} catch (SQLException e) {throw new RuntimeException(e);}}
}

2.2 使用 Filter 过滤器统一给所有的 Service 方法都加上 try-catch,来进行实现的管理

原理分析图:
Filter 类代码:

public class TransactionFilter implements Filter {public void init(FilterConfig config) throws ServletException {}public void destroy() {}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {try {chain.doFilter(request, response);JdbcUtils.commitAndClose();  // 提交事务} catch (Exception e) {JdbcUtils.rollbackAndClose(); // 回滚事务e.printStackTrace();}}
}
在 web.xml 中的配置:

    <filter><filter-name>TransactionFilter</filter-name><filter-class>com.chenyixin.book.filter.TransactionFilter</filter-class></filter><filter-mapping><filter-name>TransactionFilter</filter-name><!-- /* 表示当前工程下的所有请求 --><url-pattern>/*</url-pattern></filter-mapping>
一定要记得把 BaseServlet 中的异常往外抛给 Filter 过滤器:

@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 解决post请求中文乱码问题// 一定要在获取请求参数之前调用才有效request.setCharacterEncoding("UTF-8");String action = request.getParameter("action");// 获取 action 业务鉴别字符串,获取相应的业务 方法反射对象try {Method method = this.getClass().getDeclaredMethod(action, HttpServletRequest.class, HttpServletResponse.class);// 调用目标业务 方法method.invoke(this, request, response);} catch (Exception e) {// e.printStackTrace();throw new RuntimeException(e);}}

2.3 将所有异常都统一交给 Tomcat,让 Tomcat 展示友好的错误信息页面

在 web.xml 中我们可以通过错误页面配置来进行管理
<!-- error-page 标签配置,服务器出错之后,自动跳转的页面 --><error-page><!-- error-code 是错误类型--><error-code>404</error-code><!-- location 标签表示。要跳转去的页面 --><location>/pages/error/error404.jsp</location></error-page><error-page><error-code>500</error-code><location>/pages/error/error500.jsp</location></error-page>

error404.jsp页面:

<%@ 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>error404</title><%-- 静态包含 base标签、css样式、jQuery文件 --%><%@ include file="/pages/common/head.jsp"%></head>
<body>
<h1>很抱歉。您访问的页面不存在,或已经被删除!!! </h1><br/>
<h1><a href="index.jsp">返回首页</a></h1></body>
</html>

error500.jsp页面:

<%@ 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>error404</title><%-- 静态包含 base标签、css样式、jQuery文件 --%><%@ include file="/pages/common/head.jsp"%></head>
<body>
<h1>很抱歉,您访问的后台程序出现了错误,程序猿小哥,正在努力的为您抢修!!! </h1><br/>
<h1><a href="index.jsp">返回首页</a></h1></body>
</html>

书城项目第九阶段

1. 使用 AJAX 验证用户名是否可用

步骤:

        1.在UserServlet 程序中添加 ajaxExistsUsername 方法

        2regist.jsp 页面中的代码

1.在UserServlet 程序中添加 ajaxExistsUsername 方法
/*** 注册用户名的失去焦点事件,判断用户名是否存在** @param request* @param response* @throws ServletException* @throws IOException*/protected void ajaxExistsUsername(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 获取请求的参数 usernameString username = request.getParameter("username");// 调用 userService.existsUsername();boolean existsUsername = userService.existsUsername(username);// 把返回的结果封装成为 map 对象Map<String, Object> map = new HashMap<>();map.put("existsUsername", existsUsername);Gson gson = new Gson();String json = gson.toJson(map);response.getWriter().write(json);}

2regist.jsp 页面中的代码

// 给用户名添加失去焦点事件$("#username").blur(function () {// 获取用户名var username = this.value;if (username != "") {$.getJSON("userServlet", "action=ajaxExistsUsername&username=" + username, function (data) {if (data.existsUsername) {$("span.errorMsg").text("用户名已存在!");} else {$("span.errorMsg").text("用户名可用!");}});} else {$("span.errorMsg").text("用户名不能为空!");}});

2. 使用 AJAX 修改把商品添加到购物车

CartServlet 程序:
/*** 加入购物车** @param request* @param response* @throws ServletException* @throws IOException*/protected void ajaxAddItem(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 1. 获取请求的参数 商品编号int id = WebUtils.parseInt(request.getParameter("id"), 0);// 2. 调用 bookService.queryBookById(id):Book 得到图书的信息Book book = bookService.queryBookById(id);// 3. 把图书信息,转换成为 CartItem 商品项CartItem cartItem = new CartItem(book.getId(), book.getName(), 1, book.getPrice(), book.getPrice());// 4. 调用 Cart.addItem(CartItem);添加商品项Cart cart = (Cart) request.getSession().getAttribute("cart");if (cart == null) {cart = new Cart();request.getSession().setAttribute("cart", cart);}cart.addItem(cartItem);// 最后一个添加商品的名称request.getSession().setAttribute("lastName", cartItem.getName());// 返回购物车总的商品数量和最后一个添加的商品名称Map<String, Object> map = new HashMap<>();map.put("totalCount", cart.getTotalCount());map.put("lastName", cartItem.getName());Gson gson = new Gson();String resultMapJsonString = gson.toJson(map);response.getWriter().write(resultMapJsonString);}

index.jsp页面

<%@ 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><%@include file="/pages/common/head.jsp" %><script type="text/javascript">$(function () {$(".addToCart").click(function () {/*** 在事件响应的 function 函数 中,有一个 this 对象,这个 this 对象,是当前正在响应事件的 dom 对象* attr()    可以设置和获取属性的值* location.href: 返回当前页面的完整的URL地址;*/var bookId = $(this).attr("bookId");// location.href = "cartServlet?action=addItem&id=" + bookId;$.getJSON("cartServlet", "action=ajaxAddItem&id=" + bookId, function (data) {$(".totalCount").text("您的购物车中有" + data.totalCount + "件商品");$(".lastName").text(data.lastName);});});});</script>
</head>
<body><div id="header"><img class="logo_img" alt="" src="static/img/logo.gif"><span class="wel_word">网上书城</span><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="orderServlet?action=showMyOrders&userId=${sessionScope.user.id}">我的订单</a><a href="userServlet?action=logout">注销</a></c:if>&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="client/bookServlet" 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><c:if test="${empty sessionScope.cart.items}"><div style="text-align: center"><span class="totalCount"> </span><div><span class="lastName" style="color: red">您当前的购物车里没有商品哦</span></div></div></c:if><c:if test="${not empty sessionScope.cart.items}"><div style="text-align: center"><span class="totalCount">您的购物车中有${sessionScope.cart.totalCount}件商品</span><div>您刚刚将<span class="lastName" style="color: red">${sessionScope.lastName}</span>加入到了购物车中</div></div></c:if><%--  书籍的开始  --%><c:forEach items="${requestScope.page.items}" var="book"><div class="b_list"><div class="img_div"><img class="book_img" alt="" src="${book.img_path}"/></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 bookId="${book.id}" class="addToCart">加入购物车</button></div></div></div></c:forEach><%--  书籍的结束 --%></div><%--  分页条的开始  --%><%@include file="/pages/common/nav.jsp" %><%--  分页条的结束  --%></div>
<%@include file="/pages/common/footer.jsp" %>
</body>
</html>

JavaWeb 尚硅谷书城项目相关推荐

  1. Java web 2022跟学尚硅谷书城项目完整开源分享

    Java web 2022跟学尚硅谷书城项目完整开源分享 项目介绍 项目类图 sql建表和添加数据 界面展示 用户登录界面 用户注册界面 首页 添加购物车 我的购物车 我的订单 功能说明 项目介绍 跟 ...

  2. 尚硅谷前端项目开发笔记

    尚硅谷前端项目开发笔记 B站视频直达,这个项目亮点在于所有 API 请求都并非在组件内编写,而是在组件内使用this.$store.dispatch() 派发任务,再由 Vuex(actions.mu ...

  3. 基于JavaWeb实现的书城项目:阶段五至阶段六

    第五阶段 分页模型 分页实现 根据上图所述流程,首先需要创建一个Page对象,保存以下几个页面属性: pageNo 当前页码 pageTotal 总页码 pageTotalCount 总记录数 pag ...

  4. 尚硅谷VUE项目-前端项目问题总结07--产品详情页【vuex-排他操作foreach-放大镜-轮播图-兄弟组件通信$bus-购物车-路由跳转传参-路由传参+会话存储】-游客身份-节流

    尚硅谷VUE项目-前端项目问题总结07---产品详情页 1.静态组件(详情页还未注册为路由组件) 2.发请求 3.vuex-获取产品详情信息 3.1放大镜 3.2 属性值[排他操作] 3.3轮播图[j ...

  5. JavaWeb尚硅谷【学习笔记】(整合)未完成

    文章目录 一.HTML 1.网页的组成部分 2.HTML简介 3.HTML文件的书写规范 4.HTML标签的介绍 5.标签的语法 6.常用标签介绍 二.CSS技术 1.CSS技术介绍 2.CSS语法规 ...

  6. JAVAWEB之小说书城项目

    一,项目目的 1.熟悉网站开发的基本流程. 2.将以学习的知识进行复习总结. 3,明确自身知识薄弱区. 二,项目内容 使用所学习对jsp,HTML,Mysql,css等知识制作一个小型的网页,初步实现 ...

  7. 基于 JavaWeb 的在线书城项目

    开源项目:atguigu-online-bookshop 开发时间:2022.02.17 - 2022.04.19 文章目录 一.快速开始 二.功能演示 1.用户登录 2.用户注册 3.尚书房首页 4 ...

  8. 02尚硅谷书城案例-用户的注册

    1.JavaEE 项目的三层架构 2.先创建书城需要的数据库和表 drop database if exists book; create database book; CREATE TABLE t_ ...

  9. 尚硅谷VUE项目实战,前端项目-尚品汇

    001-尚硅谷-尚品汇-教程简介_哔哩哔哩_bilibili 需要的知识点   搭建vue-router pages文件夹放置路由页面 router文件夹配置路由 回到入口文件注册(main.js) ...

最新文章

  1. 推荐系列:2008年第07期 总9期
  2. 【程序员跳槽】学弟被高薪挖走不到一年就后悔的无奈经历,到底是老板的水深?还是学弟太年轻?
  3. 系统设计面试题思路综述
  4. asp.net Forums 之HttpHandler和HttpModule
  5. python androidhelper kivy_顶SLA4、QPython学习笔记
  6. 于我,过去,现在和未来 —— 西格里夫·萨松
  7. 受 SQLite 多年青睐,C 语言到底好在哪儿?
  8. pug模板引擎(原jade)
  9. 《大数据》第一届编委会顾问委员会议圆满召开
  10. 444. Sequence Reconstruction
  11. Matlab中出现“无法打开电子表格,MATLAB报告了以下错误;错误:服务器出现意外情况”
  12. Shell 常用积累
  13. 【渝粤教育】国家开放大学2018年春季 0538-21T社区护理 参考试题
  14. C#,数独游戏(Sudoku Game)的算法与源代码
  15. Mac配置OpenGL环境
  16. Unite Beijing 2018 | 精彩游戏案例议题曝光
  17. LimeSDR实验教程(5) WiFi解调
  18. 苹果 服务通知 V2
  19. Java抓取网页图片
  20. quorum examples初探

热门文章

  1. 计算机课可以用mac吗,粉笔直播课可不可以用苹果电脑看
  2. 圣诞节主题头像生成器
  3. 学习分享——基于深度学习的NILM负荷分解(三)深度学习处理,基础思路
  4. 腾讯花18亿获捷成独家授权!影视版权格局再变动
  5. 华为云WeLink会议系统还有“隐藏功能”?这些你都知道吗?
  6. 汉邦高科参与华为鸿蒙系统,汉邦高科(300449)03月09日10:30大单揭秘
  7. 另一种持续集成工具Bamboo
  8. 和画意思相近的字_跟若、似、如意思相近的字还有什么?
  9. 基于SpringCloud的微服务架构分析,神仙框架!
  10. 滑动窗口,看这篇就够了(java)