书城项目详细整理

声明:书城项目来自尚硅谷web课程
具体源码:书城资源链接

JavaEE三层架构

在这里插入图片描述

第二阶段

在这里插入图片描述

1 先创建书城项目所需要的数据库和表

命令行启动mysql服务

net start mysql
mysql -uroot -p
******
#创建数据库
DROP DATABASE IF EXISTS book
CREATE DATABASE book

#使用数据库
USE book

#创建表t_user
CREATE TABLE t_user(
	`id` INT PRIMARY KEY AUTO_INCREMENT,
	`username` VARCHAR(20) NOT NULL UNIQUE,
	`password` VARCHAR(20) NOT NULL,
	`email` VARCHAR(100)
);

#插入管理员数据
INSERT INTO t_user(`username`,`password`,`email`)
VALUES('admin','admin','admin@126.com');

SELECT * FROM t_user;

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

在pojo包下面创建User类

package com.atgw.pojo;/*
 *  @author GaoWei
 *  Date  2021/1/1--13:59
 */

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

    public Integer getId() {
        return id;
    }

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

    public String getUsername() {
        return username;
    }

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

    public String getPassword() {
        return password;
    }

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

    public String getEmail() {
        return email;
    }

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

    public User() {
    }

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

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

3 编写工具类JdbcUtils

jdbc.properties配置文件

username=root
password=000519
url=jdbc:mysql://localhost:3306/book?useUnicode=true&characterEncoding=utf-8&useSSL=false&rewriteBatchedStatement=true
driverClassName=com.mysql.jdbc.Driver
#最小空闲连接数
initialSize=5
#最大连接数
maxActive=10

在utils包下面创建JdbcUtils类

public class JdbcUtils {

    //创建数据库连接池放在静态代码块中是为了只创建一个,因为当类加载的时候就会创建,且只创建一次
    private static DataSource dataSource;
    static {
        try { 
            //读取配置文件,返回输入流
            InputStream is = JdbcUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
            Properties prop = new Properties();
            //加载流
            prop.load(is);
            //创建数据库连接池返回
            dataSource = DruidDataSourceFactory.createDataSource(prop);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //获取连接
    //返回null说明获取连接失败
    public static Connection getConnection(){
        Connection con = null;
        try {
            con = dataSource.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return con;
    }

    //关闭连接
    public static void closeConnection(Connection c){
        //连接非空时才需要关闭
        if(c!=null){
            try {
                c.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

测试JdbaUtils类

public class JdbcUtilsTest {

    @Test
    public void testJdbcUtils(){
        Connection connection = JdbcUtils.getConnection();
        System.out.println(connection);
    }
}

4 编写BaseDao

在dao.impl包下面创建一个抽象类,写一些表的通用操作(其他某种类可以继承使用里面的方法)

//声明为抽象类,写一些表的通用操作(不是特定的某张表)
public abstract class BaseDao {

    //使用DbUtils操作数据库
    //DbUtils封装了一些常用的方法,不用我们自己去写获取和关闭连接的方法
    //QueryRunner已经解决了Connection连接问题,
    private QueryRunner queryRunner = new QueryRunner();

    //修改一个表的数据
    //返回影响的行数
    public int update(String sql,Object...args){
        Connection connection = JdbcUtils.getConnection();
        try {
            return queryRunner.update(connection,sql,args);
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JdbcUtils.close(connection);
        }
        return -1;
    }

    //查询一行数据
    public <T> T queryForOne(Class<T> clazz,String sql,Object...args){
        Connection connection = JdbcUtils.getConnection();
        try {
            //返回一个对象
            return queryRunner.query(connection,sql,new BeanHandler<T>(clazz),args);
            //BeanHandler是ResultSetHandler接口的实现类,结果集的处理器
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JdbcUtils.close(connection);
        }
        return null;
    }

    //查询多行数据
    public <T> List<T> queryForList(Class<T> clazz, String sql, Object...args){
        Connection connection = JdbcUtils.getConnection();
        try {
            return queryRunner.query(connection,sql,new BeanListHandler<T>(clazz),args);
            //BeanListHandler返回多个数据
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JdbcUtils.close(connection);
        }
        return null;
    }

    //查询特殊的数据
    //比如一些集函数Count Avg
    public Object queryForSingleValue(String sql,Object...args){
        Connection connection = JdbcUtils.getConnection();
        try {
            //返回一个Object类型
            return queryRunner.query(connection,sql,new ScalarHandler());
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JdbcUtils.close(connection);
        }
        return null;
    }
}

5 编写UserDao

接口UserDao

//用户在登录时我们需要做的三件事
//在接口中声明这些方法,在用户实现类中需要具体实现这些方法
public interface UserDao {

   //根据用户名查询是否存在(注册页面)
   //返回null说明不存在
   public User queryUserByUsername(String username);

   //根据用户名和密码查询用户信息(登录页面)
   //返回null说明不存在
   public User queryUserByUsernameAndPassword(String username,String password);

   //保存用户信息(注册页面)
   public int saveUser(User user);
}

UserDaoImpl实现类

public class UserDaoImpl extends BaseDao implements UserDao {
    @Override
    public User queryUserByUsername(String username) {
        String sql = "select `id`,`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 `id`,`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());
    }
}

测试类UserDaoTest

快捷键,在UserDao接口中:Ctrl+Shift+t,在test包下面创建测试类

public class UserDaoTest {

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

    @Test
    public void queryUserByUsernameAndPassword() {
        UserDao userDao = new UserDaoImpl();
        User user = userDao.queryUserByUsernameAndPassword("admin","123456");
        if (user != null){
            System.out.println("登录成功");
        }else{
            System.out.println("登录失败");
        }
    }

    @Test
    public void saveUser() {
        UserDao userDao = new UserDaoImpl();
        int result = userDao.saveUser(new User(null,"admin","123456","gw123@qq.com"));
        System.out.println(result);//-1 表示插入失败
    }
}

6 编写UserService

UserService接口

public interface UserService {

    //注册
    public void registUser(User user);

    //登录
    public User login(User user);

    //检查用户名是否存在
    public boolean existsUsername(String username);
}

UserServiceImpl实现类

public class UserServiceImpl implements UserService {

    UserDao userDao = new UserDaoImpl();

    @Override
    public void registUser(User user) {
        userDao.saveUser(user);
    }

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

    @Override
    public boolean existsUsername(String username) {
        User user =  userDao.queryUserByUsername(username);
        //user不等于null,说明用户名已经存在,此用户名不可用,返回true
        if (user!=null){
            return true;
        }
        return false;
    }
}

UserServiceTest测试类

在UserService快捷键Ctrl+Shift+t

public class UserServiceTest {

     UserService userService = new UserServiceImpl();

    @Test
    public void registUser() {
        userService.registUser(new User(null,"li","124578","li@qq.com"));
        userService.registUser(new User(null,"wang","235689","wang@qq.com"));
        //已经插入到表中了
    }

    @Test
    public void login() {
        User user = userService.login(new User(null,"li","124578",null));
        System.out.println(user);
        //User{id=4, username='li', password='124578', email='li@qq.com'}
    }

    @Test
    public void existsUsername() {
        if (userService.existsUsername("wang")){
            System.out.println("用户名不可用");
        }else{
            System.out.println("用户名可用");
        }
        //用户名不可用
    }

7 实现用户注册功能

在web包下创建RegistServlet类

public class RegistServlet extends HttpServlet {
    
    private UserService userService = new UserServiceImpl();
    
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        String username = request.getParameter("username");
        String password = request.getParameter("password");
        String email = request.getParameter("email");
        String code = request.getParameter("code");

        //验证码是否正确
        if ("abcde".equalsIgnoreCase(code)){//现在我们只需要检查验证码是否等于abcde
            //验证码正确时
            //查看用户名是否已经存在
            if (userService.existsUsername(username)){
                //如果已经存在
                System.out.println("用户名" + username +"已经存在");
                //跳转到注册页面
                request.getRequestDispatcher("pages/user/regist.html").forward(request,response);
            }else{
                //用户名不存在
                //保存用户信息到数据库
                userService.registUser(new User(null,username,password,email));
                //调到注册成功页面
                request.getRequestDispatcher("pages/user/regist_success.html").forward(request,response);

            }
        }else{
            //验证码不等于abcde
            System.out.println("验证码"+ code +"错误");
            //跳转到注册页面(请求转发)
            request.getRequestDispatcher("pages/user/regist.html").forward(request,response);
        }
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }
}

配置好web.xml文件

<servlet>
    <servlet-name>RegistServlet</servlet-name>
    <servlet-class>com.atgw.web.RegistServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>RegistServlet</servlet-name>
    <url-pattern>/registServlet</url-pattern>
</servlet-mapping>

修改regist.html文件的base标签

同时修改依赖相对路径跳转到路径

<!--base标签,永远固定相对路径跳转的结果-->
<base href="http://localhost:8080/09_book/">

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

表单的提交方式

<form action="registServlet" method="post">

8 实现用户登录功能

在web包下面创建LoginServlet类

public class LoginServlet extends HttpServlet {

    private UserService userService = new UserServiceImpl();

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        String username = request.getParameter("username");
        String password = request.getParameter("password");

        //根据用户名和密码查询
        if (userService.login(new User(null,username,password,null))==null){
            //没有查询到
            //登录失败,返回到登录页面
            System.out.println("登录失败");
            request.getRequestDispatcher("pages/user/login.html").forward(request,response);
        }else{
            //查询到了
            //登录成功,跳转到登录成功页面
            request.getRequestDispatcher("pages/user/login_success.html").forward(request,response);
        }
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }
}

web.xml文件

    <servlet>
        <servlet-name>LoginServlet</servlet-name>
        <servlet-class>com.atgw.web.LoginServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>LoginServlet</servlet-name>
        <url-pattern>/loginServlet</url-pattern>
    </servlet-mapping>

base标签

    <!--base标签,永远固定相对路径跳转的结果-->
   <base href="http://localhost:8080/09_book/">

<link type="text/css" rel="stylesheet" href="static/css/style.css" >

表单提交

<form action="loginServlet" method="post">

第三阶段(优化)

1 所有html页面改为jsp

  1. 添加page标签

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    
  2. 改文件后缀名为jsp (快捷键:Ctrl+Shift+R)

2 提取共同部分

便于修改和管理

login_success_menu.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<div>
    <span>欢迎<span class="um_span">大帅哥</span>光临尚硅谷书城</span>
    <a href="../order/order.jsp">我的订单</a>
    <a href="../../index.jsp">注销</a>&nbsp;&nbsp;
    <a href="../../index.jsp">返回</a>
</div>

原页面静态引用

			<%-- 静态包含,登陆成功之后的菜单 --%>
			<%@include file="/pages/common/login_success_menu.jsp"%>>

head.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!--base标签,永远固定相对路径跳转的结果-->
<base href="http://localhost:8080/09_book/">

<link type="text/css" rel="stylesheet" href="static/css/style.css" >
<script type="text/javascript" src="static/script/jquery-1.7.2.js"></script>
	<%-- 静态包含 base标签,css文件,jQuery文件 --%>
	<%@include file="/pages/common/head.jsp"%>

footer.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<div id="bottom">
			<span>
				尚硅谷书城.Copyright &copy;2015
			</span>
</div>
	<%--静态包含页脚信息--%>
	<%@include file="/pages/common/footer.jsp"%>

manager目录下

manager_menu.jsp

<div>
    <a href="book_manager.jsp">图书管理</a>
    <a href="order_manager.jsp">订单管理</a>
    <a href="../../index.jsp">返回商城</a>
</div>
			<%--静态包含 图书相关--%>
			<%@include file="/pages/common/manager_menu.jsp"%>

3 动态的base标签值

head.jsp

<%
    String basePath = request.getScheme()//获取协议
        + "://"
        + request.getServerName()//获取ip地址
        + ":"
        + request.getServerPort()//获取端口号
        + request.getContextPath()//获取工程名
        + "/";
%>
<!--base标签,永远固定相对路径跳转的结果-->
<base href="<%=basePath%>>">

4 表单提交失败的错误回显

登录页面

LoginServlet.java

        if (userService.login(new User(null,username,password,null))==null){
            //没有查询到

            //把错误信息,和回显的表单项信息保存在request域中
            request.setAttribute("msg","用户名或密码错误");
            request.setAttribute("username",username);
            //登录失败,返回到登录页面
            request.getRequestDispatcher("pages/user/login.jsp").forward(request,response);
        }

login.jsp

错误信息

								<span class="errorMsg">
									<%=request.getAttribute("msg")==null?"请输入用户名和密码":request.getAttribute("msg")%>
								</span>

回显信息

									<input class="itxt" type="text" placeholder="请输入用户名" autocomplete="off" tabindex="1" name="username"
											value="<%=request.getAttribute("username")==null?"":request.getAttribute("username")%>"
									/>

注册页面

RegisterServlet.java

{
            //验证码不等于abcde

            //将错误信息,回显的信息保存在request域中
            request.setAttribute("msg","验证码错误");
            request.setAttribute("username",username);
            request.setAttribute("email",email);

            //跳转到注册页面(请求转发)
            request.getRequestDispatcher("pages/user/regist.jsp").forward(request,response);
        }
if (userService.existsUsername(username)){
                //如果已经存在

                //将错误信息,回显的信息保存在request域中
                request.setAttribute("msg","用户名已存在");
                request.setAttribute("username",username);
                request.setAttribute("email",email);

                //跳转到注册页面
                request.getRequestDispatcher("pages/user/regist.jsp").forward(request,response);
            }

regist.jsp

错误信息

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

5 合并RegistServlet和LoginServlet为UserServlet

1 添加隐藏表单项,并修改from表单的action为”userServlet“

		<form action="userServlet" method="post">
									<%-- 隐藏表单 hidden --%>
									<input type="hidden" name="action" value="regist">
<form action="userServlet" method="post">
									<%--隐藏表单项--%>
									<input type="hidden" name="action" value="login">

2 创建UserServlet

public class UserServlet extends HttpServlet {

     private UserService userService = new UserServiceImpl();

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String action = request.getParameter("action");
        //判断是哪个表单提交了
        if ("regist".equals(action)){
            regist(request,response);
        }else if("login".equals(action)){
            login(request,response);
        }
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    //处理注册的业务
    protected void regist(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {

        String username = request.getParameter("username");
        String password = request.getParameter("password");
        String email = request.getParameter("email");
        String code = request.getParameter("code");

        //验证码是否正确
        if ("abcde".equalsIgnoreCase(code)){//现在我们只需要检查验证码是否等于abcde
            //验证码正确时
            //查看用户名是否已经存在
            if (userService.existsUsername(username)){
                //如果已经存在

                //将错误信息,回显的信息保存在request域中
                request.setAttribute("msg","用户名已存在");
                request.setAttribute("username",username);
                request.setAttribute("email",email);

                //跳转到注册页面
                request.getRequestDispatcher("pages/user/regist.jsp").forward(request,response);
            }else{
                //用户名不存在
                //保存用户信息到数据库
                userService.registUser(new User(null,username,password,email));
                //调到注册成功页面
                request.getRequestDispatcher("pages/user/regist_success.jsp").forward(request,response);

            }
        }else{
            //验证码不等于abcde

            //将错误信息,回显的信息保存在request域中
            request.setAttribute("msg","验证码错误");
            request.setAttribute("username",username);
            request.setAttribute("email",email);

            //跳转到注册页面(请求转发)
            request.getRequestDispatcher("pages/user/regist.jsp").forward(request,response);
        }
    }

    //处理登录的业务
    protected void login (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String username = request.getParameter("username");
        String password = request.getParameter("password");

        //根据用户名和密码查询
        if (userService.login(new User(null,username,password,null))==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);
        }
    }
}

配置web.xml

    <servlet>
        <servlet-name>UserServlet</servlet-name>
        <servlet-class>com.atgw.web.UserServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>UserServlet</servlet-name>
        <url-pattern>/userServlet</url-pattern>
    </servlet-mapping>

6 使用反射优化不同的用户业务

UserServlet

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取隐藏表单项的action的值
        String action = request.getParameter("action");

        //利用反射获取相应的用户行为,避免使用大量的if else
        //我们只需要在下面编写相应的方法即可,修改密码,绑定邮箱,绑定手机号等业务
        try {
            //获取action鉴别对象,获取相应的业务
            Method method = this.getClass().getDeclaredMethod(action,HttpServletRequest.class,HttpServletResponse.class);
            //调用目标业务
            method.invoke(this,request,response );

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

7 抽取BaseServlet

/*
* 因为在用户的业务和图书的业务上都会使用相同的反射方法
* 所以我们可以抽取相同的部分放在 抽象类BaseServlet中
* 使用UserServlet来继承BaseServlet
* */
public abstract class BaseServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取隐藏表单项的action的值
        String action = request.getParameter("action");

        //利用反射获取相应的用户行为,避免使用大量的if else
        //我们只需要在下面编写相应的方法即可,修改密码,绑定邮箱,绑定手机号等业务
        try {
            //获取action鉴别对象,获取相应的业务
            Method method = this.getClass().getDeclaredMethod(action,HttpServletRequest.class,HttpServletResponse.class);
            //调用目标业务
            method.invoke(this,request,response );

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

UserServlet来继承BaseServlet

public class UserServlet extends BaseServlet{
    
}

8 数据的封装和抽取BeanUtils的使用

BeanUtils工具类,它可以一次性的把所有的请求的参数注入JavaBean(对象)中

导包:

  • commons-beanutils-1.8.0.jar

  • commons-logging-1.1.1.jar

Utils中的工具类

public class WebUtils {
    public static <T> T copyParamToBean(Map value,T bean){
        try {
            System.out.println("参数注入之前:"+bean);
            //把键值对形式Map的value(请求的参数)注入到T(User)中
            BeanUtils.populate(bean,value);
            System.out.println("参数注入之后:"+bean);

        } catch (Exception e) {
            e.printStackTrace();
        }
        return bean;
    }
}

第四阶段(EL表达式替换jsp语句)

使用EL表达式替换相应的jsp语句

EL表达式中:requestScope可以获取request域中的数据

regist.jsp页面

	<%=request.getAttribute("msg")==null?"":request.getAttribute("msg")%>

替换成


${ requestScope.msg }

											value="<%=request.getAttribute("username")==null?"":request.getAttribute("username")%>"
										   <%-- EL表达式替换 --%>
								<%--value="${requestScope.username}"--%>								
										   value="<%=request.getAttribute("email")==null?"":request.getAttribute("email")%>"
										   <%--value="${requestScope.email}"--%>

login页面

									<%=request.getAttribute("msg")==null?"请输入用户名和密码":request.getAttribute("msg")%>
									<%--使用EL表达式替换--%>
									<%--${ empty requestScope.msg?"请输入用户名和密码":requestScope.msg}--%>
											value="<%=request.getAttribute("username")==null?"":request.getAttribute("username")%>"
										   <%--value="${ requestScope.username}"--%>

第五阶段(图书模块)

MVC概念

ModelViewController
模型视图控制器
  • 它可以有效指导Web层的代码如何有效分离,单独工作

  • 一种思想,目的是降低耦合,方便后期升级维护

在这里插入图片描述

1 图书模块的数据库表

CREATE TABLE t_book(
	`id` INT PRIMARY KEY AUTO_INCREMENT,
	`name` VARCHAR(20),#书名
	`price` DECIMAL(11,2),#价格
	`author` VARCHAR(20),#作者
	`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;

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 imgPath = "static/img/default.jpg";

    public Book() {
    }

    public Book(Integer id, String name, String author, BigDecimal price, Integer sales, Integer stock, String imgPath) {
        this.id = id;
        this.name = name;
        this.author = author;
        this.price = price;
        this.sales = sales;
        this.stock = stock;
                //要求传入的imgPath不能为空
        if (this.imgPath != null && !"".equals(this.imgPath)){
            this.imgPath = imgPath;
        }
    }

    public Integer getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public String getAuthor() {
        return author;
    }

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

    public BigDecimal getPrice() {
        return price;
    }

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

    public Integer getSales() {
        return sales;
    }

    public void setSales(Integer sales) {
        this.sales = sales;
    }

    public Integer getStock() {
        return stock;
    }

    public void setStock(Integer stock) {
        this.stock = stock;
    }

    public String getImgPath() {
        return imgPath;
    }

    public void setImgPath(String imgPath) {
                //要求传入的imgPath不能为空

        if (this.imgPath != null && !"".equals(this.imgPath)){
            this.imgPath = imgPath;
        }
    }

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

3 Dao和测试Dao

BookDao接口

public interface BookDao {
    //增加
    public int addBook(Book book);

    //删除
    public int deleteBookById(Integer id);

    //修改
    public int updateBook(Book book);

    //根据id查询图书
    public Book queryBookById(Integer id);

    //查询所有图书
    public 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.getImgPath());
    }

    @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.getImgPath(),book.getId());
    }

    @Override
    public Book queryBookById(Integer id) {
        String sql = "select `id`,`name`,`author`,`price`,`sales`,`stock`,`img_path`as imgPath 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`as imgPath from t_book";
        return queryForList(Book.class,sql);
    }
}

BookDaoTest测试类(在BookDao接口中使用快捷键Ctrl+Shift+T)

public class BookDaoTest {

     private BookDao bookDao = new BookDaoImpl();

    @Test
    public void addBook() {
        bookDao.addBook(new Book(null,"这个世界好奇怪","詹三",new BigDecimal(99),987567,66,null));
    }

    @Test
    public void deleteBookById() {
        bookDao.deleteBookById(21);
    }

    @Test
    public void updateBook() {
        bookDao.updateBook(new Book(22,"这个世界真好","詹三",new BigDecimal(99),9867,66,null));
    }

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

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

4 Service和测试Service

BookService接口

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();
}

BookServiceImpl实现类

public class BookServiceImpl implements BookService {

    private BookDao bookDao = new BookDaoImpl();

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

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

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

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

    @Override
    public List<Book> queryBooks() {
        return bookDao.queryBooks();
    }
}

BookServiceTest测试类

public class BookServiceTest {

     private BookService bookService = new BookServiceImpl();

    @Test
    public void addBook() {
        bookService.addBook(new Book(null,"这个世界好奇怪","詹三",new BigDecimal(99),987567,66,null));
    }

    @Test
    public void deleteBookById() {
        bookService.deleteBookById(23);
    }

    @Test
    public void updateBook() {
        bookService.updateBook(new Book(23,"这个世界真好","詹三",new BigDecimal(99),9567,66,null));
    }

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

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

5 Web层和页面联调测试

在这里插入图片描述

BookServlet类

图书列表
public class BookServlet extends BaseServlet {

    private BookService bookService = new BookServiceImpl();

     //增加图书
    protected void add(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    //删除图书
    protected void delete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    //修改图书
    protected void update(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    //列表查询所有图书
    protected void list(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        //1.通过bookService查询全部图书
        List<Book> books = bookService.queryBooks();
        //2.设置域对象
        request.setAttribute("books",books);
        //3.请求转发到 /pages/manager/book_manager.jsp页面
        request.getRequestDispatcher("/pages/manager/book_manager.jsp").forward(request,response);
    }
}

web.xml

    <servlet>
        <servlet-name>BookServlet</servlet-name>
        <servlet-class>com.atgw.web.BookServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>BookServlet</servlet-name>
                <!--此处的/manager/bookServlet 并不是manager目录的意思,而是后台管理的意思-->
        <url-pattern>/manager/bookServlet</url-pattern>
    </servlet-mapping>


修改manager_menu页面的图书管理地址

 <%-- /manager/bookServlet 是Servlet访问地址,action=list是调用服务器(BookServlet)中的list方法,查询全部图书--%>
    <a href="manager/bookServlet?action=list">图书管理</a>

在book_manager页面对遍历所有图书

			<%--利用JSTL对所有的图书进行遍历--%>
			<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>

实现DoGet方法(因为图书管理板块的提交方式是Get请求,所以我们让doGet执行doPost相同的操作)

BaseServlet

public abstract class BaseServlet extends HttpServlet {

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取隐藏表单项的action的值
        String action = request.getParameter("action");

        //利用反射获取相应的用户行为,避免使用大量的if else
        //我们只需要在下面编写相应的方法即可,修改密码,绑定邮箱,绑定手机号等业务
        try {
            //获取action鉴别对象,获取相应的业务
            Method method = this.getClass().getDeclaredMethod(action,HttpServletRequest.class,HttpServletResponse.class);
            //调用目标业务
            method.invoke(this,request,response );

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //因为图书管理板块的提交方式是Get请求,所以我们让doGet执行doPost相同的操作
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request,response);
    }
}
前台和后台

前台

  • 普通用户使用
  • 一般不需要权限检查,就可以访问的资源(比如淘宝不进行登录就可以访问的首页)
  • 地址:/client/bookServlet

后台

  • 管理员使用
  • 一般都需要权限检查
  • 地址:/manager/bookServlet
添加图书

在这里插入图片描述

book_edit.jsp页面(对表单的提交行为和方式设置,设置隐藏表单项)

隐藏域是用来收集或发送信息的不可见元素,对于网页的访问者来说,隐藏域是看不见的。当表单被提交时,隐藏域就会将信息用你设置时定义的名称和值发送到服务器上

		<div id="main">
			<%--修改表单的提交行为 跳转到book_manager.jsp页面,提交方式get和post都可以--%>
			<form action="manager/bookServlet" method="get">
				<%--隐藏表单域,告诉服务器要执行的方法是add --%>
				<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="时间简史"/></td>
						<td><input name="price" type="text" value="30.00"/></td>
						<td><input name="author" type="text" value="霍金"/></td>
						<td><input name="sales" type="text" value="200"/></td>
						<td><input name="stock" type="text" value="300"/></td>
						<td><input type="submit" value="提交"/></td>
					</tr>	
				</table>
			</form>

BookServlet中的add方法

     //增加图书
    protected void add(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //把添加的图书封装成一个图书对象
        Book book = WebUtils.copyParamToBean(request.getParameterMap(),new Book());
        //保存图书到数据库
        bookService.addBook(book);
        //跳转到图书列表页面
        //调用list方法是要再一次刷新图书信息

        /*但是如果用请求转发的话,会有bug,如果用户按下功能键F5,则浏览器会发起记录中最后一次请求,造成表单重复提交*/
        //request.getRequestDispatcher("/manager/bookServlet?action=list").forward(request,response);
        //使用重定向
        //它的地址是从端口号开始,所以要先获取工程路径request.getContextPath()
        response.sendRedirect(request.getContextPath()+"/manager/bookServlet?action=list");
    }
删除图书

book_manager页面的删除a标签

  • 点击后执行的方法delete,并且返回id参数
					<td><a class="deleteClass" href="manager/bookServlet?action=delete&id=${book.id}">删除</a></td>

添加点击删除提示框

	<script type="text/javascript">
		$(function () {
			//给删除的a标签添加点击事件,出现提示框
			$("a.deleteClass").click(function(){
			    //获取当前点击a标签的dom对象
                var bookName = $(this).parent().parent().find("td:first").text();
                return confirm("确认删除【"+bookName+"】吗?");
                //返回true,继续执行原操作;返回false,则什么也不做
            });
        });
	</script>

BookServlet中的delete方法

  • 将字符串转化成int封装成一个工具类(在WebUtils中)
    //将字符串转化成int类型
    public static int parseInt(String s,int defaultValue){
        try {
            return Integer.parseInt(s);
        }catch (Exception e){
            e.printStackTrace();
        }
        return defaultValue;
    }
    //删除图书
    protected void delete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取参数id
        int id = WebUtils.parseInt(request.getParameter("id"), 0);
        //删除图书
        bookService.deleteBookById(id);
        //重定向跳转图书管理页面
        response.sendRedirect(request.getContextPath()+"/manager/bookServlet?action=list");
    }
修改图书

在这里插入图片描述

1.数据回显

book_manager.jsp(点击修改执行的动作,getBook()方法查询图书,并返回id)

					<td><a href="manager/bookServlet?action=getBook&id=${book.id}">修改</a></td>

book_edit.jsp页面(动态获取当前的修改对象图书的信息)

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

2.保存修改并显示

因为book_edit同时实现添加图书和修改图书两种功能,所以我们需要判断是哪种行为,从而执行不同的方法

book_edit.jsp的隐藏表单域

		<%--隐藏表单域,告诉服务器要执行的方法时add --%>
				<input type="hidden" name="action" value="${empty param.id?"add":"update"}">
					<%--${empty param.id?"add":"update"} 是判断页面的当前行为,因为添加不需要id属性,而修改需要id属性--%>

用隐藏域来获取id

				<input type="hidden" name="id" value="${requestScope.book.id}">

修改方法update

    //修改图书
    protected void update(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取请求的参数,封装成BOOk对象
        Book book = WebUtils.copyParamToBean(request.getParameterMap(), new Book());
        //修改图书
        bookService.updateBook(book);
        //重定向到图书管理列表
        response.sendRedirect(request.getContextPath()+"/manager/bookServlet?action=list");
    }
后台分页

在这里插入图片描述

Page类

public class Page<T> {

    //静态常量,页面显示数据数目
    public static final Integer PAGE_SIZE = 4;

    //当前页码
    private Integer pageNo;

    //总页码
    private Integer pageTotal;

    //当前页显示数量
    private Integer pageSize = PAGE_SIZE;

    //总记录数
    private Integer pageTotalCount;

    //当前页数据
    private List<T> items;

    public static Integer getPageSize() {
        return PAGE_SIZE;
    }

    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) {
        this.pageNo = pageNo;
    }

    public Integer getPageTotal() {
        return pageTotal;
    }

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

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

    public Page() {
    }

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

BookDao接口

    Integer queryForPageTotalCount();

    List<Book> quertForItems(int begin, int pageSize);

BookDaoImpl实现类(queryForPageTotalCount()方法,quertForItems(int begin, int pageSize)方法)

    @Override
    public Integer queryForPageTotalCount() {
        String sql = "select count(*) from t_book";
        Number count = (Number)queryForSingleValue(sql);
        //queryForSingleValue()返回值是Object类型,不能直接强制转化为Integer
        return count.intValue();//转成Integer类
    }

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

BookDaoTest测试类

    @Test
    public void queryForPageTotalCount(){
        System.out.println(bookDao.queryForPageTotalCount());
    }
    @Test
    public void quertForItems(){
        for( Book book:bookDao.quertForItems(0,4)){
            System.out.println(book);
        }
    }

BookService接口

 Page<Book> page(int pageNo, int pageSize);

BookServiceImpl实现类(page()方法)

 @Override
 public Page<Book> page(int pageNo, int pageSize) {
     Page page = new Page();
     //设置当前页
     page.setPageNo(pageNo);
     //设置每页显示数量
     page.setPageSize(pageSize);

     //获取(使用Dao层的sql语句获取)并设置总记录数
     Integer pageTotalCount = bookDao.queryForPageTotalCount();
     page.setPageTotalCount(pageTotalCount);

     //获取并设置总页码数
     int pageTotal = pageTotalCount / pageSize;
     if (pageTotalCount % pageSize!=0){//除不尽时,总页码加1
         pageTotal+=1;
     }
     page.setPageTotal(pageTotal);

     //设置当前页面显示数据
     int begin = (page.getPageNo() - 1)*pageSize;//当前页数据的开始索引
     List<Book> books = bookDao.quertForItems(begin,pageSize);
     page.setItems(books);

     return page;
 }

BookService测试(page方法)

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

BookServlet(page()方法)

    //分页
    protected void page(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
        //1 获取当前页码,默认值为1
        int pageNo = WebUtils.parseInt(request.getParameter("pageNo"), 1);
        //1 获取每页显示数据数目,默认值为PAGE_SIZE
        int pageSize = WebUtils.parseInt(request.getParameter("pageSize"), Page.PAGE_SIZE);
        //2 获取Page对象
        Page<Book> page = bookService.page(pageNo, pageSize);
        //3 保存到域对象
        request.setAttribute("page",page);
        //4 请求转发到book_manager页面
        request.getRequestDispatcher("/pages/manager/book_manager.jsp").forward(request,response);
    }

manager_menu.jsp

  • 跳转的行为(由list–>page;全部遍历–>分页遍历)
        <a href="manager/bookServlet?action=page">图书管理</a>

book_manager.jsp

  • 全部图书遍历修改为分页信息遍历
  • 添加页脚信息(动态)
			<%--分页后,只对page对象的items(当前页面数据)进行遍历--%>
			<c:forEach items="${requestScope.page.items}" var="book">

并在最后加上页脚信息

		<div id="page_nav">
			<a href="#">首页</a>
			<a href="#">上一页</a>
			<a href="#">${requestScope.page.pageNo-1}</a>
			【${requestScope.page.pageNo}】
			<a href="#">${requestScope.page.pageNo+1}</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>

首页,上一页,末页,下一页(book_manager.jsp)

			<%--如果当前页大于第1页,才显示首页,上一页--%>
			<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: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>

跳转到指定页面(绑定单击响应事件)

到第<input value="${requestScope.page.pageNo}" name="pn" id="pn_input"/>页
	<input id="searchPageBtn" type="button" value="确定">


	<script type="text/javascript">
		$(function () {
			//给页码跳转绑定单击响应事件
			$("#searchPageBtn").click(function () {
				//获取跳转输入框的值
				var pageNo = $("#pn_input").val();

				//获取总页码
				var pageTotal = ${requestScope.page.pageTotal};

				//合法条件下跳转
				if(pageNo>0 && pageNo<= pageTotal){
                    //location地址栏对象
                    //href属性可以获取浏览器地址栏中的地址
					//动态获取ip地址,端口号,工程名
                    location.href="${pageScope.basePath}manager/bookServlet?action=page&pageNo="+pageNo;
                }
            });
        });
	</script>

在head.jsp中设置

 pageContext.setAttribute("basePath",basePath);

如果在地址栏中直接改变页数,则我们需要设置数据边界有效性检查(在每次改变地址时都会检查)

    public void setPageNo(Integer pageNo) {
        //数据边界的有效性检查
        if (pageNo < 1){
            pageNo=1;
        }
        if (pageNo > pageTotal){
            pageNo = pageTotal;
        }
        this.pageNo = pageNo;
    }

分页点击跳转

                <%--页码点击跳转--%>
            <c:choose>
                <%--情况1:总页数小于等于5时--%>
                <c:when test="${requestScope.page.pageTotal <=5}">
                    <c:forEach begin="1" end="${requestScope.page.pageTotal}" var="i">
                        <%--如果是当前页,则不可点击,不可跳转--%>
                        <c:if test="${ i == requestScope.page.pageNo}">
                            【${i}】
                        </c:if>
                        <%--如果是其他页,则可以点击,可以跳转--%>
                        <c:if test="${i != requestScope.page.pageNo}">
                            <a href="manager/bookServlet?action=page&pageNo=${i}">${i}</a>
                        </c:if>
                    </c:forEach>
                </c:when>

                <%--情况2:总页数大于5时 --%>
                <c:when test="${requestScope.page.pageTotal >5}">
                    <%--情况2.1 当前页是前3页 --%>
                    <c:choose>
                        <c:when test="${requestScope.page.pageNo <=3}">
                            <c:forEach begin="1" end="5" var="i">
                                <%--如果是当前页,则不可点击,不可跳转--%>
                                <c:if test="${ i == requestScope.page.pageNo}">
                                    【${i}】
                                </c:if>
                                <%--如果是其他页,则可以点击,可以跳转--%>
                                <c:if test="${i != requestScope.page.pageNo}">
                                    <a href="manager/bookServlet?action=page&pageNo=${i}">${i}</a>
                                </c:if>
                            </c:forEach>
                        </c:when>

                        <%--情况2.2 当前页是后3页 --%>
                        <c:when test="${requestScope.page.pageNo >= requestScope.page.pageTotal-2}">
                            <c:forEach begin="${requestScope.page.pageTotal -4}" end="${requestScope.page.pageTotal}" var="i">
                                <%--如果是当前页,则不可点击,不可跳转--%>
                                <c:if test="${ i == requestScope.page.pageNo}">
                                    【${i}】
                                </c:if>
                                <%--如果是其他页,则可以点击,可以跳转--%>
                                <c:if test="${i != requestScope.page.pageNo}">
                                    <a href="manager/bookServlet?action=page&pageNo=${i}">${i}</a>
                                </c:if>
                            </c:forEach>
                        </c:when>

                        <%--情况2.3 当前页是中间页 --%>
                        <c:otherwise>
                            <c:forEach begin="${requestScope.page.pageNo-2}" end="${requestScope.page.pageNo+2}" var="i">
                                <%--如果是当前页,则不可点击,不可跳转--%>
                                <c:if test="${ i == requestScope.page.pageNo}">
                                    【${i}】
                                </c:if>
                                <%--如果是其他页,则可以点击,可以跳转--%>
                                <c:if test="${i != requestScope.page.pageNo}">
                                    <a href="manager/bookServlet?action=page&pageNo=${i}">${i}</a>
                                </c:if>
                            </c:forEach>
                        </c:otherwise>
                    </c:choose>
                </c:when>
            </c:choose>

修改分页对添加,修改,删除图书的影响

  • BookServlet中的方法请求转发和重定向语句所有的list改为page

1 添加图书(添加之后要看到最后一页添加的结果)

				<td><a href="pages/manager/book_edit.jsp?pageNo=${requestScope.page.pageTotal}">添加图书</a></td>

add()方法(因为在边界有效性上做了处理,大于总页数的会显示最后一页的内容,所以给当前最后一页永远加1时会一直显示最后一页)

        //分页之后,要看到刚才添加的图书信息,所以要跳转到最后一页
        int pageNo = WebUtils.parseInt(request.getParameter("pageNo"),0);
        pageNo+=1;

        response.sendRedirect(request.getContextPath()+"/manager/bookServlet?action=page&pageNo="+pageNo);

2 删除图书

					<td><a class="deleteClass" href="manager/bookServlet?action=delete&id=${book.id}&pageNo=${requestScope.page.pageTotal}">删除</a></td>

delete()方法

        response.sendRedirect(request.getContextPath()+"/manager/bookServlet?action=page&pageNo="+request.getParameter("pageNo"));

3 修改图书

					<td><a href="manager/bookServlet?action=getBook&id=${book.id}&pageNo=${requestScope.page.pageNo}">修改</a></td>
        response.sendRedirect(request.getContextPath()+"/manager/bookServlet?action=page&pageNo="+request.getParameter("pageNo"));
前台分页

在这里插入图片描述

从web目录下的index.jsp请求转发到client目录下的index.jsp(间接访问)

ClientBookServlet

public class ClientBookServlet extends BaseServlet {

     private BookService bookService = new BookServiceImpl();

     //分页
    protected void page(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
        //1 获取当前页码,默认值为1
        int pageNo = WebUtils.parseInt(request.getParameter("pageNo"), 1);
        //1 获取每页显示数据数目,默认值为PAGE_SIZE
        int pageSize = WebUtils.parseInt(request.getParameter("pageSize"), Page.PAGE_SIZE);
        //2 获取Page对象
        Page<Book> page = bookService.page(pageNo, pageSize);
        //3 保存到域对象
        request.setAttribute("page",page);
        //4 请求转发到book_manager页面
        request.getRequestDispatcher("/pages/client/index.jsp").forward(request,response);
    }
}

web.xml

    <servlet>
        <servlet-name>ClientBookServlet</servlet-name>
        <servlet-class>com.atgw.web.ClientBookServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>ClientBookServlet</servlet-name>
        <url-pattern>/client/bookServlet</url-pattern>
    </servlet-mapping>

Web目录下的index.jsp

  • 只负责请求转发,并执行page()方法
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<%--只负责请求转发,并执行page方法--%>
<jsp:forward page="/client/bookServlet?action=page"></jsp:forward>

client目录下的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>
	<%-- 静态包含 base标签,css文件,jQuery文件 --%>
	<%@include file="/pages/common/head.jsp"%>

	<script type="text/javascript">
        $(function () {
            //给删除的a标签添加点击事件,出现提示框
            $("a.deleteClass").click(function(){
                //获取当前点击a标签的dom对象
                var bookName = $(this).parent().parent().find("td:first").text();
                return confirm("确认删除【"+bookName+"】吗?");
                //返回true,继续执行原操作;返回false,则什么也不做
            });
        });
	</script>

	<script type="text/javascript">
        $(function () {
            //给页码跳转绑定单击响应事件
            $("#searchPageBtn").click(function () {
                //获取跳转输入框的值
                var pageNo = $("#pn_input").val();

                //获取总页码
                var pageTotal = ${requestScope.page.pageTotal};

                //合法条件下跳转
                if(pageNo>0 && pageNo<= pageTotal){
                    //location地址栏对象
                    //href属性可以获取浏览器地址栏中的地址
                    //动态获取ip地址,端口号,工程名
                    location.href="${pageScope.basePath}manager/bookServlet?action=page&pageNo="+pageNo;
                }
            });
        });
	</script>
</head>
<body>
	
	<div id="header">
			<img class="logo_img" alt="" src="static/img/logo.gif" >
			<span class="wel_word">网上书城</span>
			<div>
				<a href="pages/user/login.jsp">登录</a> |
				<a href="pages/user/regist.jsp">注册</a> &nbsp;&nbsp;
				<a href="pages/cart/cart.jsp">购物车</a>
				<a href="pages/manager/manager.jsp">后台管理</a>
			</div>
	</div>
	<div id="main">
		<div id="book">
			<div class="book_cond">
				<form action="" method="get">
					价格:<input id="min" type="text" name="min" value=""> 元 - 
						<input id="max" type="text" name="max" value=""> 元 
						<input type="submit" value="查询" />
				</form>
			</div>
			<div style="text-align: center">
				<span>您的购物车中有3件商品</span>
				<div>
					您刚刚将<span style="color: red">时间简史</span>加入到了购物车中
				</div>
			</div>

			<c:forEach items="${requestScope.page.items}" var="book">
				<div class="b_list">
					<div class="img_div">
						<img class="book_img" alt="" src="static/img/default.jpg" />
					</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">
			<%--如果当前页大于第1页,才显示首页,上一页--%>
			<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时--%>
				<c:when test="${requestScope.page.pageTotal <=5}">
					<c:forEach begin="1" end="${requestScope.page.pageTotal}" var="i">
						<%--如果是当前页,则不可点击,不可跳转--%>
						<c:if test="${ i == requestScope.page.pageNo}">
							【${i}】
						</c:if>
						<%--如果是其他页,则可以点击,可以跳转--%>
						<c:if test="${i != requestScope.page.pageNo}">
							<a href="client/bookServlet?action=page&pageNo=${i}">${i}</a>
						</c:if>
					</c:forEach>
				</c:when>

				<%--情况2:总页数大于5时 --%>
				<c:when test="${requestScope.page.pageTotal >5}">
					<%--情况2.1 当前页是前3页 --%>
					<c:choose>
						<c:when test="${requestScope.page.pageNo <=3}">
							<c:forEach begin="1" end="5" var="i">
								<%--如果是当前页,则不可点击,不可跳转--%>
								<c:if test="${ i == requestScope.page.pageNo}">
									【${i}】
								</c:if>
								<%--如果是其他页,则可以点击,可以跳转--%>
								<c:if test="${i != requestScope.page.pageNo}">
									<a href="client/bookServlet?action=page&pageNo=${i}">${i}</a>
								</c:if>
							</c:forEach>
						</c:when>

						<%--情况2.2 当前页是后3页 --%>
						<c:when test="${requestScope.page.pageNo >= requestScope.page.pageTotal-2}">
							<c:forEach begin="${requestScope.page.pageTotal -4}" end="${requestScope.page.pageTotal}" var="i">
								<%--如果是当前页,则不可点击,不可跳转--%>
								<c:if test="${ i == requestScope.page.pageNo}">
									【${i}】
								</c:if>
								<%--如果是其他页,则可以点击,可以跳转--%>
								<c:if test="${i != requestScope.page.pageNo}">
									<a href="client/bookServlet?action=page&pageNo=${i}">${i}</a>
								</c:if>
							</c:forEach>
						</c:when>

						<%--情况2.3 当前页是中间页 --%>
						<c:otherwise>
							<c:forEach begin="${requestScope.page.pageNo-2}" end="${requestScope.page.pageNo+2}" var="i">
								<%--如果是当前页,则不可点击,不可跳转--%>
								<c:if test="${ i == requestScope.page.pageNo}">
									【${i}】
								</c:if>
								<%--如果是其他页,则可以点击,可以跳转--%>
								<c:if test="${i != requestScope.page.pageNo}">
									<a href="client/bookServlet?action=page&pageNo=${i}">${i}</a>
								</c:if>
							</c:forEach>
						</c:otherwise>
					</c:choose>
				</c:when>
			</c:choose>


			<%--如果当前页小于总页数,才显示末页,下一页--%>
			<c:if test="${requestScope.page.pageNo < requestScope.page.pageTotal}">
				<a href="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>
	
	</div>
	
	<div id="bottom">
		<span>
			尚硅谷书城.Copyright &copy;2015
		</span>
	</div>
</body>
</html>
前后台分页条的抽取

Page类中新添加一个url属性(分页条的地址)

Private String url

1 替换为url

  • 将前台(client目录下的index.jsp)分页条代码块中的client/bookServlet?action=page替换成${ requestScope.page.url}

  • 将后台(book_manager.jsp)分页条代码块中的manager/bookServlet?action=page替换成${ requestScope.page.url}

2 设置page对象的url

  • 前台 ClientBookServlet中的page()方法

            //设置请求地址
            page.setUrl("client/bookServlet?action=page");
    
  • 后台BookServlet中的page()方法

            //设置请求地址
            page.setUrl("manager/bookServlet?action=page");
    

3 最后从client/index.jsp和book_manager.jsp中抽取分页的代码块到common/page_nav.jsp中,并在原两个页面中静态引用

page_nav.jsp

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

<%--分页条--%>
<div id="page_nav">
    <%--如果当前页大于第1页,才显示首页,上一页--%>
    <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时--%>
        <c:when test="${requestScope.page.pageTotal <=5}">
            <c:forEach begin="1" end="${requestScope.page.pageTotal}" 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:when>

        <%--情况2:总页数大于5时 --%>
        <c:when test="${requestScope.page.pageTotal >5}">
            <%--情况2.1 当前页是前3页 --%>
            <c:choose>
                <c:when test="${requestScope.page.pageNo <=3}">
                    <c:forEach begin="1" end="5" 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:when>

                <%--情况2.2 当前页是后3页 --%>
                <c:when test="${requestScope.page.pageNo >= requestScope.page.pageTotal-2}">
                    <c:forEach begin="${requestScope.page.pageTotal -4}" end="${requestScope.page.pageTotal}" 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:when>

                <%--情况2.3 当前页是中间页 --%>
                <c:otherwise>
                    <c:forEach begin="${requestScope.page.pageNo-2}" end="${requestScope.page.pageNo+2}" 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:otherwise>
            </c:choose>
        </c:when>
    </c:choose>


    <%--如果当前页小于总页数,才显示末页,下一页--%>
    <c:if test="${requestScope.page.pageNo < requestScope.page.pageTotal}">
        <a href="${ requestScope.page.url}&pageNo=${requestScope.page.pageNo+1}">下一页</a>
        <a href="${ requestScope.page.url}&pageNo=${requestScope.page.pageTotal}">末页</a>
    </c:if>

    共${requestScope.page.pageTotal}页,${requestScope.page.pageTotalCount}条记录 到第<input value="${requestScope.page.pageNo}" name="pn" id="pn_input"/>页
    <input id="searchPageBtn" type="button" value="确定">

</div>

client/index.jsp和book_manager.jsp

        <%--静态包含分页条--%>
		<%@include file="../common/page_nav.jsp"%>
根据价格区间分页

在这里插入图片描述

BookDao接口

    List<Book> queryForItemsByPrice(int begin, int pageSize, int min, int max);

    Integer queryForPageTotalCountByPrice(int min, int max);

BookDaoImpl实现类


    @Override
    public Integer queryForPageTotalCountByPrice(int min, int max) {
        String sql = "select count(*) from t_book where price between ? and ?";
        Number count = (Number)queryForSingleValue(sql,min,max);
        //queryForSingleValue()返回值是Object类型,不能直接强制转化为Integer
        return count.intValue();//转成Integer类
    }

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

BookDao测试

   @Test
    public void queryForPageTotalCountByPrice(){
        System.out.println(bookDao.queryForPageTotalCountByPrice(20,100));
    }

    @Test
    public void queryForItemsByPrice(){
        for( Book book:bookDao.queryForItemsByPrice(0,4,20,100)){
            System.out.println(book);
        }
    }

BookService接口

//根据价区间查询
    Page<Book> pageByPrice(int pageNo, int pageSize, int min, int max);

BookServiceImpl实现类

   @Override
    public Page<Book> pageByPrice(int pageNo, int pageSize, int min, int max) {
        Page page = new Page();

        //设置每页显示数量
        page.setPageSize(pageSize);

        //获取(使用Dao层的sql语句获取)并设置总记录数
        Integer pageTotalCount = bookDao.queryForPageTotalCountByPrice(min,max);
        page.setPageTotalCount(pageTotalCount);

        //获取并设置总页码数
        int pageTotal = pageTotalCount / pageSize;
        if (pageTotalCount % pageSize!=0){//除不尽时,总页码加1
            pageTotal+=1;
        }
        page.setPageTotal(pageTotal);

        //设置当前页
        page.setPageNo(pageNo);

        //设置当前页面显示数据
        int begin = (page.getPageNo() - 1)*pageSize;//当前页数据的开始索引
        List<Book> books = bookDao.queryForItemsByPrice(begin,pageSize,min,max);
        page.setItems(books);

        return page;
    }

BookService测试

    @Test
    public void pageByPrice(){
        System.out.println(bookService.pageByPrice(0,4,20,100));
    }

ClientBookServlet

    //价格区间搜索分页
    protected void pageByPrice(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
        //1 获取当前页码,默认值为1
        int pageNo = WebUtils.parseInt(request.getParameter("pageNo"), 1);
        //1 获取每页显示数据数目,默认值为PAGE_SIZE
        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 获取Page对象
        Page<Book> page = bookService.pageByPrice(pageNo, pageSize,min,max);

        //设置请求地址
        page.setUrl("client/bookServlet?action=pageByPrice");

        //3 保存到域对象
        request.setAttribute("page",page);
        //4 请求转发到book_manager页面
        request.getRequestDispatcher("/pages/client/index.jsp").forward(request,response);
    }

区间参数回显(${param.min} ${param.max})

<input id="min" type="text" name="min" value="${param.min}"> 元 -
						<input id="max" type="text" name="max" value="${param.max}"> 元

解决bug

当我们点击分页行跳转时,地址栏中不会带有区间的min和max,造成价格区间所搜索的记录不会显示,以我们添加上去

在BookServlet程序pageByPrice()中

        /*解决bug*/
        //当我们点击分页行跳转时,地址栏中不会带有区间的min和max,造成价格区间所搜索的记录不会显示,以我们添加上去
        StringBuilder sb = new StringBuilder("client/bookServlet?action=pageByPrice");
        //获取地址栏中的min,max参数
        //如果有最小价格参数,就追加到地址栏中
        if (request.getParameter("min")!=null){
            sb.append("&min=").append(request.getParameter("min"));
        }
        //如果有最大价格参数,就追加到地址栏中
        if (request.getParameter("max")!=null){
            sb.append("&max=").append(request.getParameter("max"));
        }

        //设置请求地址
        page.setUrl(sb.toString());

第六阶段

1 显示登录用户的信息

因为在用户登录成功之后,在前台所有的页面上都要显示用户的用户名信息,所以我们用Session域保存数据

UserSetvlet的login()方法

//保存用户登陆成功之后的信息到Session域中
         request.getSession().setAttribute("user",loginUser);

login_success_menu.jsp中改变欢迎信息,并修改其他页的地址

    <span>欢迎<span class="um_span">${sessionScope.user.username}</span>光临尚硅谷书城</span>

    <a href="pages/order/order.jsp">我的订单</a>
    <a href="index.jsp">注销</a>&nbsp;&nbsp;
    <a href="index.jsp">返回</a>

当用户登录成功之后,登录和注册按钮就不用显示了(client/index.jsp)

<%--如果用户还没有登录,显示登录和注册的菜单--%>
               <c:if test="${empty sessionScope.user}">
                   <a href="pages/user/login.jsp">登录</a> |
                   <a href="pages/user/regist.jsp">注册</a> &nbsp;&nbsp;
               </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>&nbsp;&nbsp;
               </c:if>

2 注销用户

  1. 销毁Session中用户的信息
  2. 重定向到首页

UserServlet程序

   //注销
   protected void logout(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
       //销毁Session对象
       request.getSession().invalidate();
       //重定向到首页
       response.sendRedirect(request.getContextPath()+"/");//工程路径下默认就是首页
   }

client/index.jsp

                   <a href="userServlet?action=logout">注销</a>&nbsp;&nbsp;

login_success_menu.jsp

   <a href="userServlet?action=logout">注销</a>&nbsp;&nbsp;

3 表单重复提交—验证码

三种情况

  1. 提交完表单,服务器使用请求转发来进行页面跳转,如果用户按下F5刷新功能键,就会发起最后一次请求,造成表单的重复提交,解决方法:使用重定向来进行转发
  2. 用户正常提交服务器,但是由于网络延迟等原因,迟迟未收到服务器的响应,这个时候,用户以为提交失败就重复点击提交按钮,就会造成表单的重复提交
  3. 用户正常提交服务器,服务器也没有延迟,但是提交完成之后,用户回退浏览器,重新提交,也会造成表单重复提交

在这里插入图片描述

4 谷歌验证码kaptcha的使用

  1. 导入kaptcha-2.3.2.jar
  2. 在web.xml中去配置用于验证码生成的Servlet程序(KaptchaServlet)
  3. 在表单中使用img标签使用它
  4. 在服务器获取谷歌生成验证码和客户端发送过来的验证码比较使用

UserServlet程序(在注册方法中进行判断)

//获取Session中的验证码
       String token = (String) request.getSession().getAttribute(KAPTCHA_SESSION_KEY);
       //获取之后直接删除Session中的验证码,防止重复提交表单
       request.getSession().removeAttribute(KAPTCHA_SESSION_KEY);


//验证码是否正确
       if (token!=null && token.equalsIgnoreCase(code))

web.xml

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

注册页面regist.jsp

<label>验证码:</label>
									<input class="itxt" type="text" style="width: 120px;" name="code" id="code"/>
									<img alt="" src="kaptcha.jpg" style="float: right; margin-right: 40px; width: 90px; height: 35px">

点击验证码进行切换(就是给验证码图片加上点击事件)

  • 因为浏览器的缓存原因,我们每次都要将请求地址的值变得不同,所以加上一个时间戳

在这里插入图片描述

		<script type="text/javascript">
			$(function () {
				//给验证码图片加上单击响应事件
				$("#code_img").click(function () {
				    //this是指当前点击的验证码图片,src就是它的属性
					//因为浏览器的缓存原因,我们每次都要将请求地址的值变得不同,所以加上一个时间戳
					this.src = "${basePath}kaptcha.jpg?"+new Date();
                });
            })
		</script>

5 购物车模块

在这里插入图片描述

购物车商品对象 CartItem类

public class CartItem {
    private Integer id;
    private String name;
    private Integer count;
    private BigDecimal price;
    private BigDecimal totalPrice;
}

购物车对象 Cart类

public class Cart {
   //实际上用不到总价格和总数量的全局变量,这两个变量我们是通过对所有的商品项遍历而得的
//private Integer totalCount;
  // private BigDecimal totalPrice;
   //private List<CartItem> items = new ArrayList<CartItem>();
   
   
   //我们想一种商品只是一个商品项,所以用Map数据结构(不重复)来存储商品项对象,id-->商品对象
   private Map<Integer,CartItem> items = new LinkedHashMap<>();
}

Cart类的四个方法的实现

    //增加商品项
    public void addItem(CartItem cartItem){
        //在购物车的商品项中查找是否已经存在了要插入的商品
        CartItem item = items.get(cartItem.getId());

        if (item == null) {
            //没有存在,则插入
            items.put(cartItem.getId(),cartItem);
        }else{
            //已经存在,则修改商品总数量和总价格
            item.setCount(item.getCount()+1);
            item.setTotalPrice(item.getPrice().multiply(new BigDecimal(item.getCount())));
        }
    }

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

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

    //修改商品数量
    public void updateCount(Integer id,Integer count){
        //先查看购物车中是否有此商品
        CartItem item = items.get(id);
        if (item !=null){
            item.setCount(count);
            //修改商品总价格
            item.setTotalPrice(item.getPrice().multiply(new BigDecimal(item.getCount())));
        }
    }

    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 +
                '}';
    }
}

6 添加商品到购物车

理解

  • 因为在添加购物车时,购物车只有一个,我们把它放在Session中,刚开始添加购物车时,先判断Session中是否有购物车,如果没有,则新创建一个存放在Session域中
  • 因为在添加完购物车之后,服务器处理了添加操作,我们还要把页面返回到刚才用户浏览的页面
    在HTTP协议中有一个请求头,叫Referer,它可以把请求发起时,浏览器地址栏中的地址发送给服务器

CartServlet(addItem方法)

//添加购物车
    protected void addItem(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        //1 获取请求的参数,商品编号
        int id = WebUtils.parseInt(request.getParameter("id"),0);
        //2 调用bookServlet.queryBookById() 查询获取到图书的信息
        Book book = bookService.QueryBookById(id);
        //3 把图书信息,转换成CartItem商品项
        CartItem item = new CartItem(book.getId(), book.getName(), 1, book.getPrice(), book.getPrice());
        //4 调用Cart.addItem(CartItem) 添加商品项
        /*因为在添加购物车时,购物车只有一个,我们把它放在Session中
        * 刚开始添加购物车时,先判断Session中是否有购物车,如果没有,
        * 则新创建一个存放在Session域中 */
        Cart cart = (Cart) request.getSession().getAttribute("cart");
        if (cart == null){
            cart = new Cart();
            request.getSession().setAttribute("cart",cart);
        }
        cart.addItem(item);
        //5 重定向回商品列表页面
        //因为在添加完购物车之后,服务器处理了添加操作,我们还要把页面返回到刚才用户浏览的页面
        /*在HTTP协议中有一个请求头,叫Referer,它可以把请求发起时,浏览器地址栏中的地址发送给服务器*/
        response.sendRedirect(request.getHeader("Referer"));
    }

web.xml

    <servlet>
        <servlet-name>CartServlet</servlet-name>
        <servlet-class>com.atgw.web.CartServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>CartServlet</servlet-name>
        <url-pattern>/cartServlet</url-pattern>
    </servlet-mapping>

client/index.jsp

<%--自定义属性bookId,获取点击的商品的id--%>
							<button bookId="${book.id}" class="addToCart">加入购物车</button>



    <script type="text/javascript">
        $(function () {
            // 给添加购物车按钮添加点击响应事件
            $("button.addToCart").click(function () {
                //跳转到购物车模块执行添加商品方法
                //获取点击的商品的id(bookId是button按钮的自定义属性)
                var bookId = $(this).attr("bookId");
                location.href = "http://localhost:8080/09_book/cartServlet?action=addItem&id="+bookId;
            })
        })
    </script>

7 购物车的展示

遍历购物车 cart.jsp

			<%--遍历购物车--%>
			<c:forEach items="${sessionScope.cart.items}" var="entry">
				<tr>
					<td>${entry.value.name}</td>
					<td>${entry.value.count}</td>
					<td>${entry.value.price}</td>
					<td>${entry.value.totalPrice}</td>
					<td><a href="#">删除</a></td>
				</tr>
			</c:forEach>

购物车非空和空的情况

<%--购物车非空,输出下面的信息--%>
		<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>
		<c:if test="${empty sessionScope.cart.items}">
		<div class="cart_info">
			<td colspan="5">
				<a href="index.jsp">您还没有添加商品到购物车,去商城逛逛吧!!</a>
			</td>
		</div>
		</c:if>

8 删除购物车的商品

CartServlet,先设置cart的删除标签的跳转地址(执行删除商品项deleteItem方法)

					<td><a class="deleteItem" href="cartServlet?action=deleteItem&id=${entry.value.id}">删除</a></td>

//删除商品项
   protected void deleteItem(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
       //1 获取要删除的商品id
       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"));
       }
   }

添加点击响应事件,提示用户

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

9 清空购物车

cart.jsp

				<span class="cart_span"><a class="clearCart" href="cartServlet?action=clear">清空购物车</a></span>



	<script type="text/javascript">
            //给【清空购物车】添加点击事件
			$("a.clearCart").click(function () {
				confirm("你确定要清空购物车吗?");
            })
        })

	</script>

CartServlet

    //清空购物车
    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"));
        }
    }

10 修改购物车商品项的数量

将数量的表格修改为文本框

<td>
						<input class="updateCount"
							   style="width: 70px" type="text"
							   bookId="${entry.value.id}"
							   value="${entry.value.count}">
					</td>

添加文本框的内容改变触发函数change

<script type="text/javascript">
			//修改商品数量(onchange函数是当文本框里的值发生变化时出发的)
			$(".updateCount").change(function () {
			    //获取商品名称和数量
				var name = $(this).parent().parent().find("td:first").text();
			    var count = this.value;
			    //获取商品id
				var id = $(this).attr("bookId");

			    if(confirm("确定修改【"+name+"】的数量为 "+ count +" ;吗?")){
			        //发起请求,给服务器保存修改
					location.href = "${pageScope.basePath}cartServlet?action=updateCount&id="+id+"&count="+count;
				}else{
			        //this.defaultValue是输入框默认的value值.原来的值
			        this.value=this.defaultValue;
			        return false;
				}
           });</script>

CartServlet的updateCount方法

   //修改商品数量
   protected void updateCount(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
       //获取要修改的商品id和数量
       int id = WebUtils.parseInt(request.getParameter("id"), 0);
       int count = WebUtils.parseInt(request.getParameter("count"), 0);
       Cart cart = (Cart) request.getSession().getAttribute("cart");
       if (cart != null){
           cart.updateCount(id,count);
           response.sendRedirect(request.getHeader("Referer"));
       }
   }

11 首页购物车数据展示

将刚才添加的商品的信息和总数量显示在首页上,我们把它保存在Session域中

addItem()方法

//将最后一个添加的商品保存在Session域中
       request.getSession().setAttribute("lastName",item.getName());

client/index.jsp

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

第七阶段

1 订单模块

在这里插入图片描述

2 实现订单模块

数据库表

t_order数据库表的创建

#创建订单表
CREATE TABLE t_order(
	`order_id` VARCHAR(50) PRIMARY KEY,#订单编号
	`create_time` DATETIME,#创建时间
	`price` DECIMAL(11,2),#价格
	`status` INT,#状态
	`user_id` INT,#用户id
	FOREIGN KEY(`user_id`) REFERENCES t_user(`id`)
);

t_order_item表的创建

create table t_order_item(
	`id` int primary key auto_increment,#订单项id
	`name`  varchar(100),#订单项商品name
	`count` int,#订单项数量
	`price` decimal(11,2),#订单项价格
	`total_price` decimal(11,2),#订单项总价格
	`order_id` varchar(50),#订单编号
	foreign key(`order_id`) references t_order(`order_id`) 
);

数据模型Order和OrderItem

Order类

public class Order {

   private String orderId;
   private Date createTime;
   private BigDecimal price;

   private Integer status = 0;
   //0表示未发货,1表示已发货,2表示已签收

   private Integer userId;
}

OrderItem类

public class OrderItem {

   private Integer id;
   private String name;
   private Integer count;
   private BigDecimal price;
   private BigDecimal totalPrice;
   private String orderId;
}

DAO和测试

OrderDao接口

public interface OrderDao {

   int saveOrder(Order order);
}

OrderDaoImpl实现类

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());
   }
}

OrderDaoTest测试类

   @Test
   public void saveOrder() {
       OrderDao orderDao = new OrderDaoImpl();
       orderDao.saveOrder(new Order("2232425211",new Date(),new BigDecimal(100),0,1));
   }

OrderItemDao接口

public interface OrderItemDao {

   int saveOrderItem(OrderItem orderItem);
}

OrderItemDaoImpl实现类

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());
   }
}

OrderItemTest测试类

   @Test
   public void saveOrderItem() {
       OrderItemDao orderItemDao = new OrderItemDaoImpl();
       orderItemDao.saveOrderItem(new OrderItem(null,"java从入门到放弃",1 ,new BigDecimal(100),new BigDecimal(100),"2232425211"));
   }

Service和测试

OrderService接口

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

OrderServiceImpl实现类

public class OrderServiceImpl 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> entry : cart.getItems().entrySet()){
           //获取每一个购物车中的商品
           CartItem cartItem = entry.getValue();
           //转化为每一个订单
           OrderItem orderItem = new OrderItem(null,cartItem.getName(),cartItem.getCount(),cartItem.getPrice(),cartItem.getTotalPrice(),orderId);
           //保存订单到数据库
           orderItemDao.saveOrderItem(orderItem);
       }
       //生成订单之后,购物车要清空
       cart.clear();

       return orderId;
   }
}

OrderServiceTest测试类

   @Test
   public void createOrder() {
       Cart cart = new Cart();
       cart.addItem(new CartItem(1,"java从入门到放弃",1,new BigDecimal(50),new BigDecimal(50)));
       cart.addItem(new CartItem(1,"java从入门到放弃",1,new BigDecimal(50),new BigDecimal(50)));
       cart.addItem(new CartItem(2,"python实践",1,new BigDecimal(100),new BigDecimal(100)));

       OrderService orderService = new OrderServiceImpl();
       orderService.createOrder(cart,1);
   }

结账功能实现

OrderServlet程序(生成订单createOrder()方法)

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

        //订单信息必须要有用户的id,如果用户还没有登录,Session域中获取不到,那我们就要让他跳转到登录界面
        if (loginUser == null){
            //请求转发到登录页面
            request.getRequestDispatcher("/pages/user/login.jsp").forward(request,response);
            //下面的语句没有必要执行,所以返回
            return;
        }

        Integer userId = loginUser.getId();

        //创建订单之后返回订单号
        String orderId = orderService.createOrder(cart, userId);

        //将订单号保存到Session域中
        request.setAttribute("orderId",orderId);

        //请求转发到/pages/cart/checkout.jsp
        request.getRequestDispatcher("/pages/cart/checkout.jsp").forward(request,response);

    }
}

web.xml

   <servlet>
       <servlet-name>OrderServlet</servlet-name>
       <servlet-class>com.atgw.web.OrderServlet</servlet-class>
   </servlet>
   <servlet-mapping>
       <servlet-name>OrderServlet</servlet-name>
       <url-pattern>/orderServlet</url-pattern>
   </servlet-mapping>

cart.jsp

				<span class="cart_span"><a href="orderServlet?action=createOrder">去结账</a></span>

checkout.jsp

		<h1>你的订单已结算,订单号为:${requestScope.orderId}</h1>

解决生成订单的bug

bug1(避免表单重复提交)

createOrder()

//将订单号保存到Session域中
        //request.setAttribute("orderId",orderId);(使用重定向时,request域就获取不到数据)
        request.getSession().setAttribute("orderId",orderId);

        //请求转发到/pages/cart/checkout.jsp
        //request.getRequestDispatcher("/pages/cart/checkout.jsp").forward(request,response);
        //防止表单重复提交,要使用重定向
        response.sendRedirect(request.getContextPath()+"/pages/cart/checkout.jsp");
    }

checkout.jsp

		<h1>你的订单已结算,订单号为:${sessionScope.orderId}</h1>

bug2(提交订单之后,原来商品的销量和库存要改变)

OrderServiceImpl中createOrder()

           //更新库存和销量
           Book book = bookDao.queryBookById(cartItem.getId());
           book.setSales(book.getSales() + cartItem.getCount());
           book.setStock(book.getStock() - cartItem.getCount());
           bookDao.updateBook(book);

第八阶段

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

ManagerServlet中doFilter()方法

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

   }

   //权限检查
   @Override
   public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
       HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;

       //判断用户是否登录
       Object user = httpServletRequest.getSession().getAttribute("user");
       if(user == null){
           // 如果没有登录,请求转发到登录页面
           httpServletRequest.getRequestDispatcher("/pages/user/login.jsp").forward(servletRequest,servletResponse);
       }else{
           filterChain.doFilter(servletRequest,servletResponse);
       }
   }

   @Override
   public void destroy() {

   }
}

配置ManagerFilter过滤器

   <filter>
       <filter-name>ManagerFilter</filter-name>
       <filter-class>com.atgw.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 ThreadLocal的使用

它可以解决多线程的数据安全问题

ThreadLocal可以给当前线程关联一个数据(可以是普通变量,可以是对象,也可以是数组,集合)

特点

  1. ThreadLocal可以为当前线程关联一个数据(像Map一样)
  2. 每一个ThreadLocal对象,只能为当前线程关联一个数据,如果要为当前线程关联多个数据,就需要使用多个ThreadLocal对象实例
  3. 每个ThreadLocal对象实例定义的时候,一般都是static类型
  4. ThreadLocal中保存数据,在线程销毁后,会有JVM虚拟机自动释放

3 使用ThreadLocal来确保所有操作都使用一个Connection来处理事务

JDBC数据库事务的处理

Connection conn = JdbcUtils.getConnection();
try{
   conn.setAutoCommit(false);//设置为手动管理事务
   
   执行一系列的jdbc操作
       
   conn.commit;//手动提交事务
}catch(Exception e){
   conn.rollback();//回滚事务
}finally{
   JdbcUtils.close(conn);
}

在这里插入图片描述

改变JdbcUtils中获取连接的方法

//创建一个ThreadLocal对象,将一个数据库连接放在其中(保证事务的实现,连接的唯一)
   private static ThreadLocal<Connection> conns = new ThreadLocal<>();

获取连接

//获取连接
   //返回null说明获取连接失败
   public static Connection getConnection(){
       //从ThreadLocal对象conns中取出连接
       Connection conn = conns.get();
       if(conn == null){
           try {
               //如果没有,就从数据库连接池中获取
               conn = dataSource.getConnection();

               //保存在ThreadLocal对象conns中,供后面的Jdbc操作
               conns.set(conn);

               conn.setAutoCommit(false);//设置为手动管理,手动提交
           } catch (SQLException e) {
               e.printStackTrace();
           }
       }
       return conn;
   }

提交事务并关闭连接

//提交事务并关闭连接
   public static void commitAndClose(){
       Connection connection = conns.get();
       if (connection != null){//不等于null,说明之前使用过,操作过数据库
           try {
               connection.commit();//提交事务
           } catch (SQLException e) {
               e.printStackTrace();
           }finally {
               try {
                   connection.close();//关闭连接
               } catch (SQLException e) {
                   e.printStackTrace();
               }
           }
       }
       //一定要remove(),否则就会出错,(Tomcat服务器底层使用了线程池技术,不移除可以线程池会溢出)
       conns.remove();
   }

回滚事务并关闭连接

//回滚事务并关闭连接
   public static void rollbackAndClose(){
       Connection connection = conns.get();
       if (connection != null){//不等于null,说明之前使用过,操作过数据库
           try {
               connection.rollback();//提交事务
           } catch (SQLException e) {
               e.printStackTrace();
           }finally {
               try {
                   connection.close();//关闭连接
               } catch (SQLException e) {
                   e.printStackTrace();
               }
           }
       }
       //一定要remove(),否则就会出错,(Tomcat服务器底层使用了线程池技术,不移除可以线程池会溢出)
       conns.remove();
   }

在Dao层,我们不用关闭连接,因为后面和前面使用的是一个连接对象,我们只需要在这一层把所有方法的异常抛出

catch (SQLException e) {
           e.printStackTrace();
           throw new RuntimeException(e);
       }

OrderServlet程序

//创建订单之后返回订单号
        String orderId = null;
        try{
            orderId =  orderService.createOrder(cart, userId);

            JdbcUtils.commitAndClose();//没有异常,提交

        }catch (Exception e){
            JdbcUtils.rollbackAndClose();//有异常,回滚

            e.printStackTrace();
        }

4 使用Filter过滤器给所有Service方法加上try-catch来管理事务

在这里插入图片描述

TransactionFilter过滤器

  • 给 filterChain.doFilter()方法加上了try-catch,然后在web.xml中配置了拦截地址(所有请求),所以相当于对所有的Service方法加上了try-catch
  • 底层实现,把异常抛给外边的Filter程序,对异常进行处理(提交事务或回滚事务)
   @Override
   public 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();
       }
   }

配置web.xml(对所有请求都进行拦截)

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

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

错误404提示页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html>
<head>
   <meta charset="UTF-8">
   <title></title>

</head>
<body>
<h3>很抱歉,您访问的页面不存在或已经被删除!!</h3>
<a href="index.jsp">返回首页</a>
</body>
</html>

错误500提示页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html>
<head>
   <meta charset="UTF-8">
   <title></title>

</head>
<body>
<h3>很抱歉,您访问的后台程序出现了错误,程序员小哥,正在努力的为您抢修!!</h3>
<a href="index.jsp">返回首页</a>
</body>
</html>

web.xml文件配置错误提示页面

   <!-- error-page 标签配置,服务器出错之后,自动跳转到页面-->
   <error-page>
       <!-- 500是错误类型 -->
       <error-code>500</error-code>
       <!-- location 标签表示,要跳转的页面路径 -->
       <location>/pages/error/error500.jsp</location>
   </error-page>

   <error-page>
       <!-- 500是错误类型 -->
       <error-code>404</error-code>
       <!-- location 标签表示,要跳转的页面路径 -->
       <location>/pages/error/error404.jsp</location>
   </error-page>

第九阶段

使用AJAX验证表单信息

在这里插入图片描述

UserServlet程序

  1. 开始ajax程序发送请求给服务器,把用户名信息追加到了请求地址中
  2. 我们在Servlet程序中可以获取到请求地址的用户名参数,然后对其进行数据库的访问,判断这个用户名是否存在
  3. 并把其是否存在的信息封装到Map集合中,使用响应的字符输出流输出数据,也就是存在了data中
  4. 在页面上使用data获取结果信息,做出相应的提示信息
     //验证表单的用户名信息
    protected void ajaxExistsUsername(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
        //获取请求的参数username
        String username = request.getParameter("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);

        //响应的字符输出流输出数据
        response.getWriter().write(json);
    }

regist.jsp页面中,验证表单的信息

		<script type="text/javascript">
			$(function () {

			    //AJAX验证表单的用户名信息(失去焦点事件)
				$("#username").blur(function () {

				    //获取用户名
					var username = this.value;

					//把用户名信息添加到请求信息中,在Servlet程序中获取用户名,并检查在数据库中是否存在
					//服务器返回的数据类型data
					$.getJSON("http://localhost:8080/09_book/userServlet","action=ajaxExistsUsername&username="+username,function(data) {
					    //existsUsername是Servlet程序中封装到Map集合中的数据,发送到了服务器,data接收它
					    if(data.existsUsername){
                            $("span.errorMsg").text("用户名已存在");
                        }else{
                            $("span.errorMsg").text("用户名可用");
                        }
                    });
                });
            })
  </script>

使用AJAX把商品添加到购物车

在这里插入图片描述

CartServlet程序,添加购物车

  • 因为添加购物车时,页面需要对购物车中总的商品数量和最后一个添加的商品名做出显示,所以我们需要返回这两个参数
  • 使用Map集合存储,将数据发送给响应端
//使用AJAX添加购物车
   //添加购物车
   protected void ajaxAddItem(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

       //1 获取请求的参数,商品编号
       int id = WebUtils.parseInt(request.getParameter("id"),0);
       //2 调用bookServlet.queryBookById() 查询获取到图书的信息
       Book book = bookService.QueryBookById(id);
       //3 把图书信息,转换成CartItem商品项
       CartItem item = new CartItem(book.getId(), book.getName(), 1, book.getPrice(), book.getPrice());
       //4 调用Cart.addItem(CartItem) 添加商品项
       /*因为在添加购物车时,购物车只有一个,我们把它放在Session中
        * 刚开始添加购物车时,先判断Session中是否有购物车,如果没有,
        * 则新创建一个存放在Session域中 */
       Cart cart = (Cart) request.getSession().getAttribute("cart");
       if (cart == null){
           cart = new Cart();
           request.getSession().setAttribute("cart",cart);
       }
       cart.addItem(item);

       //将最后一个添加的商品保存在Session域中
       request.getSession().setAttribute("lastName",item.getName());

       //返回购物车总的商品数量和最后一个添加的商品名称
       Map<String,Object> resultMap = new HashMap<>();
       resultMap.put("totalCount",cart.getTotalCount());
       resultMap.put("lastName",item.getName());

       Gson gson = new Gson();
       String s = gson.toJson(resultMap);

       //响应发送数据
       res
           ponse.getWriter().write(s);
   }

client/index.jsp(对其进行重写,使用AJAX来显示页面提示信息)

			<div style="text-align: center">
				<%--购物车为空--%>
				<c:if test="${empty sessionScope.cart.items}">
                   <span id="cartTotalCount"></span>
					<div>
						<span style="color: red" id="cartLastName">您的购物车中还没有商品</span>
					</div>
				</c:if>
					<%--购物车不为空--%>
				<c:if test="${not empty sessionScope.cart.items}">
					<span id="cartTotalCount">您的购物车中有 ${sessionScope.cart.totalCount} 件商品</span>
					<div>
						您刚刚将<span style="color: red" id="cartLastName">${sessionScope.lastName}</span>加入到了购物车中
					</div>
				</c:if>
			</div>
  • 获取服务器响应的数据,保存在data中
  • 然后对其进行显示
   <script type="text/javascript">
       $(function () {
           // 给添加购物车按钮添加点击响应事件
           $("button.addToCart").click(function () {
               //跳转到购物车模块执行添加商品方法
               //获取点击的商品的id(bookId是button按钮的自定义属性)
               var bookId = $(this).attr("bookId");
               // location.href = "http://localhost:8080/09_book/cartServlet?action=addItem&id="+bookId;

				//使用AJAX来实现
				$.getJSON("http://localhost:8080/09_book/cartServlet","action=ajaxAddItem&id="+bookId,function (data) {
                   $("#cartTotalCount").text("您的购物车中有"+ data.totalCount +" 件商品");
                   $("#cartLastName").text(data.lastName);
               })
           })
       })
   </script>
  • 6
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值