书城项目第一阶段:表单验证
需求:
验证用户名:必须由字母,数字下划线组成,并且长度为 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/controllerservice 层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.jarmysql-connector-java-8.0.27.jar以下是测试需要:hamcrest-core-1.3.jarjunit-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 包
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 {
@Override
public User queryUserByUsername(String username) {
String sql = "select username,password,email from t_user where username=?";
return queryForOne(User.class,sql,username);
}
@Override
public 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);
}
@Override
public 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();
@Test
public void queryUserByUsername() {
if (userDao.queryUserByUsername("admin134") == null) {
System.out.println("用户名可用!");
} else {
System.out.println("用户名已存在!");
}
}
@Test
public void queryUserByUsernameAndPassword() {
if (userDao.queryUserByUsernameAndPassword("admin", "admin1234") == null) {
System.out.println("用户名或密码错误,登录失败");
} else {
System.out.println("查询成功");
}
}
@Test
public 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();
@Override
public User login(User user) {
return userDao.queryUserByUsernameAndPassword(user.getUsername(), user.getPassword());
}
@Override
public void regist(User user) {
userDao.saveUser(user);
}
@Override
public boolean existsUsername(String username) {
return userDao.queryUserByUsername(username) != null;
}
}
UserServiceTest类测试:
public class UserServiceTest {
UserService userService = new UserServiceImpl();
@Test
public void login() {
System.out.println(userService.login(new User(null, "aaa", "666666", null)));
}
@Test
public void regist() {
userService.regist(new User(null, "aaa", "666666", "bbj168@qq.com"));
}
@Test
public void existsUsername() {
if (userService.existsUsername("aaa")) {
System.out.println("用户名已存在!");
} else {
System.out.println("用户名可用!");
}
}
}
7. 编写 web 层
7.1 实现用户注册的功能
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类 并继承 HttpServlet2、获取请求的参数3、检查 验证码是否正确正确4、检查 用户名是否可用可用调用 Service 保存到数据库不可用跳回注册页面不正确跳回注册页面
代码:
public class RegistServlet extends HttpServlet {
@Override
protected 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 用户登录功能的实现
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 类 并继承 HttpServlet2、获取请求的参数3、调用 userService.login()登录处理业务(检查用户名密码是否正确)正确跳到成功页面 login_success.html不正确跳回登录页面
public class LoginServlet extends HttpServlet {
private UserService userService = new UserServiceImpl();
@Override
protected 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 动态化
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 ©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>
<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. 登录,注册错误提示,及表单回显
表单要回显信息:
4. BaseServlet 的抽取
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();
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String action = request.getParameter("action");
if ("login".equals(action)) {
login(request, response);
} else if ("regist".equals(action)) {
regist(request, response);
}
}
/**
* 处理登录功能
*
* @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();
@Override
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();
}
}
}
5. 数据的封装和抽取 BeanUtils 的使用
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;
}
}
@Override
public String toString() {
return "Book{" +
"id=" + id +
", name='" + name + '\'' +
", author='" + author + '\'' +
", price=" + price +
", sales=" + sales +
", stock=" + stock +
", img_path='" + img_path + '\'' +
'}';
}
}
1.3、编写图书模块的 Dao 和测试 Dao
public interface BookDao {
int addBook();
int deleteBook();
int update();
Book queryBookById();
List<Book> queryBooks();
}
BookDaoImpl实现类:
public class BookDaoImpl extends BaseDao implements BookDao {
@Override
public 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());
}
@Override
public int deleteBookById(Integer id) {
String sql = "delete from t_book where id = ?";
return update(sql,id);
}
@Override
public 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());
}
@Override
public 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);
}
@Override
public 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();
@Test
public void addBook() {
bookDao.addBook(new Book(null,"强哥好帅","chenyixin",new BigDecimal(123),999,999,null));
System.out.println("添加成功");
}
@Test
public void deleteBookById() {
bookDao.deleteBookById(21);
}
@Test
public void updateBook() {
bookDao.updateBook(new Book(21, "大家都好帅", "佚名", new BigDecimal(123), 999, 999, null));
}
@Test
public void queryBookById() {
System.out.println(bookDao.queryBookById(21));
}
@Test
public 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();
@Override
public void addBook(Book book) {
bookDao.addBook(book);
}
@Override
public void delete(Integer id) {
bookDao.deleteBookById(id);
}
@Override
public void update(Book book) {
bookDao.updateBook(book);
}
@Override
public Book queryBookById(Integer id) {
return bookDao.queryBookById(id);
}
@Override
public List<Book> queryBooks() {
return bookDao.queryBooks();
}
}
测试:
public class BookServiceTest {
private BookService bookService = new BookServiceImpl();
@Test
public void addBook() {
bookService.addBook(new Book(null,"强哥好帅","chenyixin",new BigDecimal(123),999,999,null));
System.out.println("修改成功");
}
@Test
public void delete() {
bookService.delete(22);
}
@Test
public void update() {
bookService.update(new Book(22, "大家都好帅", "佚名", new BigDecimal(123), 999, 999, null));
}
@Test
public void queryBookById() {
System.out.println( bookService.queryBookById(22));
}
@Test
public 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 添加图书功能的实现
问题说明:表单重复提交:
思路步骤:
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. 修改删除的连接地址
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");
}
public static int parseInt(String strId, int defaultValue) {
try {
return Integer.parseInt(strId);
} catch (NumberFormatException e) {
e.printStackTrace();
}
return defaultValue;
}
<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 修改图书功能的实现
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>
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 页面
/**
* 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;
protected void page(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1.获取请求参数 pageNo 和 pageSize
int 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.jsp
request.getRequestDispatcher("/pages/manager/book_manager.jsp").forward(request, response);
}
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实现类:
@Override
public Integer queryForPageTotalCount() {
String sql = "select count(*) from t_book";
Number number = (Number) queryForSingleValue(sql);
return number.intValue();
}
@Override
public 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程序
@Test
public void queryForPageTotalCount() {
System.out.println(bookDao.queryForPageTotalCount());
}
@Test
public 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 分页模块中跳转到指定页数功能实现
// 给按钮绑定点击事件
$("#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 分页模块中的页码显示
分析情况 1 :如果总页码小于等于 5 的情况,页码的范围是: 1- 总页码1 页 12 页 1,23 页 1,2, 34 页 1,2, 3 , 45 页 1,2, 3 , 4 , 5情况 2 :总页码大于 5 的情况。假设一共 10 页小情况 1 :当前页码为前面 3 个: 1 , 2 , 3 的情况,页码范围是: 1-5.【1 】 2 , 3 , 4 , 51【 2 】 3 , 4 , 51,2 【 3 】 4 , 5小情况 2 :当前页码为最后 3 个, 8 , 9 , 10 ,页码范围是:总页码减 4 - 总页码6, 7 【 8 】 9 , 106, 7 , 8 【 9 】 106, 7 , 8 , 9 【 10 】小情况 3 : 4 , 5 , 6 , 7 ,页码范围是:当前页码减 2 - 当前页码加 22, 3, 【4】 , 5 , 63, 4 ,【 5】 , 6 , 74, 5 ,【 6】 , 7 , 85, 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 修改分页后,增加,删除,修改图书信息的回显页面
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 代码实现
2.编写ClientBookServlet程序,并继承BaseServlet父类
protected void page(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1.获取请求参数 pageNo 和 pageSize
int 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.jsp
request.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>
<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 页面
2.在 Servlet 程序的 page 分页方法中设置 url 的分页请求地址
在ClientBookServlet类中
<%--
Created by IntelliJ IDEA.
User: pc
Date: 2022/7/28
Time: 21:37
To 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 和 pageSize
int 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.js
request.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程序
@Override
public 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();
}
@Override
public 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程序
@Test
public void queryForPageTotalCountByPrice() {
System.out.println(bookDao.queryForPageTotalCountByPrice(10,50));
}
@Test
public void queryForPageItemsByPrice() {
for (Book item : bookDao.queryForPageItemsByPrice(0,4,10,50)) {
System.out.println(item);
}
}
6.测试BookService程序
@Test
public void pageByPrice() {
System.out.println(bookService.pageByPrice(1,4,10,50));
}
项目第五阶段 -- 完善登录/注册功能
1. 登陆---显示用户名
需求:在用户登录成功后显示用户名欢迎菜单
步骤:1. UserServlet 程序中保存用户登录的信息2.修改 login_succuess_menu.jsp3.还要修改首页 index.jsp 页面的菜单(没登陆时显示登录/注册,若已登录显示用户欢迎菜单)
/**
* 处理登录功能
*
* @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
<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>
<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. 修改【注销】的菜单地址
/**
* 注销登录功能
*
* @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.jar2、在 web.xml 中去配置用于生成验证码的 Servlet 程序3、在表单中使用 img 标签去显示验证码图片并使用它4、在服务器获取谷歌生成的验证码和客户端发送过来的验证码比较使用。
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 可以当作是验证码值的key
String 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;
}
@Override
public String toString() {
return "Cart{" +
"totalCount=" + getTotalCount() +
", totalPrice=" + getTotalPrice() +
", items=" + items +
'}';
}
}
2.3 测试Cart类
public class CartTest {
Cart cart = new Cart();
@Test
public 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);
}
@Test
public 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);
}
@Test
public 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);
}
@Test
public 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. 重定向回原来商品所在的地址页面
<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属性
/**
* 清空购物车
* @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 () {
// 获取商品id
var 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 {
@Override
public 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 {
@Override
public 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();
@Test
public 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();
@Test
public 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();
@Override
public String createOrder(Cart cart, Integer userId) {
// 获取唯一性的订单号
String orderId = System.currentTimeMillis() + "" + userId;
// 创建一个订单对象
Order order = new Order(orderId, new Date(), cart.getTotalPrice(), 0, userId);
// 保存订单
orderDao.saveOrder(order);
// 遍历购物车中每一个商品项转换成为订单项保存到数据库
for (Map.Entry<Integer, CartItem> 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();
@Test
public 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 页面,输出订单号
@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");
// 获取 Userid
User loginUser = (User) request.getSession().getAttribute("user");
// 判断用户是否存在,若不存在,则转发到登录界面
if (loginUser == null) {
request.getRequestDispatcher("/pages/user/login.jsp").forward(request, response);
return;
}
// 调用 orderService.createOrder(Cart,Userid);生成订单 并返回订单号
// 获取用户id
Integer 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 页面,结账的请求地址
6.2 获取所有订单功能(管理员)
步骤:1. 编写OrderServlet 程序2. 修改 manager_menu.jsp 页面,订单管理的请求地址3. 修改 pages/manager/order_manager.jsp 页面,遍历订单
/**
* 查看所有订单
*
* @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 页面,订单管理的请求地址
<%@ 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 程序
2. 编写OrderServlet 程序
/**
* 管理员发货功能
* @param request
* @param response
* @throws ServletException
* @throws IOException
*/
protected void sendOrder(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取参数 orderId
String 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 {
// 获取参数 orderId
String 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页面,遍历数据
/**
* 查看我的订单
*
* @param request
* @param response
* @throws ServletException
* @throws IOException
*/
protected void showMyOrders(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取参数 orderId
int 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 程序
/**
* 签收订单功能
* @param request
* @param response
* @throws ServletException
* @throws IOException
*/
protected void receiverOrder(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取参数 orderId
String 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() {
}
@Override
public 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 连接对象中完成
原理示意图
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();
// }
// }
}
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,来进行实现的管理
public class TransactionFilter implements Filter {
public void init(FilterConfig config) throws ServletException {
}
public void destroy() {
}
@Override
public 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();
}
}
}
<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>
@Override
protected 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 展示友好的错误信息页面
<!-- 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 方法
2. regist.jsp 页面中的代码
/**
* 注册用户名的失去焦点事件,判断用户名是否存在
*
* @param request
* @param response
* @throws ServletException
* @throws IOException
*/
protected void ajaxExistsUsername(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取请求的参数 username
String 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);
}
2. regist.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 修改把商品添加到购物车
/**
* 加入购物车
*
* @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>
<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>