书城管理系统(Java web)

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

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

书城项目第二阶段:用户注册和登陆

需求 1:用户注册
1)访问注册页面
2)填写注册信息,提交给服务器
3)服务器应该保存用户
4)当用户已经存在 ===>>> 提示用户注册失败,用户名已存在
5)当用户不存在 ===>>> 册成功
需求 2:用户登陆
1)访问登陆页面
2)填写用户名密码后提交
3)服务器判断用户是否存在
4)如果登陆失败 ===>>> 返回用户名或者密码错误信息
5)如果登录成功 ===>>> 返回登陆成功 信息

一、JavaEE 项目的三层架构

1、客户端:html、css、JavaScript、JQuery
http://ip:port/工程路径/资源路径
后端处理结果相应在客户端,解析展示在前端页面
2、JavaEE 项目的三层架构
Web层:Servlet程序、SpringMVC
(1) 获取请求参数,封装成为Bean对象
(2) 调用Service层处理业务
(3) 响应数据给客户端,请求转发/重定向
Service业务层:Spring
(1) 处理业务逻辑
(2) 调用持久层保存到数据库
DAO持久层:JDBC、DbUtils、Mybatis、HIbernate
负责与数据库交互,CRUD操作;增删改查
3、数据库:MySQL、Oracle、Sql Server

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

drop database if exists book;
create database book;
use book;
create table t_user( `id` int primary key auto_increment,         `username` varchar(20) not null unique, `password` varchar(32) not null, `email` varchar(200),`power` int
);
insert into t_user(`username`,`password`,`email`,`power`) values('admin','admin','admin@atanhui.com',2);
select * from t_user;

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

package com.atanhui.pojo;public class User {private Integer id;private String username;private String password;private String email;private Integer power;public Integer getPower() {return power;}public void setPower(Integer power) {this.power = power;}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public String getEmail() {return email;}public void setEmail(String email) {this.email = email;}public User() {}public User(Integer id, String username, String password, String email, Integer power) {this.id = id;this.username = username;this.password = password;this.email = email;this.power = power;}@Overridepublic String toString() {return "User{" +"id=" + id +", username='" + username + '\'' +", password='" + password + '\'' +", email='" + email + '\'' +", power=" + power +'}';}
}

四、编写工具类 JdbcUtils

1、导入需要的 jar 包
druid-1.1.9.jar
mysql-connector-java-5.1.7-bin.jar
hamcrest-core-1.3.jar
junit-4.12.jar
2、在 src 源码目录下编写 jdbc.properties 属性配置文件:

username=root
password=root
url=jdbc:mysql://localhost:3306/book?useUnicode=true&characterEncoding=utf-8
driverClassName=com.mysql.jdbc.Driver
initialSize=5
maxActive=10

3、编写 JdbcUtils 工具类:

package com.atanhui.utils;import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;public class JdbcUtils {// 数据库连接池private static DruidDataSource dataSource;private static ThreadLocal<Connection> conns = new ThreadLocal<Connection>();static {try {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();}}/** 如果返回null,说明获取连接失败,有值就是获取连接成功* 获取数据库连接池中的连接*/public static Connection getConnection(){Connection conn = conns.get();if (conn == null) {try {conn = dataSource.getConnection();      // 从数据库连接池中获取连接conns.set(conn);        // 保存到threadlocal对象中,用于后面的jdbc操作使用conn.setAutoCommit(false);      // 设置手动管理事务} catch (Exception e) {e.printStackTrace();}}return conn;}/** 提交事务,关闭连接,放回数据连接池*/public static void commitAndClose(){Connection conn = conns.get();if (conn != null) {     // 如果不等于null,说明之前使用过,操作过数据库try {conn.commit();      // 提交事务} catch (Exception e) {e.printStackTrace();} finally {try {conn.close();       // 关闭连接} catch (Exception e) {e.printStackTrace();}}}// 一定要执行remove操作,否则会出错,因为Tomcat服务器底层使用了线程池技术conns.remove();}/** 回滚事务,关闭连接,放回数据库连接池*/public static void rollbackAndClose(){Connection conn = conns.get();if (conn != null) {     // 如果不等于null,说明之前使用过,操作过数据库try {conn.rollback();      // 回滚事务} catch (Exception e) {e.printStackTrace();} finally {try {conn.close();       // 关闭连接} catch (Exception e) {e.printStackTrace();}}}// 一定要执行remove操作,否则会出错,因为Tomcat服务器底层使用了线程池技术conns.remove();}
}

4、JdbcUtils 测试

package com.atanhui.test;import com.atanhui.utils.JdbcUtils;
import org.junit.Test;
import java.sql.Connection;public class JdbcUtilsTest {@Testpublic void testJdbcUtils() {for (int i = 0; i < 100; i++) {Connection connection = JdbcUtils.getConnection();System.out.println(connection);JdbcUtils.commitAndClose();}}
}

五、编写 BaseDao

1、导入 DBUtils 的 jar 包
commons-dbutils-1.3.jar
2、编写 BaseDao

package com.atanhui.dao.impl;import com.atanhui.utils.JdbcUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;public abstract class BaseDAO {// DBUtils操作数据库private QueryRunner queryRunner = new QueryRunner();// update方法 用来执行insert update delete语句// 如果return返回-1,说明执行失败,返回其他,表示返回影响的行数public int update(String sql, Object...args) {System.out.println("baseDAO程序在【" + Thread.currentThread().getName() + "】线程中");Connection conn = JdbcUtils.getConnection();try {return queryRunner.update(conn, sql, args);} catch (SQLException e) {e.printStackTrace();// 捕获异常,向外抛出异常throw new RuntimeException(e);}}// 查询一条javaBean的sql语句// type 返回对象类型,sql 执行的sql语句,args sql对应的参数值,<T> 返回的类型的泛型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 (Exception e) {e.printStackTrace();throw new RuntimeException(e);}}// 查询多条javaBean的sql语句// type 返回对象类型,sql 执行的sql语句,args sql对应的参数值,<T> 返回的类型的泛型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 (Exception e) {e.printStackTrace();throw new RuntimeException(e);}}// 执行返回一行一列的sql语句// sql 执行sql的语句,args sql对应的参数值public Object queryForSingleValue(String sql, Object...args) {Connection conn = JdbcUtils.getConnection();try {return queryRunner.query(conn, sql, new ScalarHandler(), args);} catch (SQLException e) {e.printStackTrace();throw new RuntimeException(e);}}
}

六、编写 UserDao 和测试

UserDao 接口

package com.atanhui.dao;import com.atanhui.pojo.User;import java.util.List;public interface UserDAO {// 根据用户名查询用户信息// 如果返回null,说明没有用户信息,反之亦然public User queryUserByUsername(String username);// 保存用户信息// 返回-1,表示操作失败,其他是sql语句影响的行数public int saveUser(User user);// 根据用户名和密码查询用户信息// 如果返回null,说明用户名和密码错误public User queryUserByUsernameAndPassword(String username, String password);// 查询全部用户信息public List<User> queryUser();// 修改用户信息public int updateUser(User user);// 删除用户信息public int deleteUserById(Integer id);// 根据用户编号查询用户public User queryUserById(Integer id);Integer queryForPageTotalCount();List<User> queryForPageItems(int begin, int pageSize);
}

UserDaoImpl 实现类

package com.atanhui.dao.impl;import com.atanhui.dao.UserDAO;
import com.atanhui.pojo.User;import java.util.List;public class UserDAOImpl extends BaseDAO implements UserDAO {@Overridepublic User queryUserByUsername(String username) {String sql = "select id,username,password,email,power from t_user where username=?";return queryForOne(User.class, sql, username);}@Overridepublic int saveUser(User user) {String sql = "insert into t_user(username,password,email,power) values(?,?,?,?)";return update(sql, user.getUsername(),user.getPassword(),user.getEmail(),user.getPower());}@Overridepublic User queryUserByUsernameAndPassword(String username, String password) {String sql = "select id,username,password,email,power from t_user where username=? and password=?";return queryForOne(User.class, sql, username, password);}@Overridepublic List<User> queryUser() {String sql = "select id,username,password,email,power from t_user";return queryForList(User.class, sql);}@Overridepublic int updateUser(User user) {String sql = "update t_user set username=?, password=?, email=?, power=? where id=?";return update(sql, user.getUsername(), user.getPassword(), user.getEmail(), user.getPower(), user.getId());}@Overridepublic int deleteUserById(Integer id) {String sql = "delete from t_user where id=?";return update(sql, id);}@Overridepublic User queryUserById(Integer id) {String sql = "select id,username,password,email,power from t_user where id=?";return queryForOne(User.class, sql, id);}@Overridepublic Integer queryForPageTotalCount() {String sql = "select count(*) from t_user ";Number count = (Number)queryForSingleValue(sql);return count.intValue();}@Overridepublic List<User> queryForPageItems(int begin, int pageSize) {String sql = "select id,username,password,email,power from t_user limit ? , ?";return queryForList(User.class, sql, begin, pageSize);}
}

UserDao 测试

package com.atanhui.dao;import com.atanhui.dao.impl.UserDAOImpl;
import com.atanhui.pojo.User;
import org.junit.jupiter.api.Test;public class UserDAOTest {UserDAOImpl userDAO = new UserDAOImpl();@Testpublic void queryUserByUsername() {if (userDAO.queryUserByUsername("admin")==null) {System.out.println("用户名可用");} else {System.out.println("用户名已存在");}}@Testpublic void saveUser() {User user = new User();user.setUsername("zhang");user.setPassword("zhang");user.setEmail("zhang@163.com");if (userDAO.saveUser(user)==-1) {System.out.println("插入失败");} else {System.out.println("插入成功");}}@Testpublic void queryUserByUsernameAndPassword() {if (userDAO.queryUserByUsernameAndPassword("admin","admin")==null) {System.out.println("用户名,密码错误,登录失败!");} else {System.out.println("登录成功!");}}@Testpublic void queryUser() {userDAO.queryUser().forEach(System.out::println);}@Testpublic void updateUser() {System.out.println(userDAO.updateUser(new User(10, "黎明", "liming", "liming@186.com", 0)));}@Testpublic void deleteUserById() {System.out.println(userDAO.deleteUserById(15));}@Testpublic void queryForPageTotalCount() {System.out.println(userDAO.queryForPageTotalCount());}@Testpublic void queryForPageItems() {userDAO.queryForPageItems(1, 4).forEach(System.out::println);}
}

七、编写 UserService 和测试

UserService 接口

package com.atanhui.service;import com.atanhui.pojo.Page;
import com.atanhui.pojo.User;import java.util.List;public interface UserService {// 注册用户 添加用户public int registUser(User user);// 登录public User login(User user);// 用户名是否可用,返回true,表示用户名已存在,返回false,表示用户名可用public boolean existsUsername(String username);// 修改用户public int updateUser(User user);// 删除用户public int deleteUserById(Integer id);// 根据用户编号查询用户public User queryUserById(Integer id);// 查询全部用户public List<User> queryUser();Page<User> page(int pageNo, int pageSize);
}

UserServiceImpl 实现类

package com.atanhui.service;import com.atanhui.dao.UserDAO;
import com.atanhui.dao.impl.UserDAOImpl;
import com.atanhui.pojo.Page;
import com.atanhui.pojo.User;
import com.atanhui.service.UserService;import java.util.List;public class UserServiceImpl implements UserService {private UserDAO userDAO = new UserDAOImpl();@Overridepublic int registUser(User user) {return userDAO.saveUser(user);}@Overridepublic User login(User user) {return userDAO.queryUserByUsernameAndPassword(user.getUsername(),user.getPassword());}@Overridepublic boolean existsUsername(String username) {if (userDAO.queryUserByUsername(username)==null) {// 等于null,说明没查到,没查到表示可用return false;} else {return true;}}@Overridepublic int updateUser(User user) {return userDAO.updateUser(user);}@Overridepublic int deleteUserById(Integer id) {return userDAO.deleteUserById(id);}@Overridepublic User queryUserById(Integer id) {return userDAO.queryUserById(id);}@Overridepublic List<User> queryUser() {return userDAO.queryUser();}@Overridepublic Page<User> page(int pageNo, int pageSize) {Page<User> page = new Page<User>();page.setPageSize(pageSize);Integer pageTotalCount = userDAO.queryForPageTotalCount();page.setPageTotalCount(pageTotalCount);int pageTotal = pageTotalCount / pageSize;if (pageTotalCount%pageSize > 0){pageTotal++;}page.setPageTotal(pageTotal);page.setPageNo(pageNo);int begin = (pageNo-1)*page.getPageSize();List<User> items = userDAO.queryForPageItems(begin, pageSize);page.setItems(items);return page;}
}

UserService 测试

package com.atanhui.service.impl;import com.atanhui.pojo.Page;
import com.atanhui.pojo.User;
import com.atanhui.service.UserService;
import org.junit.jupiter.api.Test;public class UserServiceTest {UserService userService = new UserServiceImpl();@Testpublic void registUser() {userService.registUser(new User(null, "yang", "yang", "yang@qq.com",0));userService.registUser(new User(null, "yan", "yan", "yan@qq.com",0));}@Testpublic void login() {System.out.println(userService.login(new User(null, "admin", "admin", null,null)));}@Testpublic void existsUsername() {if (userService.existsUsername("admin")){System.out.println("用户名已存在!");} else {System.out.println("用户名可用!");}}@Testpublic void page() {Page<User> page = userService.page(1, 4);System.out.println(page);}
}

八、编写 web 层

1、实现用户注册的功能
(1) 修改 regist.html 和 regist_success.html 页面
添加 base 标签,修改 base 标签对页面中所有相对路径的影响,修改注册表单的提交地址和请求方式
(2) 编写 RegistServlet 程序

package com.atanhui.web;import com.atanhui.pojo.User;
import com.atanhui.service.UserService;
import com.atanhui.service.impl.UserServiceImpl;import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;public class RegistServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {req.setCharacterEncoding("utf-8");resp.setContentType("text/html;charset=utf-8");super.doPost(req, resp);}private UserService userService = new UserServiceImpl();@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//  获取请求的参数String username = req.getParameter("username");String password = req.getParameter("password");String email = req.getParameter("email");String code = req.getParameter("code");// 检查 证明码是否正确  写死,要求验证码为abcdeif ("abcde".equalsIgnoreCase(code)) {// 验证码正确,检查用户名是否可用if (userService.existsUsername(username)) {req.setAttribute("username", username);req.setAttribute("email", email);// 用户名不可用,跳回注册页面req.setAttribute("msg", "用户名已存在!");req.getRequestDispatcher("/pages/user/regist.jsp").forward(req, resp);} else {// 用户名可用,调用service,保存数据库,跳到register_success.htmluserService.registUser(new User(null, username, password, email, 0));req.getRequestDispatcher("/pages/user/regist_success.jsp").forward(req, resp);}} else {req.setAttribute("username", username);req.setAttribute("email", email);// 验证码不正确,跳回注册页面req.setAttribute("msg", "验证码错误!");req.getRequestDispatcher("/pages/user/regist.jsp").forward(req, resp);}}
}

2、IDEA 中 Debug 调试的使用
3、用户登录功能的实现
(1) 修改 login.html 页面和 login_success.html 页面
添加 base 标签,修改 base 标签对页面中所有相对路径的影响,修改 login.html 表单的提交地址和请求方式
(2) 编写 LoginServlet 程序

package com.atanhui.web;import com.atanhui.pojo.User;
import com.atanhui.service.UserService;
import com.atanhui.service.impl.UserServiceImpl;import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;public class LoginServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {req.setCharacterEncoding("utf-8");resp.setContentType("text/html;charset=utf-8");super.doPost(req, resp);}private UserService userService = new UserServiceImpl();@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 获取请求的参数String username = req.getParameter("username");String password = req.getParameter("password");// 调用xxxservlet.xxx()处理业务,userServlet.login()登录// 根据login() 方法返回结果判断登录是否成功if (userService.login(new User(null,username,password,null,null))==null) {// 把错误信息,和回显的表单项信息,保存到request域中req.setAttribute("msg", "用户名或密码错误!");req.setAttribute("username", username);// 失败,跳转到登录页面System.out.println("用户名密码错误,登录失败,登录失败!");req.getRequestDispatcher("/pages/user/login.jsp").forward(req, resp);} else {// 成功,跳转到页面login_success.htmlSystem.out.println("登录成功!");req.getRequestDispatcher("/pages/user/login_success.jsp").forward(req,resp);}}
}

书城项目第三阶段

一、页面 jsp 动态化

1、在 html 页面顶行添加 page 指令。
2、修改文件后缀名为:.jsp
3、使用 IDEA 搜索替换.html 为.jsp

二、抽取页面中相同的内容

i. head 中 css、jquery、base 标签

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

ii. 每个页面的页脚

<%@ page contentType="text/html; charset=UTF-8" language="java" %>
<div id="bottom"><span>安徽大学.Copyright &copy;2021</span>
</div>

iii. 登录成功后的菜单

<%@ page contentType="text/html; charset=UTF-8" language="java" %>
<div><span>欢迎<span class="um_span">${sessionScope.user.username}</span>光临安徽大学书城</span><a href="manager/orderServlet?action=showMyOrders">我的订单</a><a href="clientServlet?action=loginout">注销</a><a href="index.jsp">返回</a>
</div>

iv. manager 模块的菜单

<%@ page contentType="text/html; charset=UTF-8" language="java" %>
<div><%--  请求资源的地址?服务器资源中调用的方法  --%><a href="manager/bookServlet?action=page">图书管理</a><a href="manager/orderServlet?action=showAllOrders">订单管理</a><a href="manager/userServlet?action=page">用户管理</a><a href="clientServlet?action=loginout">注销</a><a href="index.jsp">返回商城</a>
</div>

三、登录,注册错误提示,及表单回显

四、BaseServlet 的抽取

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

package com.atanhui.web;import com.atanhui.pojo.Page;
import com.atanhui.pojo.User;
import com.atanhui.service.UserService;
import com.atanhui.service.impl.UserServiceImpl;
import com.atanhui.utils.WebUtils;
import com.google.gson.Gson;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;public class UserServlet extends BaseServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {req.setCharacterEncoding("utf-8");resp.setContentType("text/html;charset=utf-8");super.doPost(req, resp);}private UserService userService = new UserServiceImpl();// 查看所有用户protected void showAllUsers(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {List<User> users = userService.queryUser();req.setAttribute("users", users);req.getRequestDispatcher("/pages/manager/user_manager.jsp").forward(req, resp);}protected void page(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 获取请求的参数int pageNo = WebUtils.parseInt(req.getParameter("pageNo"), 1);int pageSize = WebUtils.parseInt(req.getParameter("pageSize"), Page.PAGE_SIZE);Page<User> page = userService.page(pageNo, pageSize);page.setUrl("manager/userServlet?action=page");req.setAttribute("page", page);req.getRequestDispatcher("/pages/manager/user_manager.jsp").forward(req, resp);}protected void deleteUser(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {int id = WebUtils.parseInt(req.getParameter("id"), 0);userService.deleteUserById(id);resp.sendRedirect(req.getContextPath()+"/manager/userServlet?action=page&pageNo="+req.getParameter("pageNo"));}protected void getUser(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {int id = WebUtils.parseInt(req.getParameter("id"), 0);User user = userService.queryUserById(id);req.setAttribute("user", user);req.getRequestDispatcher("/pages/manager/user_edit.jsp").forward(req, resp);}protected void updateUser(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {User user = WebUtils.copyParamToBean(req.getParameterMap(), new User());userService.updateUser(user);resp.sendRedirect(req.getContextPath()+"/manager/userServlet?action=page&pageNo="+req.getParameter("pageNo"));}protected void addUser(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// *********int pageNo = WebUtils.parseInt(req.getParameter("pageTotal"), 0);pageNo = pageNo + 1;User user = WebUtils.copyParamToBean(req.getParameterMap(), new User());userService.registUser(user);resp.sendRedirect(req.getContextPath()+"/manager/userServlet?action=page&pageNo="+pageNo);}protected void ajaxExistsUsername(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 获取请求的参数String username = req.getParameter("username");// 调用userService.existsUsername(username)boolean existsUsername = userService.existsUsername(username);// 把返回的结果封装成为map对象Map<String, Object> resultMap = new HashMap<>();resultMap.put("existsUsername", existsUsername);Gson gson = new Gson();String json = gson.toJson(resultMap);resp.getWriter().write(json);}
}

还要给 login.jsp 添加隐藏域和修改请求地址,给 tegist.jsp 页面添加隐藏域和修改请求地址。
代码优化二:使用反射优化大量 else if ,抽取 BaseServlet 程序。

package com.atanhui.web;import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;public abstract class BaseServlet extends HttpServlet {protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {req.setCharacterEncoding("utf-8");resp.setContentType("text/html;charset=utf-8");String action = req.getParameter("action");try {Method method = this.getClass().getDeclaredMethod(action, HttpServletRequest.class, HttpServletResponse.class);method.invoke(this, req, resp);} catch (Exception e) {e.printStackTrace();throw new RuntimeException(e);      // 吧异常抛给FIlter}}
}

修改 UserServlet 程序继承 BaseServlet 程序。

五、数据的封装和抽取 BeanUtils 的使用

BeanUtils 工具类,它可以一次性的把所有请求的参数注入到 JavaBean 中。
BeanUtils 工具类,经常用于把 Map 中的值注入到 JavaBean 中,或者是对象属性值的拷贝操作。
BeanUtils 它不是 Jdk 的类。而是第三方的工具类。所以需要导包。
1、导入需要的 jar 包:
commons-beanutils-1.8.0.jar
commons-logging-1.1.1.jar
2、编写 WebUtils 工具类使用:

package com.atanhui.utils;import org.apache.commons.beanutils.BeanUtils;import java.util.Map;public class WebUtils {public static <T> T copyParamToBean(Map value, T bean) {try {System.out.println("注入之前"+bean);// 把所有请求req的参数注入到JavaBean属性中BeanUtils.populate(bean, value);System.out.println("注入之后"+bean);} catch (Exception e) {e.printStackTrace();}return bean;}// 将字符串转化成为int类型的数据public static int parseInt(String strInt, int defaultValve) {try {return Integer.parseInt(strInt);} catch (NumberFormatException e) {//            e.printStackTrace();}return defaultValve;}public static void parseInt(String status) {}
}

六、使用 EL 表达式修改表单回显

<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>安徽大学书城会员登录页面</title><%-- 静态包含 base标签、css样式、jQuery文件 --%><%@include file="/pages/common/head.jsp" %>
</head>
<body><div id="login_header"><img class="logo_img" alt="" src="static/img/logo.gif" ></div><div class="login_banner"><div id="l_content"><span class="login_word">欢迎登录</span></div><div id="content"><div class="login_form"><div class="login_box"><div class="tit"><h1>安徽大学书城会员</h1><a href="pages/user/regist.jsp">立即注册</a></div><div class="msg_cont"><b></b><span class="errorMsg">${ requestScope.msg ? "请输入用户名和密码" : requestScope.msg }</span></div><div class="form"><form action="clientServlet" method="post"><input type="hidden" name="action" value="login"><label>用户名称:</label><input class="itxt" type="text" placeholder="请输入用户名" autocomplete="off" tabindex="1" name="username" value="${ requestScope.username }"  /><br /><br /><label>用户密码:</label><input class="itxt" type="password" placeholder="请输入密码" autocomplete="off" tabindex="1" name="password" /><br /><br /><input type="submit" value="登录" id="sub_btn" /></form></div>  </div></div></div></div><%--静态包含页脚内容--%><%@include file="/pages/common/footer.jsp" %>
</body>
</html>

书城项目第五阶段:图书模块

一、MVC 概念

MVC 全称:Model 模型、 View 视图、 Controller 控制器。
MVC 最早出现在 JavaEE 三层中的 Web 层,它可以有效的指导 Web 层的代码如何有效分离,单独工作。
Model 模型:将与业务逻辑相关的数据封装为具体的 JavaBean 类,其中不掺杂任何与数据处理相关的代码 — JavaBean/domain/entity/pojo。
View 视图:只负责数据和界面的显示,不接受任何与显示数据无关的代码,便于程序员和美工的分工合作 — JSP/HTML。
Controller 控制器:只负责接收请求,调用业务层的代码处理请求,然后派发页面,是一个“调度者”的角色 —Servlet,转到某个页面,或者是重定向到某个页面。

二、图书模块

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');

2、编写图书模块的 JavaBean

package com.atanhui.pojo;import java.math.BigDecimal;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";@Overridepublic String toString() {return "Book{" +"id=" + id +", name='" + name + '\'' +", author='" + author + '\'' +", price=" + price +", sales=" + sales +", stock=" + stock +", 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;}}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 Book() {}
}

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

package com.atanhui.dao;import com.atanhui.pojo.Book;import java.util.List;public interface BookDAO {public int addBook(Book book);public int deleteBookById(Integer id);public int updateBook(Book book);public Book queryBookById(Integer id);public List<Book> queryBooks();Integer queryForPageTotalCount();List<Book> queryForPageItems(int begin, int pageSize);Integer queryForPageTotalCountByPrice(int min, int max);List<Book> queryForPageItemsByPrice(int begin, int pageSize, int min, int max);
}
package com.atanhui.dao.impl;import com.atanhui.dao.BookDAO;
import com.atanhui.pojo.Book;import java.util.List;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) {System.out.println("bookDAO程序在【" + Thread.currentThread().getName() + "】线程中");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);}@Overridepublic Integer queryForPageTotalCount() {String sql = "select count(*) from t_book ";Number count = (Number)queryForSingleValue(sql);return count.intValue();}@Overridepublic List<Book> queryForPageItems(int 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);}@Overridepublic Integer queryForPageTotalCountByPrice(int min, int max) {String sql = "select count(*) from t_book where price between ? and ? ";Number count = (Number)queryForSingleValue(sql, min, max);return count.intValue();}@Overridepublic List<Book> queryForPageItemsByPrice(int 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 limit ? , ?";return queryForList(Book.class, sql, min, max, begin, pageSize);}
}
package com.atanhui.dao;import com.atanhui.dao.impl.BookDAOImpl;
import com.atanhui.pojo.Book;
import org.junit.Test;import java.math.BigDecimal;public class BookDAOTest {private BookDAO bookDAO = new BookDAOImpl();@Testpublic void addBook() {bookDAO.addBook(new Book(null, "十万个为什么?", "张总", new BigDecimal(999), 100, 2, null));}@Testpublic void deleteBookById() {bookDAO.deleteBookById(20);}@Testpublic void updateBook() {bookDAO.updateBook(new Book(21, "颜氏菜谱", "颜仔", new BigDecimal(999), 100, 5, null));}@Testpublic void queryBookById() {System.out.println(bookDAO.queryBookById(21));}@Testpublic void queryBooks() {System.out.println(bookDAO.queryBooks());}@Testpublic void queryForPageTotalCount() {System.out.println(bookDAO.queryForPageTotalCount());}@Testpublic void queryForPageItems() {bookDAO.queryForPageItems(8, 4).forEach(System.out::println);}@Testpublic void queryForPageTotalCountByPrice() {System.out.println(bookDAO.queryForPageTotalCountByPrice(10, 50));}@Testpublic void queryForPageItemsByPrice() {bookDAO.queryForPageItemsByPrice(1, 4, 10, 50).forEach(System.out::println);}
}

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

package com.atanhui.service;import com.atanhui.pojo.Book;
import com.atanhui.pojo.Page;import java.util.List;public interface BookService {public void addBook(Book book);public void deleteBookById(Integer id);public void updateBook(Book book);public Book queryBookById(Integer id);public List<Book> queryBooks();Page<Book> page(int pageNo, int pageSize);Page<Book> pageByPrice(int pageNo, int pageSize, int min, int max);
}
package com.atanhui.service.impl;import com.atanhui.dao.BookDAO;
import com.atanhui.dao.impl.BookDAOImpl;
import com.atanhui.pojo.Book;
import com.atanhui.pojo.Page;
import com.atanhui.service.BookService;import java.util.List;public class BookServiceImpl implements BookService {// service 依赖于 daoprivate BookDAO bookDAO = new BookDAOImpl();@Overridepublic void addBook(Book book) {bookDAO.addBook(book);}@Overridepublic void deleteBookById(Integer id) {bookDAO.deleteBookById(id);}@Overridepublic void updateBook(Book book) {bookDAO.updateBook(book);}@Overridepublic Book queryBookById(Integer id) {return bookDAO.queryBookById(id);}@Overridepublic List<Book> queryBooks() {return bookDAO.queryBooks();}@Overridepublic Page<Book> page(int pageNo, int pageSize) {Page<Book> page = new Page<Book>();page.setPageSize(pageSize);// 求总记录数Integer pageTotalCount = bookDAO.queryForPageTotalCount();page.setPageTotalCount(pageTotalCount);// 求总页码int pageTotal = pageTotalCount / pageSize;if (pageTotalCount%pageSize > 0){pageTotal++;}page.setPageTotal(pageTotal);// 设置 当前页码,页面大小page.setPageNo(pageNo);// 当前页面数据int begin = (pageNo-1)*page.getPageSize();List<Book> items = bookDAO.queryForPageItems(begin, pageSize);page.setItems(items);return page;}@Overridepublic Page<Book> pageByPrice(int pageNo, int pageSize, int min, int max) {Page<Book> page = new Page<Book>();page.setPageSize(pageSize);// 求总记录数Integer pageTotalCount = bookDAO.queryForPageTotalCountByPrice(min, max);page.setPageTotalCount(pageTotalCount);// 求总页码int pageTotal = pageTotalCount / pageSize;if (pageTotalCount%pageSize > 0){pageTotal++;}page.setPageTotal(pageTotal);// 设置 当前页码,页面大小page.setPageNo(pageNo);// 当前页面数据int begin = (pageNo-1)*page.getPageSize();List<Book> items = bookDAO.queryForPageItemsByPrice(begin, pageSize, min, max);page.setItems(items);return page;}
}
package com.atanhui.service;import com.atanhui.pojo.Book;
import com.atanhui.pojo.Page;
import com.atanhui.service.impl.BookServiceImpl;
import org.junit.Test;import java.math.BigDecimal;public class BookServiceTest {private BookService bookService = new BookServiceImpl();@Testpublic void addBook() {bookService.addBook(new Book(null, "脑壳手术大揭秘", "六六", new BigDecimal(88), 60, 66, null));}@Testpublic void deleteBookById() {bookService.deleteBookById(4);}@Testpublic void updateBoot() {bookService.updateBook(new Book(22, "脑壳手术大揭秘", "六六san", new BigDecimal(88), 60, 66, null));}@Testpublic void queryBookById() {System.out.println(bookService.queryBookById(22));}@Testpublic void queryBooks() {bookService.queryBooks().forEach(System.out::println);}@Testpublic void page() {System.out.println(bookService.page(1, Page.PAGE_SIZE));}@Testpublic void pageByPrice() {System.out.println(bookService.pageByPrice(1, Page.PAGE_SIZE, 10, 50));}
}

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

1)BookServlet 程序中添加 list 方法

protected void list(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 通过BookService查询全部图书List<Book> books = bookService.queryBooks();// 把全部图书保存到request域中req.setAttribute("books", books);// 请求转发到/pages/manager/book_manager.jsp页面req.getRequestDispatcher("/pages/manager/book_manager.jsp").forward(req, resp);
}

2)修改 图书管理 请求地址
3)修改 pages/manager/book_manager.jsp 页面的数据遍历输出

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>图书管理</title><%-- 静态包含 base标签、css样式、jQuery文件 --%><%@include file="/pages/common/head.jsp" %><script type="text/javascript">$(function () {// 给删除的a标签绑定单击事件,用于删除的确认提示操作$("a.deleteClass").click(function () {// 在事件的function函数中有一个this对象,是当前正在响应事件的dom对象var tf = $(this).parent().parent().find("td:first").text();// confirm 确认提示框函数,参数是它的提示内容,他有两个按钮,一个确认,一个取消// 返回true,表示点击确认,返回false表示点击取消return confirm("你确定要删除【"+ tf +"】?");// return false; // 阻止元素的默认行为,不提交请求});// 给提交按钮绑定单击事件,跳到指定的页码$("#searchPageBtn").click(function () {var pageNo = $("#pn_input").val();// JavaScript提供了一个atanhui地址栏对象// 它有一个属性叫做href,它可以获取浏览器地址栏中的地址// href 可读 可写 可赋值if (pageNo <= ${ requestScope.page.pageTotal} && pageNo >= 1) {location.href = "${pageScope.basePath}${requestScope.page.url}&pageNo="+pageNo;} else {alert("请输入正确页码!");}// alert(location.href)});});</script>
</head>
<body><div id="header"><img class="logo_img" alt="" src="static/img/logo.gif" ><span class="wel_word">图书管理系统</span><%--静态包含manager管理模块的菜单--%><%@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}&pageNo=${requestScope.page.pageNo}">修改</a></td><td><a class="deleteClass" href="manager/bookServlet?action=delete&id=${book.id}&pageNo=${requestScope.page.pageNo}">删除</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?pageTotal=${requestScope.page.pageTotal}">添加图书</a></td></tr>    </table><%@include file="/pages/common/page_nav.jsp"%></div><%--静态包含页脚内容--%><%@include file="/pages/common/footer.jsp" %>
</body>
</html>

3)添加图书功能的实现
BookServlet 程序中添加 add 方法

protected void add(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {int pageNo = WebUtils.parseInt(req.getParameter("pageTotal"), 0);Book book = WebUtils.copyParamToBean(req.getParameterMap(), new Book());bookService.addBook(book);// 重定向resp.sendRedirect(req.getContextPath()+"/manager/bookServlet?action=page&pageNo="+pageNo);
}

修改 book_edit.jsp 页面

<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>编辑图书</title><%-- 静态包含 base标签、css样式、jQuery文件 --%><%@include file="/pages/common/head.jsp" %><style type="text/css">h1 {text-align: center;margin-top: 200px;}h1 a {color:red;}input {text-align: center;}</style>
</head>
<body><div id="header"><img class="logo_img" alt="" src="static/img/logo.gif" ><span class="wel_word">编辑图书</span><%--静态包含manager管理模块的菜单--%><%@include file="/pages/common/manager_menu.jsp" %></div><div id="main"><form action="manager/bookServlet" method="post"><input type="hidden" name="pageNo" value="${ param.pageNo }"><input type="hidden" name="pageTotal" value="${ param.pageTotal }"><input type="hidden" name="action" value="${ empty param.id ? "add" : "update" }"><input type="hidden" name="id" value="${ requestScope.book.id }"><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><%--静态包含页脚内容--%><%@include file="/pages/common/footer.jsp" %>
</body>
</html>

4)删除图书功能的实现
BookServlet 程序中的 delete 方法

protected void delete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 获取请求参数String id = (String) req.getParameter("id");int i = WebUtils.parseInt(id, 0);// 调用bookService.deleteBookById()bookService.deleteBookById(i);// 重定向回图书列表管理页面resp.sendRedirect(req.getContextPath()+"/manager/bookServlet?action=page&pageNo="+req.getParameter("pageNo"));
}

给 WebUtils 工具类添加转换 int 类型的工具方法
修改删除的连接地址,给删除添加确认提示操作
5)修改图书功能的实现
更新 修改 的请求地址
BookServlet 程序中添加 getBook 方法

protected void getBook(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 获取请求的参数,图书编号String id = req.getParameter("id");int i = WebUtils.parseInt(id, 0);// 调用bookService.queryBookById查询图书Book book = bookService.queryBookById(i);// 保存图书到request域中req.setAttribute("book", book);// 请求转发到 pages/manager/book_edit.jsp页面req.getRequestDispatcher("/pages/manager/book_edit.jsp").forward(req, resp);
}

在 book_edit.jsp 页面中显示修改的数据
在 BookServlet 程序中添加 update 方法:

protected void update(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 获取请求的参数=对象成为Book对象Book book = WebUtils.copyParamToBean(req.getParameterMap(), new Book());// 调用BookService.updateBook(),修改图书bookService.updateBook(book);// 重定向回图书列表管理页面// 地址: /工程名/manager/bookService?action=listresp.sendRedirect(req.getContextPath()+"/manager/bookServlet?action=page&pageNo="+req.getParameter("pageNo"));
}

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

<input type="hidden" name="pageNo" value="${ param.pageNo }">
<input type="hidden" name="pageTotal" value="${ param.pageTotal }">
<input type="hidden" name="action" value="${ empty param.id ? "add" : "update" }">
<input type="hidden" name="id" value="${ requestScope.book.id }">

书城项目第六阶段:图书分页

一、图书分页

1)分页模型 Page 的抽取(当前页数,总页数,总记录数, 当前页数据,每页记录数)

package com.atanhui.pojo;import java.util.List;// page是分页的模型对象,<T>是具体的模块的javaBean类
public class Page<T> {public static final Integer PAGE_SIZE = 4;private String url;// 当前页码private Integer pageNo;// 总页码private Integer pageTotal;// 页面大小private Integer pageSize = PAGE_SIZE;// 总记录数private Integer pageTotalCount;// 当前页面数据private List<T> items;@Overridepublic String toString() {return "Page{" +"url='" + url + '\'' +", pageNo=" + pageNo +", pageTotal=" + pageTotal +", pageSize=" + pageSize +", pageTotalCount=" + pageTotalCount +", items=" + items +'}';}public Integer getPageTotal() {return pageTotal;}public void setPageTotal(Integer pageTotal) {this.pageTotal = pageTotal;}public Integer getPageSize() {return pageSize;}public void setPageSize(Integer pageSize) {this.pageSize = pageSize;}public Integer getPageTotalCount() {return pageTotalCount;}public void setPageTotalCount(Integer pageTotalCount) {this.pageTotalCount = pageTotalCount;}public List<T> getItems() {return items;}public void setItems(List<T> items) {this.items = items;}public Integer getPageNo() {return pageNo;}public void setPageNo(Integer pageNo) {// url 数据边界的有效检查if (pageNo < 1) {pageNo = 1;}if (pageNo > pageTotal) {pageNo = pageTotal;}this.pageNo = pageNo;}public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}public Page(String url, Integer pageNo, Integer pageTotal, Integer pageSize, Integer pageTotalCount, List<T> items) {this.url = url;this.pageNo = pageNo;this.pageTotal = pageTotal;this.pageSize = pageSize;this.pageTotalCount = pageTotalCount;this.items = items;}public Page() {}
}

2)分页的初步实现

@Override
public Integer queryForPageTotalCount() {String sql = "select count(*) from t_book ";Number count = (Number)queryForSingleValue(sql);return count.intValue();
}@Override
public List<Book> queryForPageItems(int 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);
}
@Override
public Page<Book> page(int pageNo, int pageSize) {Page<Book> page = new Page<Book>();page.setPageSize(pageSize);// 求总记录数Integer pageTotalCount = bookDAO.queryForPageTotalCount();page.setPageTotalCount(pageTotalCount);// 求总页码int pageTotal = pageTotalCount / pageSize;if (pageTotalCount%pageSize > 0){pageTotal++;}page.setPageTotal(pageTotal);// 设置 当前页码,页面大小page.setPageNo(pageNo);// 当前页面数据int begin = (pageNo-1)*page.getPageSize();List<Book> items = bookDAO.queryForPageItems(begin, pageSize);page.setItems(items);return page;
}
// 处理分页功能
protected void page(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 1 获取请求的参数int pageNo = WebUtils.parseInt(req.getParameter("pageNo"), 1);int pageSize = WebUtils.parseInt(req.getParameter("pageSize"), Page.PAGE_SIZE);// 2 调用BookService.page(pageNo, pageSize)返回page对象Page<Book> page = bookService.page(pageNo, pageSize);page.setUrl("manager/bookServlet?action=page");// 3 保存page对象到request域中req.setAttribute("page", page);// 4 请求转发到page/manager/book_manager.jsp页面req.getRequestDispatcher("/pages/manager/book_manager.jsp").forward(req, resp);
}

manager_menu.jsp 中 图书管理 请求地址的修改
book_manager.jsp 修改
3)首页、上一页、下一页、末页实现

<%@ 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><c:set var="end" value="${requestScope.page.pageNo}"></c:set></c:when><%-- 情况2,如果总页码大于5 --%><c:when test="${requestScope.page.pageTotal > 5}"><c:choose><%-- 当前页码为前面3个,1,2,3的情况,页码范围:1-5 --%><c:when test="${requestScope.page.pageNo <= 3}"><c:set var="begin" value="1"></c:set><c:set var="end" value="5"></c:set></c:when><%-- 当前页码为最后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><c:set var="end" value="${requestScope.page.pageTotal}"></c:set></c:when><%-- 4,5,6,页码范围:当前页码减2,当前页码加2 --%><c:otherwise><c:set var="begin" value="${requestScope.page.pageNo-2}"></c:set><c:set var="end" value="${requestScope.page.pageNo+2}"></c:set></c:otherwise></c:choose></c:when></c:choose><c:forEach begin="${begin}" end="${end}" step="1" var="i"><c:if test="${i == requestScope.page.pageNo}">【${i}】</c:if><c:if test="${i != requestScope.page.pageNo}"><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 type="button" value="确定" id="searchPageBtn">
</div>

4)分页模块中跳转到指定页数功能实现
Page 对象中的修改,BookService 中 page 方法的修改
5)分页模块中,页码 1,2,【3】,4,5 的显示,要显示 5 个页 码,并且页码可以点击跳转。
6)修改分页后,增加,删除,修改图书信息的回显页面

二、首页 index.jsp 的跳转

三、分页条的抽取

1)抽取分页条中请求地址为 url 变量
2)在 page 对象中添加 url 属性
3)在 Servlet 程序的 page 分页方法中设置 url 的分页请求地址
4)修改分页条中请求地址为 url 变量输出,并抽取一个单独的 jsp 页面

四、首页价格搜索

书城项目第七阶段

一、登陆,显示用户名

UserServlet 程序中保存用户登录的信息
修改 login_succuess_menu.jsp
还要修改首页 index.jsp 页面的菜单

二、登出,注销用户

1、销毁 Session 中用户登录的信息(或者销毁 Session)
2、重定向到首页(或登录页面)
UserServlet 程序中添加 logout 方法
修改 注销 的菜单地址

三、表单重复提交之-----验证码

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

四、谷歌 kaptcha 图片验证码的使用

1、导入谷歌验证码的 jar 包
kaptcha-2.3.2.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 标签去显示验证码图片并使用它

<div class="form"><form action="clientServlet" method="post"><input type="hidden" name="action" value="regist"><label>用户名称:</label><input class="itxt" type="text" placeholder="请输入用户名" autocomplete="off" tabindex="1" name="username" id="username" value="${ requestScope.username }" /><br /><br /><label>用户密码:</label><input class="itxt" type="password" placeholder="请输入密码" autocomplete="off" tabindex="1" name="password" id="password" /><br /><br /><label>确认密码:</label><input class="itxt" type="password" placeholder="确认密码" autocomplete="off" tabindex="1" name="repwd" id="repwd" /><br /><br /><label>电子邮件:</label><input class="itxt" type="text" placeholder="请输入邮箱地址" autocomplete="off" tabindex="1" name="email" id="email" value="${ requestScope.email }" /><br /><br /><label>验证码:</label><input class="itxt" type="text" name="code" style="width: 100px;" id="code"/><img alt="" src="kaptcha.jpg" id="code_img" style="float: right; margin-right: 40px" width="130px" height="40px"><br /><br /><input type="submit" value="注册" id="sub_btn" /><br /><br /></form>
</div>

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

// 处理注册功能protected void regist(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String token = (String) req.getSession().getAttribute(KAPTCHA_SESSION_KEY);req.getSession().removeAttribute(KAPTCHA_SESSION_KEY);//  获取请求的参数String username = req.getParameter("username");String password = req.getParameter("password");String email = req.getParameter("email");String code = req.getParameter("code");User user = WebUtils.copyParamToBean(req.getParameterMap(), new User());// 检查 证明码是否正确  写死,要求验证码为abcdeif (token != null && token.equals(code)) {// 验证码正确,检查用户名是否可用if (userService.existsUsername(username)) {req.setAttribute("username", username);req.setAttribute("email", email);// 用户名不可用,跳回注册页面req.setAttribute("msg", "用户名已存在!");req.getRequestDispatcher("/pages/user/regist.jsp").forward(req, resp);} else {// 用户名可用,调用service,保存数据库,跳到register_success.htmluserService.registUser(new User(null, username, password, email, 0));req.getRequestDispatcher("/pages/user/regist_success.jsp").forward(req, resp);}} else {req.setAttribute("username", username);req.setAttribute("email", email);// 验证码不正确,跳回注册页面req.setAttribute("msg", "验证码错误!");req.getRequestDispatcher("/pages/user/regist.jsp").forward(req, resp);}}

切换验证码:

// 给验证码图片绑定单击事件
$("#code_img").click(function () {// 在事件响应的function函数中有一个this对象,这个this对象是当前正在响应事件的dom对象// src 属性 验证码img标签的图片路径,可读可写// 加入时间戳 每次获取不同的验证码this.src = "${basePath}kaptcha.jpg?d=" + new Date();
});

书城项目第八阶段:购物车模块

一、购物车模块分析

二、购物车模型编写

1、购物车模型

package com.atanhui.pojo;import java.math.BigDecimal;
import java.util.*;public class Cart {// key 商品编号 value 商品信息// map集合private Map<Integer, CartItem> items = new LinkedHashMap<Integer, CartItem>();// 添加商品项public void addItem(CartItem cartItem) {// 查看购物车中是否已经添加过此商品,如果已经添加,则数量更新,如果没有添加过,直接加入购物车即可CartItem cartItem1 = items.get(cartItem.getId());if (cartItem1 == null) {// 之前没有添加此商品items.put(cartItem.getId(), cartItem);} else {// 已经添加过此商品cartItem1.setCount(cartItem1.getCount() + 1);   // 数量累加cartItem1.setTotalPrice(cartItem1.getTotalPrice().add(cartItem1.getPrice()));   // 更新总金额}}// 删除商品项public void deleteItem(Integer id) {items.remove(id);}// 清空购物车public void clear() {items.clear();}// 修改商品数量public void updateCount(Integer id, Integer count) {// 先查看购物车中是否有此商品CartItem cartItem = items.get(id);if (cartItem != null) {// 如果商品存在,更新商品数量cartItem.setCount(count);cartItem.setTotalPrice(cartItem.getPrice().multiply(new BigDecimal(count)));}}@Overridepublic String toString() {return "Cart{" +"totalCount=" + getTotalCount() +", totalPrice=" + getTotalPrice() +", items=" + items +'}';}public Integer getTotalCount() {Integer totalCount = 0;// 方案一 遍历map集合 键值对 <key, value>for (Map.Entry<Integer, CartItem> entry : items.entrySet()) {CartItem cartItem = entry.getValue();totalCount = cartItem.getCount() + totalCount;}return totalCount;}//    public void setTotalCount(Integer totalCount) {//        this.totalCount = totalCount;
//    }public BigDecimal getTotalPrice() {BigDecimal totalPrice = new BigDecimal(0);// 方案二 遍历map集合的valuefor (CartItem cartItem : items.values()) {totalPrice = cartItem.getTotalPrice().add(totalPrice);}return totalPrice;}//    public void setTotalPrice(BigDecimal totalPrice) {//        this.totalPrice = totalPrice;
//    }public Map<Integer, CartItem> getItems() {return items;}public void setItems(Map<Integer, CartItem> items) {this.items = items;}public Cart(Map<Integer, CartItem> items) {this.items = items;}public Cart() {}
}

2、购物车的测试

package com.atanhui.pojo;import org.junit.Test;import java.math.BigDecimal;public class CartTest {@Testpublic void addItem() {Cart cart = new Cart();cart.addItem(new CartItem(1, "c+", 1, new BigDecimal(800), new BigDecimal(800)));cart.addItem(new CartItem(1, "c+", 1, new BigDecimal(800), new BigDecimal(800)));cart.addItem(new CartItem(2, "java", 1, new BigDecimal(1000), new BigDecimal(1000)));System.out.println(cart);}@Testpublic void deleteItem() {Cart cart = new Cart();cart.addItem(new CartItem(1, "c+", 1, new BigDecimal(800), new BigDecimal(800)));cart.addItem(new CartItem(1, "c+", 1, new BigDecimal(800), new BigDecimal(800)));cart.addItem(new CartItem(2, "java", 1, new BigDecimal(1000), new BigDecimal(1000)));cart.deleteItem(1);System.out.println(cart);}@Testpublic void clear() {Cart cart = new Cart();cart.addItem(new CartItem(1, "c+", 1, new BigDecimal(800), new BigDecimal(800)));cart.addItem(new CartItem(1, "c+", 1, new BigDecimal(800), new BigDecimal(800)));cart.addItem(new CartItem(2, "java", 1, new BigDecimal(1000), new BigDecimal(1000)));cart.clear();System.out.println(cart);}@Testpublic void updateCount() {Cart cart = new Cart();cart.addItem(new CartItem(1, "c+", 1, new BigDecimal(800), new BigDecimal(800)));cart.addItem(new CartItem(1, "c+", 1, new BigDecimal(800), new BigDecimal(800)));cart.addItem(new CartItem(2, "java", 1, new BigDecimal(1000), new BigDecimal(1000)));cart.updateCount(1, 10);System.out.println(cart);}
}

三、加入购物车功能的实现

CartServlet 程序

// 加入购物车
protected void addItem(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 获取请求的参数,商品编号int id = WebUtils.parseInt(req.getParameter("id"), 0);// 调用bookService.queryBookById(id) 得到图书的信息Book book = bookService.queryBookById(id);// 把图书信息转化成为cartItem商品项CartItem cartItem = new CartItem(book.getId(), book.getName(), 1, book.getPrice(), book.getPrice());// 调用Cart.addItem(cartItem),添加商品项Cart cart = (Cart) req.getSession().getAttribute("cart");if (cart == null) {cart = new Cart();req.getSession().setAttribute("cart", cart);}cart.addItem(cartItem);// 最后一个添加的商品名称 添加到session域中req.getSession().setAttribute("lastName", cartItem.getName());// 重定向回到原来的商品列表页面// Referer 请求头Referer// 重定向 不支持request域 数据共享resp.sendRedirect(req.getHeader("Referer"));
}

index.jsp 页面 js 的代码

四、购物车的展示

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>购物车</title><%-- 静态包含 base标签、css样式、jQuery文件 --%><%@include file="/pages/common/head.jsp" %><script type="text/javascript">$(function () {// 给删除购物车按钮绑定单击事件$("a.deleteItem").click(function () {return confirm("你确定要删除【"+ $(this).parent().parent().find("td:first").text() +"】吗?");});$("a.clearCart").click(function () {return confirm("你确定要清空购物车吗?");});// 给输入框绑定 失去焦点事件  ==    onchang事件 内容改变事件$(".updateCount").change(function () {// 获取商品名称var name = $(this).parent().parent().find("td:first").text();// 获取商品数量var count = this.value;if (confirm("你确定要将【" + name + "】商品修改数量修改为:" + count + "吗?")){// js对象转化JQuery对象var id = $(this).attr('boodId')// 发起请求,给服务器保存修改location.href = "${basePath}cartServlet?action=updateCount&count="+ count + "&id=" + id;} else {// defaultValue属性 表单项DOM对象的属性,它表示默认value属性值this.values = this.defaultValue;}});});</script>
</head>
<body><div id="header"><img class="logo_img" alt="" src="static/img/logo.gif" ><span class="wel_word">购物车</span><%-- 静态包含登录成功之后的菜单  --%><%@ include file="/pages/common/login_success_menu.jsp" %></div><div id="main"><table><tr><td>商品名称</td><td>数量</td><td>单价</td><td>金额</td><td>操作</td></tr><c:if test="${not empty sessionScope.cart.items}"><c:forEach items="${sessionScope.cart.items}" var="cartItem"><tr><td>${cartItem.value.name}</td><td><input type="text" class="updateCount" name="count" id="count" boodId="${cartItem.key}" style="text-align: center; width: 80px" value="${cartItem.value.count}"></td><td>${cartItem.value.price}</td><td>${cartItem.value.totalPrice}</td><td><a class="deleteItem" href="cartServlet?action=deleteItem&id=${cartItem.key}">删除</a></td></tr></c:forEach></c:if><c:if test="${empty sessionScope.cart.items}"><tr><td colspan="5"><a href="index.jsp">亲,当前购物车为空!快跟小伙伴们去浏览商品吧!!!</a></td></tr></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 class="clearCart" href="cartServlet?action=clear">清空购物车</a></span><span class="cart_span"><a href="manager/orderServlet?action=createOrder">去结账</a></span></div></c:if></div><%--静态包含页脚内容--%><%@include file="/pages/common/footer.jsp" %>
</body>
</html>

五、删除购物车商品项

CartServlet 程序

// 删除商品项
protected void deleteItem(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 获取商品编号int id = WebUtils.parseInt(req.getParameter("id"), 0);Cart cart = (Cart) req.getSession().getAttribute("cart");if (cart != null) {cart.deleteItem(id);}// 重定向为购物车原来展示页面resp.sendRedirect(req.getHeader("Referer"));
}

删除的请求地址
删除的确认提示操作

六、清空购物车

CartServlet 程序

// 清空购物车
protected void clear(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 获取购物车对象Cart cart = (Cart)req.getSession().getAttribute("cart");// 判断购物车是否为空?if (cart != null) {cart.clear();resp.sendRedirect(req.getHeader("Referer"));}
}

cart.jsp 页面的内容,给清空购物车添加请求地址,和添加 id 属性
清空的确认提示操作

七、修改购物车商品数量

CartServlet 程序

// 修海商品数量
protected void updateCount(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 获取商品编号,商品数量int id = WebUtils.parseInt(req.getParameter("id"), 0);int count = WebUtils.parseInt(req.getParameter("count"), 0);// 获取Cart购物车对象Cart cart = (Cart) req.getSession().getAttribute("cart");// 判断购物车是否为空if (cart != null){// 更新购物车cart.updateCount(id, count);resp.sendRedirect(req.getHeader("Referer"));}
}

修改 pages/cart/cart.jsp 购物车页面
修改商品数量 js 代码

八、首页,购物车数据回显

在添加商品到购物车的时候,保存最后一个添加的商品名称:
在 pages/client/index.jsp 页面中输出购物车信息:

书城项目第七阶段:订单模块

一、订单模块的分析

二、订单模块的实现

1、创建订单模块的数据库表

use book;
create table t_order( `order_id` varchar(50) primary key, `create_time` datetime, `price` decimal(11,2), `status` int, `user_id` int, foreign key(`user_id`) references t_user(`id`)
);
create table t_order_item( `id` int primary key auto_increment, `name` varchar(100), `count` int, `price` decimal(11,2), `total_price` decimal(11,2),`order_id` varchar(50), foreign key(`order_id`) references t_order(`order_id`)
);

2、创建订单模块的数据模型

package com.atanhui.pojo;import java.math.BigDecimal;
import java.util.Date;// 订单
public class Order {private String orderId;private Date createTime;// 0 表示未发货;1 表示已发货;2 表示已签收private BigDecimal price;private Integer status = 0;private Integer userId;public Order() {}@Overridepublic String toString() {return "Order{" +"orderId='" + orderId + '\'' +", createTime=" + createTime +", price=" + price +", status=" + status +", userId=" + userId +'}';}public String getOrderId() {return orderId;}public void setOrderId(String orderId) {this.orderId = orderId;}public Date getCreateTime() {return createTime;}public void setCreateTime(Date createTime) {this.createTime = createTime;}public BigDecimal getPrice() {return price;}public void setPrice(BigDecimal price) {this.price = price;}public Integer getStatus() {return status;}public void setStatus(Integer status) {this.status = status;}public Integer getUserId() {return userId;}public void setUserId(Integer userId) {this.userId = userId;}public Order(String orderId, Date createTime, BigDecimal price, Integer status, Integer userId) {this.orderId = orderId;this.createTime = createTime;this.price = price;this.status = status;this.userId = userId;}
}
package com.atanhui.pojo;import java.math.BigDecimal;// 订单项
public class OrderItem {private Integer id;private String name;private Integer count;private BigDecimal price;private BigDecimal totalPrice;private String orderId;@Overridepublic String toString() {return "OrderItem{" +"id=" + id +", name='" + name + '\'' +", count=" + count +", price=" + price +", totalPrice=" + totalPrice +", orderId='" + orderId + '\'' +'}';}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getCount() {return count;}public void setCount(Integer count) {this.count = count;}public BigDecimal getPrice() {return price;}public void setPrice(BigDecimal price) {this.price = price;}public BigDecimal getTotalPrice() {return totalPrice;}public void setTotalPrice(BigDecimal totalPrice) {this.totalPrice = totalPrice;}public String getOrderId() {return orderId;}public void setOrderId(String orderId) {this.orderId = orderId;}public OrderItem(Integer id, String name, Integer count, BigDecimal price, BigDecimal total_price, String orderId) {this.id = id;this.name = name;this.count = count;this.price = price;this.totalPrice = totalPrice;this.orderId = orderId;}public OrderItem() {}
}

3、编写订单模块的 Dao 程序和测试
OrderDao 接口

package com.atanhui.dao;import com.atanhui.pojo.Order;import java.util.List;public interface OrderDAO {// 保存订单public int saveOrder(Order order);// 查询全部订单public List<Order> queryOrder();// 修改订单状态public int changeOrderStatus(String orderId, Integer status);// 根据用户编号查询订单信息public List<Order> queryOrderByUserId(Integer userId);
}

OrderDao 实现

package com.atanhui.dao.impl;import com.atanhui.dao.OrderDAO;
import com.atanhui.pojo.Order;import java.util.List;public class OrderDAOImpl extends BaseDAO implements OrderDAO {@Overridepublic int saveOrder(Order order) {System.out.println("orderDAO程序在【" + Thread.currentThread().getName() + "】线程中");String sql = "insert into t_order(`order_id`, `create_time`, `price`, `status`, `user_id`) values(?, ?, ?, ?, ?)";int update = update(sql, order.getOrderId(), order.getCreateTime(), order.getPrice(), order.getStatus(), order.getUserId());return update;}@Overridepublic List<Order> queryOrder() {String sql = "select `order_id` orderId, `create_time` createTime, `price`, `status`, `user_id` userId from t_order";return queryForList(Order.class, sql);}@Overridepublic int changeOrderStatus(String orderId, Integer status) {String sql = "update t_order set `status`=? where `order_id`=?";return update(sql, status, orderId);}@Overridepublic List<Order> queryOrderByUserId(Integer userId) {String sql = "select `order_id` orderId, `create_time` createTime, `price`, `status`, `user_id` userId from t_order where `user_id`=?";return queryForList(Order.class, sql, userId);}
}

OrderItemDao 接口

package com.atanhui.dao;import com.atanhui.pojo.OrderItem;import java.util.List;// 订单项
public interface OrderItemDAO {// 保存订单项public int saveOrderItem(OrderItem orderItem);// 根据订单号查询订单明细public List<OrderItem> queryOrderItemByOrderId(String orderId);
}

OrderItemDao 实现

package com.atanhui.dao.impl;import com.atanhui.dao.OrderItemDAO;
import com.atanhui.pojo.OrderItem;import java.util.List;public class OrderItemDAOImpl extends BaseDAO implements OrderItemDAO {@Overridepublic int saveOrderItem(OrderItem orderItem) {System.out.println("orderItemDAO程序在【" + Thread.currentThread().getName() + "】线程中");String sql = "insert into t_order_item(`name`, `count`, `price`, `total_price`, `order_id`) values(?, ?, ?, ?, ?)";int update = update(sql, orderItem.getName(), orderItem.getCount(), orderItem.getPrice(), orderItem.getTotalPrice(), orderItem.getOrderId());return update;}@Overridepublic List<OrderItem> queryOrderItemByOrderId(String orderId) {String sql = "select `name`, `count`, `price`, `total_price` totalPrice, `order_id` orderId from t_order_item where order_id=?";return queryForList(OrderItem.class, sql, orderId);}
}

测试

package com.atanhui.dao.impl;import com.atanhui.dao.OrderDAO;
import com.atanhui.pojo.Order;
import org.junit.Test;import java.math.BigDecimal;
import java.util.Date;public class OrderDAOImplTest {OrderDAO orderDAO = new OrderDAOImpl();@Testpublic void saveOrder() {orderDAO.saveOrder(new Order("136664679", new Date(), new BigDecimal(100), 0, 1));}@Testpublic void queryOrder() {orderDAO.queryOrder().forEach(System.out::println);}@Testpublic void changeOrderStatus() {orderDAO.changeOrderStatus("136664679", 1);}@Testpublic void queryOrderByUserId() {orderDAO.queryOrderByUserId(1).forEach(System.out::println);}
}
package com.atanhui.dao.impl;import com.atanhui.dao.OrderItemDAO;
import com.atanhui.pojo.OrderItem;
import org.junit.Test;import java.math.BigDecimal;public class OrderItemDAOImplTest {OrderItemDAO orderItemDAO = new OrderItemDAOImpl();@Testpublic void saveOrderItem() {orderItemDAO.saveOrderItem(new OrderItem(null, "java从入门到精通", 1, new BigDecimal(100), new BigDecimal(100), "134679"));orderItemDAO.saveOrderItem(new OrderItem(null, "javaScript从入门到精通", 2, new BigDecimal(88), new BigDecimal(176), "134679"));}@Testpublic void queryOrderItemByOrderId() {orderItemDAO.queryOrderItemByOrderId("16463133618891").forEach(System.out::println);}
}

4、编写订单模块的 Service 和测试
OrderService 接口

package com.atanhui.service;import com.atanhui.pojo.Cart;
import com.atanhui.pojo.Order;
import com.atanhui.pojo.OrderItem;import java.util.List;public interface OrderService {// 生成订单public String createOrder(Cart cart, Integer id);// 查询全部订单public List<Order> showAllOrder();// 发货 修改订单状态为1public int sendOrder(String orderId);// 查看订单详情public List<OrderItem> showOrderDetail(String orderId);// 查看我的订单public List<Order> showMyOrders(Integer userId);// 签收订单/确认收货public int receiverOrder(String orderId);
}

OrderService 实现类

package com.atanhui.service.impl;import com.atanhui.dao.BookDAO;
import com.atanhui.dao.OrderDAO;
import com.atanhui.dao.OrderItemDAO;
import com.atanhui.dao.impl.BookDAOImpl;
import com.atanhui.dao.impl.OrderDAOImpl;
import com.atanhui.dao.impl.OrderItemDAOImpl;
import com.atanhui.pojo.*;
import com.atanhui.service.OrderService;import java.util.Date;
import java.util.List;public class OrderServiceImpl implements OrderService {private OrderDAO orderDAO = new OrderDAOImpl();private OrderItemDAO orderItemDAO = new OrderItemDAOImpl();private BookDAO bookDAO = new BookDAOImpl();@Overridepublic String createOrder(Cart cart, Integer id) {System.out.println("orderService程序在【" + Thread.currentThread().getName() + "】线程中");// 订单号唯一性String orderId = System.currentTimeMillis()+""+id;// 创建一个订单对象Order order = new Order(orderId, new Date(), cart.getTotalPrice(), 0, id);orderDAO.saveOrder(order);
//        int i = 15 / 0;// 遍历购物车中每一个商品项转换成订单项保存到数据库中for (CartItem value : cart.getItems().values()) {// 获取每一个购物车中的商品项 转换成每一个订单项OrderItem orderItem = new OrderItem(null, value.getName(), value.getCount(), value.getPrice(), value.getTotalPrice(), orderId);// 保存订单项到数据库orderItemDAO.saveOrderItem(orderItem);// 更新库存 销量Book book = bookDAO.queryBookById(value.getId());book.setSales(book.getSales()+value.getCount());book.setStock(book.getStock()-value.getCount());bookDAO.updateBook(book);}// 清空购物车cart.clear();return orderId;}@Overridepublic List<Order> showAllOrder() {return orderDAO.queryOrder();}@Overridepublic int sendOrder(String orderId) {return orderDAO.changeOrderStatus(orderId, 1);}@Overridepublic List<OrderItem> showOrderDetail(String orderId) {return orderItemDAO.queryOrderItemByOrderId(orderId);}@Overridepublic List<Order> showMyOrders(Integer userId) {return orderDAO.queryOrderByUserId(userId);}@Overridepublic int receiverOrder(String orderId) {return orderDAO.changeOrderStatus(orderId, 2);}
}

测试

package com.atanhui.service;import com.atanhui.pojo.Cart;
import com.atanhui.pojo.CartItem;
import com.atanhui.service.OrderService;
import org.junit.Test;import java.math.BigDecimal;public class OrderServiceTest {OrderService orderService = new OrderServiceImpl();@Testpublic void createOrder() {Cart cart = new Cart();cart.addItem(new CartItem(1, "c+", 1, new BigDecimal(800), new BigDecimal(800)));cart.addItem(new CartItem(2, "java", 1, new BigDecimal(1000), new BigDecimal(1000)));System.out.println(orderService.createOrder(cart, 1));}@Testpublic void showAllOrder() {orderService.showAllOrder().forEach(System.out::println);}@Testpublic void sendOrder() {System.out.println(orderService.sendOrder("16463173735151"));}@Testpublic void showOrderDetail() {orderService.showOrderDetail("16463173735151").forEach(System.out::println);}@Testpublic void showMyOrders() {orderService.showMyOrders(1).forEach(System.out::println);}@Testpublic void receiverOrder() {System.out.println(orderService.receiverOrder("16463173735151"));}
}

5、编写订单模块的 web 层和页面联调
修改 OrderService 程序
OrderServlet 程序

package com.atanhui.web;import com.atanhui.pojo.*;
import com.atanhui.service.OrderService;
import com.atanhui.service.UserService;
import com.atanhui.service.impl.OrderServiceImpl;
import com.atanhui.service.impl.UserServiceImpl;
import com.atanhui.utils.WebUtils;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.List;public class OrderServlet extends BaseServlet{@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {req.setCharacterEncoding("utf-8");resp.setContentType("text/html;charset=utf-8");super.doPost(req, resp);}OrderService orderService = new OrderServiceImpl();UserService userService = new UserServiceImpl();// 生成订单protected void createOrder(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 获取cart购物车对象HttpSession session = req.getSession();Cart cart = (Cart)session.getAttribute("cart");// 获取用户UseIdUser LoginUser = (User)session.getAttribute("user");if (LoginUser!=null) {System.out.println("orderServlet程序在【" + Thread.currentThread().getName() + "】线程中");String orderId = orderService.createOrder(cart, LoginUser.getId());//            req.setAttribute("orderId", orderId);// 请求转发/pages/cart/checkout.jsp
//            req.getRequestDispatcher("/pages/cart/checkout.jsp").forward(req, resp);session.setAttribute("orderId", orderId);// 重定向/pages/cart/checkout.jspresp.sendRedirect(req.getContextPath() + "/pages/cart/checkout.jsp");} else {req.getRequestDispatcher("/pages/user/login.jsp").forward(req, resp);}}// 查看所有订单protected void showAllOrders(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {List<Order> orders = orderService.showAllOrder();req.setAttribute("orders", orders);req.getRequestDispatcher("/pages/manager/order_manager.jsp").forward(req, resp);}// 查看我的订单protected void showMyOrders(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {User LoginUser = (User)req.getSession().getAttribute("user");if(LoginUser!=null) {List<Order> myOrders = orderService.showMyOrders(LoginUser.getId());req.setAttribute("myOrders", myOrders);req.getRequestDispatcher("/pages/order/order.jsp").forward(req, resp);} else {req.getRequestDispatcher("/pages/user/login.jsp").forward(req, resp);}}protected void showOrderDetail(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String orderId = req.getParameter("orderId");List<OrderItem> orderItems = orderService.showOrderDetail(orderId);req.setAttribute("orderItems", orderItems);req.getRequestDispatcher("/pages/order/order_detail.jsp").forward(req, resp);}protected void receiverOrSignOrRefundOrder(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String orderId = req.getParameter("orderId");int status = WebUtils.parseInt(req.getParameter("status"), 0);if (status==0) {int send = orderService.sendOrder(orderId);} else if (status==1) {int receiver = orderService.receiverOrder(orderId);} else {}resp.sendRedirect(req.getHeader("Referer"));}
}

修改 pages/cart/cart.jsp 页面,结账的请求地址
修改 pages/cart/checkout.jsp 页面,输出订单号

书城项目第八阶段

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

package com.atanhui.filter;import com.atanhui.pojo.User;import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;public class ManagerFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {HttpServletRequest request = (HttpServletRequest) servletRequest;User user = (User)request.getSession().getAttribute("user");// *********if (user!=null) {filterChain.doFilter(servletRequest, servletResponse);} else {request.getRequestDispatcher("/pages/user/login.jsp").forward(servletRequest, servletResponse);}}@Overridepublic void destroy() {}
}
<filter><filter-name>ManagerFilter</filter-name><filter-class>com.atanhui.filter.ManagerFilter</filter-class>
</filter>
<filter-mapping><filter-name>ManagerFilter</filter-name><url-pattern>/pages/manager/*</url-pattern><url-pattern>/manager/*</url-pattern>
</filter-mapping>

二、ThreadLocal 的使用

ThreadLocal 的作用,它可以解决多线程的数据安全问题。
ThreadLocal 它可以给当前线程关联一个数据(可以是普通变量,可以是对象,也可以是数组,集合)
ThreadLocal 的特点:
1、ThreadLocal 可以为当前线程关联一个数据。(它可以像 Map 一样存取数据,key 为当前线程)
2、每一个 ThreadLocal 对象,只能为当前线程关联一个数据,如果要为当前线程关联多个数据,就需要使用多个 ThreadLocal 对象实例。
3、每个 ThreadLocal 对象实例定义的时候,一般都是 static 类型。
4、ThreadLocal 中保存数据,在线程销毁后。会由 JVM 虚拟自动释放。

三、使用 Filter 和 ThreadLocal 组合管理事务

1、使用 ThreadLocal 来确保所有 dao 操作都在同一个 Connection 连接对象中完成
要确保所有操作要么都成功,要么都失败,就必须要使用数据库的事务。
要确保所有的操作都在一个事务内,就必须确保,所有操作都使用同一个Connection连接对象。
如何确保所有操作都使用同一个Connection连接对象?
我们可以使用ThreadLocal对象,来确保所有操作都使用同一个Connection对象。
ThreadLoacal对象要确保所有操作都使用同一个Connection连接对象,那么操作的前提条件是所有操作都必须在同一个线程中完成。
JdbcUtils 工具类的修改

package com.atanhui.utils;import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;public class JdbcUtils {// 数据库连接池private static DruidDataSource dataSource;private static ThreadLocal<Connection> conns = new ThreadLocal<Connection>();static {try {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();}}/** 如果返回null,说明获取连接失败,有值就是获取连接成功* 获取数据库连接池中的连接*/public static Connection getConnection(){Connection conn = conns.get();if (conn == null) {try {conn = dataSource.getConnection();      // 从数据库连接池中获取连接conns.set(conn);        // 保存到threadlocal对象中,用于后面的jdbc操作使用conn.setAutoCommit(false);      // 设置手动管理事务} catch (Exception e) {e.printStackTrace();}}return conn;}/** 提交事务,关闭连接,放回数据连接池*/public static void commitAndClose(){Connection conn = conns.get();if (conn != null) {     // 如果不等于null,说明之前使用过,操作过数据库try {conn.commit();      // 提交事务} catch (Exception e) {e.printStackTrace();} finally {try {conn.close();       // 关闭连接} catch (Exception e) {e.printStackTrace();}}}// 一定要执行remove操作,否则会出错,因为Tomcat服务器底层使用了线程池技术conns.remove();}/** 回滚事务,关闭连接,放回数据库连接池*/public static void rollbackAndClose(){Connection conn = conns.get();if (conn != null) {     // 如果不等于null,说明之前使用过,操作过数据库try {conn.rollback();      // 回滚事务} catch (Exception e) {e.printStackTrace();} finally {try {conn.close();       // 关闭连接} catch (Exception e) {e.printStackTrace();}}}// 一定要执行remove操作,否则会出错,因为Tomcat服务器底层使用了线程池技术conns.remove();}
}

修改 BaseDao

package com.atanhui.dao.impl;import com.atanhui.utils.JdbcUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;public abstract class BaseDAO {// DBUtils操作数据库private QueryRunner queryRunner = new QueryRunner();// update方法 用来执行insert update delete语句// 如果return返回-1,说明执行失败,返回其他,表示返回影响的行数public int update(String sql, Object...args) {System.out.println("baseDAO程序在【" + Thread.currentThread().getName() + "】线程中");Connection conn = JdbcUtils.getConnection();try {return queryRunner.update(conn, sql, args);} catch (SQLException e) {e.printStackTrace();// 捕获异常,向外抛出异常throw new RuntimeException(e);}}// 查询一条javaBean的sql语句// type 返回对象类型,sql 执行的sql语句,args sql对应的参数值,<T> 返回的类型的泛型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 (Exception e) {e.printStackTrace();throw new RuntimeException(e);}}// 查询多条javaBean的sql语句// type 返回对象类型,sql 执行的sql语句,args sql对应的参数值,<T> 返回的类型的泛型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 (Exception e) {e.printStackTrace();throw new RuntimeException(e);}}// 执行返回一行一列的sql语句// sql 执行sql的语句,args sql对应的参数值public Object queryForSingleValue(String sql, Object...args) {Connection conn = JdbcUtils.getConnection();try {return queryRunner.query(conn, sql, new ScalarHandler(), args);} catch (SQLException e) {e.printStackTrace();throw new RuntimeException(e);}}
}

2、使用 Filter 过滤器统一给所有的 Service 方法都加上 try-catch。来进行实现的管理
Filter 类代码

package com.atanhui.filter;import com.atanhui.utils.JdbcUtils;import javax.servlet.*;
import java.io.IOException;public class TransactionFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {try {filterChain.doFilter(servletRequest, servletResponse);JdbcUtils.commitAndClose();     // 提交事务} catch (Exception e) {JdbcUtils.rollbackAndClose();      // 回滚事务e.printStackTrace();throw new RuntimeException(e);      // 把异常抛给Tomcat服务器 管理展示友好的错误页面}}@Overridepublic void destroy() {}
}

在 web.xml 中的配置

<filter><filter-name>TransactionFilter</filter-name><filter-class>com.atanhui.filter.TransactionFilter</filter-class>
</filter>
<filter-mapping><filter-name>TransactionFilter</filter-name><!--    /* 表示当前工程下所有的请求    --><url-pattern>/*</url-pattern>
</filter-mapping>

一定要记得把 BaseServlet 中的异常往外抛给 Filter 过滤器
3、将所有异常都统一交给 Tomcat,让 Tomcat 展示友好的错误信息页面
在 web.xml 中我们可以通过错误页面配置来进行管理

<!--  error-page标签配置,服务器出错之后,自动跳转的页面  -->
<error-page><!--    error-code 错误类型    --><error-code>500</error-code><!--    location 需要跳转去的页面标签    --><location>/pages/error/error500.jsp</location>
</error-page>
<error-page><!--    error-code 错误类型    --><error-code>404</error-code><!--    location 需要跳转去的页面标签    --><location>/pages/error/error404.jsp</location>
</error-page>

书城项目第九阶段

一、使用 AJAX 验证用户名是否可用

UserServlet 程序

protected void ajaxExistsUsername(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 获取请求的参数String username = req.getParameter("username");// 调用userService.existsUsername(username)boolean existsUsername = userService.existsUsername(username);// 把返回的结果封装成为map对象Map<String, Object> resultMap = new HashMap<>();resultMap.put("existsUsername", existsUsername);Gson gson = new Gson();String json = gson.toJson(resultMap);resp.getWriter().write(json);
}

regist.jsp 页面中的代码

二、使用 AJAX 修改把商品添加到购物车

CartServlet 程序

protected void AjaxaddItem(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 获取商品编号int id = WebUtils.parseInt(req.getParameter("id"), 0);// 调用 bookService.queryBookById(id)  BookBook book = bookService.queryBookById(id);// 把book对象转换成cartItemCartItem cartItem = new CartItem(book.getId(), book.getName(), 1, book.getPrice(), book.getPrice());// 获取session中的购物车对象cartCart cart = (Cart) req.getSession().getAttribute("cart");if (cart == null) {cart = new Cart();req.getSession().setAttribute("cart", cart);}// 调用cart.addItem(cartItem) 添加购物车cart.addItem(cartItem);// 返回购物车中总商品数量和最后一个添加的商品名称Map<String, Object> resultmap = new HashMap<String, Object>();resultmap.put("lastName", cartItem.getName());resultmap.put("totalCount", cart.getTotalCount());Gson gson = new Gson();String resultmapJson = gson.toJson(resultmap);resp.getWriter().write(resultmapJson);
}

pages/client/index.jsp 页面
javaScript 代码

书城管理系统(Java web)相关推荐

  1. 企业员工管理系统java web jsp javaee项目

    企业员工管理系统java web jsp javaee项目 [源码+数据库+文档+PPT]齐全 企业员工信息管理系统主要用于实现公司的员工相关信息管理,基本功能包括:密码修改.员工管理.部门管理.出勤 ...

  2. 【毕业设计】基于SSM的酒店客房信息管理系统 - java web

    文章目录 0 前言 1 课题描述 2 相关技术 3 需求分析 3.1 系统目标 3.2 功能需求 4 系统功能描述 4.1 系统功能结构 4.2 系统功能流程图 5 系统实现 6 最后 0 前言 Hi ...

  3. java web信息管理系统_Java web学生信息管理系统

    [实例简介] 基于Jsp&servlet;+JavaBean的学生信息管理系统,实现了学生成绩.课程.教师的管理 [实例截图] [核心代码] Javaweb学生信息管理系统 └── Java ...

  4. 简单的Java web(JSP+Sevlet+Java+mysql)源码分享

    引言:Java web项目主要采用mvc的的设计思想,系统主要采用java+jsp+servlet+mysql+eclipse实现,具有登陆.分页.导出excel,增删改查等功能,适合初学者,满足基本 ...

  5. 简单的Java web项目代码(8个)

    引言:Java web项目主要采用mvc的的设计思想,系统主要采用java+jsp+servlet+mysql+eclipse实现,具有登陆.分页.导出excel,增删改查等功能,适合初学者,满足基本 ...

  6. 分享java web 期末项目实验源码20套,BBS论坛,ERP管理系统,OA自动化等等

    分享java web 期末项目实验源码20套,BBS论坛,ERP管理系统,OA自动化等等 我自己也从里面学习到了很多东西! 1.BBS论坛系统(jsp+sql) 2.ERP管理系统(jsp+servl ...

  7. Java毕业设计-网上书城管理系统

    计算机课程设计|毕业设计之网上书城管理系统代码-基于Springboot+Vue的网上书城管理系统 注意:该项目只展示部分功能,如需了解,评论区咨询即可. > > 所有工具下载链接: JD ...

  8. 企业管理系统java web_JavaWeb 基于 web的 企业人事管理系统 Jsp/Servlet 242万源代码下载- www.pudn.com...

    文件名称: JavaWeb下载  收藏√  [ 5  4  3  2  1 ] 开发工具: Java 文件大小: 8310 KB 上传时间: 2015-07-16 下载次数: 0 提 供 者: 汪伟棋 ...

  9. java人力资源管理系统设计_人力资源管理系统——《Java Web程序设计》课程设计...

    内容简介: 人力资源管理系统--<Java Web程序设计>课程设计,正文共24页,7418字,附源程序. 0 引言 全球范围内的企业内部组织正在发生巨变,未来企业成功的关键的依靠企业人力 ...

最新文章

  1. 【C++】C++11 STL算法(二):修改序列的操作(Modifying sequence operations)
  2. 根据一个大图片自动生成相应小图片的代码
  3. WHAT IS ERP
  4. 5g与edge ai_使用OpenVINO部署AI Edge应用
  5. java数组键_Java基础之数组
  6. springcloud记录篇6-分布式配置中心
  7. realm android,Realm for Android快速入门教程
  8. kettlejava脚本的api_[翻译]KETTLE JAVA API :编程定制自己的Kettle转换(transformation)...
  9. Maven 模块管理
  10. 记录几条简单的正则表达式
  11. linux系统安装自主分区,Linux系统的安装(自动引导安装、网络安装、分区选择)...
  12. JavaScript:table元素行的添加和删除
  13. 量子计算机进行量子传输,量子技术新突破:按一下按钮就能实现量子传输!
  14. 如何将多个文件的简体中文名快速翻译成繁体中文
  15. android 加速度过滤,如何校准Android加速度计并降低噪音,消除重力
  16. 静静的分析华为Mate X的部分“亮点”,静静的围观它装逼!
  17. 会议记录管理系统(6) - 查找会议记录模块设计
  18. MySQL表查询关键字
  19. 5G技术全面融入ROS2新一代机器人操作系统大量成果推出(2020整理翻译版)
  20. swsyth_kp_spxz_khxz_js

热门文章

  1. (20210129已解决)Pandas通过某列值包含特定字符串过滤行
  2. word2vec关键词提取 python_如何从word2vec的Google预训练模型中提取单词向量?
  3. 中国互联网乃至移动互联网发展最好的十个城市排名,我会这么排:北京,深圳,上海,杭州,广州,成都,武汉,南京,西安,厦门。
  4. 猿创征文|JAVA 实现《连连看》游戏
  5. Linux环境下连连看游戏代码,《连连看》算法C语言演示(自动连连看)
  6. 计算机在识字教学中的应用,运用多媒体教学,提高识字效率
  7. 普通话证计算机证英语证,老师的必备证书:教师资格证、普通话证、英语四六级、计算机二级...
  8. docker搭建并使用AB(apache bench)测试工具压力测试
  9. 常见算法思想1:枚举法
  10. Themida/WinLicense3.0 IAT修复脚本(x64)(x64dbg)