JavaWeb书城项目(一)

书城项目(一)

  • 1、表单验证的实现
    • 代码
  • 2、用户注册和登陆
    • JavaEE 项目的三层架构
    • 2.1、数据库层
      • 2.1.1、创建数据库
      • 2.1.2、定义 JavaBean 类
      • 2.1.3、编写工具类 JdbcUtils
    • 2.2、DAO 层
      • 2.2.1、编写 BaseDAO父类
      • 2.2.2、编写 UserDAO接口
      • 2.2.3、编写 UserDAOImpl
    • 2.3、Service 层
      • 2.3.1、编写 UserService接口
      • 2.3.2、编写 UesrServiceImpl
    • 2.4、Web 层
      • 2.4.1、注册servlet
      • 2.4.2、登录servlet
    • 2.5、html文件
    • 2.6、如果使用相对路径
  • 3、优化页面
    • 3.1、页面 jsp 动态化
    • 3.2、抽取出页面相同部分
      • 3.2.1、登录成功后的菜单
      • 3.2.2、每个页面的页脚
      • 3.2.3、manager 模块的菜单
      • 3.2.4、head 中 css、jquery、base 标签
        • 动态 base 标签
    • 3.3、表单回显(注册或登录失败的提示)
    • 3.4、BaseServlet 的抽取
      • 3.4.1、代码优化一:合并 Servlet 程序为 UserServlet 程序
      • 3.4.2、代码优化二:使用反射优化大量 else if 代码
      • 3.4.3、代码优化三:抽取 BaseServlet 程序
    • 3.5、编写 WebUtils 工具类封装数据
      • 演示BeanUtils类方法使用
      • 封装为 WebUtils 工具类
  • 4、图书模块
    • MVC 概念
    • 4.1、编写图书模块的数据库表
    • 4.2、编写图书模块的 JavaBean
    • 4.3、编写图书模块的 Dao 和测试 Dao
      • 4.3.1、编写 UserDAO接口
      • 4.3.2、编写 BookDAOImpl
    • 4.4、编写图书模块的 Service 和测试 Service
      • 4.4.1、编写 BookService 接口
      • 4.4.2、编写 BookServiceImpl
    • 4.5、编写图书模块的 Web 层,和页面联调测试
      • 4.5.1、图书列表功能的实现
      • 4.5.2、添加图书功能的实现
      • 4.5.3、删除图书功能的实现
      • 4.5.4、修改图书功能的实现
        • 表单回显操作
        • 修改图书操作

1、表单验证的实现

JavaWeb书城项目(一)_第1张图片
现将文件拷贝进新的模块

然后完成以下需求

JavaWeb书城项目(一)_第2张图片
字母、数字、下划线组成,且长度是5-12,
由正则表达式表示为

var patt = /^\w{5,12}$/;
if(patt.test(usernameText)){
	alert("用户名合法!");
}else {
	alert("用户名不合法!");
}

邮箱部分也需要由正则表达式表示,可查看jQuery文档的 “其他” 里面

代码

<head>
<meta charset="UTF-8">
<title>尚硅谷会员注册页面title>
<link type="text/css" rel="stylesheet" href="../../static/css/style.css" >
<style type="text/css">
	.login_form{
		height:420px;
		margin-top: 25px;
	}
style>
	<script type="text/javascript" src="../../static/script/jquery-1.7.2.js">script>
	<script type="text/javascript">
		$(function () {
			$("#sub_btn").click(function () {
				//验证用户名
				var patt = /^\w{5,12}$/;
				var usernameText = $("#username").val();
				if(!patt.test(usernameText)) {
					$("span.errorMsg").text("用户名不合法")
					return false
				}
				//验证密码
				var passwordText = $("#password").val();
				if(!patt.test(passwordText)) {
					$("span.errorMsg").text("密码不合法")
					return false
				}
				//验证确认密码
				var repwdText = $("#repwd").val();
				if(!(repwdText == passwordText)){
					$("span.errorMsg").text("密码不一致")
					return false
				}
				//邮箱验证
				var emailText = $("#email").val();
				var emailpatt = /^[a-z\d]+(\.[a-z\d]+)*@([\da-z](-[\da-z])?)+(\.{1,2}[a-z]+)+$/;
				if(!emailpatt.test(emailText)) {
					$("span.errorMsg").text("邮箱不合法")
					return false
				}
				//验证码
				var codeText = $("#code").val();
				//注意去掉前后空格
				var trim = $.trim(codeText);
				if(trim == null || trim == "") {
					$("span.errorMsg").text("验证码为空")
					return false
				}
				//注意当合法注册的时候注意需要清空提示的span里面的文本
				$("span.errorMsg").text("")
			})
		})
	script>
head>
<body>
		<div id="login_header">
			<img class="logo_img" alt="" src="../../static/img/logo.gif" >
		div>
		
			<div class="login_banner">
			
				<div id="l_content">
					<span class="login_word">欢迎注册span>
				div>
				
				<div id="content">
					<div class="login_form">
						<div class="login_box">
							<div class="tit">
								<h1>注册尚硅谷会员h1>
								<span class="errorMsg">span>
							div>
							<div class="form">
								<form action="regist_success.html">
									<label>用户名称:label>
									<input class="itxt" type="text" placeholder="请输入用户名" autocomplete="off" tabindex="1" name="username" id="username" />
									<br />
									<br />
									<label>用户密码:label>
									<input class="itxt" type="password" placeholder="请输入密码" autocomplete="off" tabindex="1" name="password" id="password" />
									<br />
									<br />
									<label>确认密码:label>
									<input class="itxt" type="password" placeholder="确认密码" autocomplete="off" tabindex="1" name="repwd" id="repwd" />
									<br />
									<br />
									<label>电子邮件:label>
									<input class="itxt" type="text" placeholder="请输入邮箱地址" autocomplete="off" tabindex="1" name="email" id="email" />
									<br />
									<br />
									<label>验证码:label>
									<input class="itxt" type="text" style="width: 150px;" id="code"/>
									<img alt="" src="../../static/img/code.bmp" style="float: right; margin-right: 40px">									
									<br />
									<br />
									<input type="submit" value="注册" id="sub_btn" />
									
								form>
							div>
							
						div>
					div>
				div>
			div>
		<div id="bottom">
			<span>
				尚硅谷书城.Copyright ©2015
			span>
		div>
body>

2、用户注册和登陆

注意点:

  1. 在数据库连接操作的时候,导入properties配置文件的时候,不可以使用系统类加载器ClassLoader.getSystemResourceAsStream
  2. 在注册和登录的 html 页面中,表单提交的链接部分,链接填写的是提供的 Servlet 程序的链接,可以写相对路径和绝对路径
    1. 绝对路径:后续若使用请求中转会无法使用样式等文件(参考Servlet文章)
    2. 相对路径:需要写 base 标签,该页面内所有的相对路径都是相对于base标签中的路径,则页面的css样式以及 jquery的配置文件的路径全部要进行更改。
      跳转页面的时候可使用请求中转

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

需求 2:用户登陆
1)访问登陆页面
2)填写用户名密码后提交
3)服务器判断用户是否存在
4)如果登陆失败 —>>>> 返回用户名或者密码错误信息
5)如果登录成功 —>>>> 返回登陆成功信息

JavaEE 项目的三层架构

JavaWeb书城项目(一)_第3张图片
分层的目的是为了解耦。解耦就是为了降低代码的耦合度。方便项目后期的维护和升级

JavaWeb书城项目(一)_第4张图片

2.1、数据库层

第三方 jar 包放在 web 目录的 WEB-INF 文件夹下
JavaWeb书城项目(一)_第5张图片

2.1.1、创建数据库

数据库存放用户注册的数据
JavaWeb书城项目(一)_第6张图片

2.1.2、定义 JavaBean 类

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

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

2.1.3、编写工具类 JdbcUtils

使用 Druid 数据库连接池,导入 jar 包

在 src 源码目录下编写 jdbc.properties 属性配置文件
JavaWeb书城项目(一)_第7张图片
工具类 JdbcUtils 中封装数据库链接和关闭操作

注意,在连接操作中,如果使用ClassLoader.getSystemResourceAsStream(“文件名”),在后面项目运行的时候会报空指针异常

要使用jdbcUtils.class.getClassLoader().getResourceAsStream(“druid.properties”)

package com.atguigu.utils;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.alibaba.druid.pool.DruidPooledConnection;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

public class jdbcUtils {
    private static DruidDataSource dataSource;
    static {
        try {
            Properties pro = new Properties();
            pro.load(jdbcUtils.class.getClassLoader().getResourceAsStream("druid.properties"));
            dataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(pro);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    //获取连接(注意获取连接的操作不允许抛异常)
    //@return 如果返回null,说明获取连接失败
    public static Connection getconn(){

        DruidPooledConnection connection = null;
        try {
            connection = dataSource.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return  connection;
    }
    //关闭连接
    public static void close(Connection conn){
        if(conn != null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

如果获取了数据库连接池的连接没有及时释放,就只能获取最大连接数的连接
JavaWeb书城项目(一)_第8张图片

但是如果及时的释放了连接,那获取几个连接都没有问题

JavaWeb书城项目(一)_第9张图片

2.2、DAO 层

2.2.1、编写 BaseDAO父类

package com.atguigu.DAO;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;

public class BaseDAO<T> {
    private Class<T> clazz = null;
    {
        ParameterizedType parameterized = (ParameterizedType) this.getClass().getGenericSuperclass();
        Type[] type = parameterized.getActualTypeArguments();
        clazz = (Class<T>) type[0];
    }
    private QueryRunner qr = new QueryRunner();
    //通用的增删改操作
    public void update(Connection conn,String sql,Object...args){
        try {
            int i = qr.update(conn, sql, args);
            System.out.println("影响了" + i + "条数据");
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    //通用的查询一条数据操作
    public T queryForOne(Connection conn,String sql,Object...args){
        T t = null;
        try {
            t = qr.query(conn,sql,new BeanHandler<T>(clazz),args);
            return t;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return t;
    }
    //通用的查询多条数据操作
    public List<T> queryForList(Connection conn,String sql,Object...args){
        List<T> list = null;
        try {
            list = qr.query(conn, sql, new BeanListHandler<T>(clazz), args);
            return list;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return list;
    }
    //通用的查询特殊值的操作
    public Object queryForSingleValue(Connection conn,String sql,Object...args){
        Object o = null;
        try {
            o = qr.query(conn, sql, new ScalarHandler(), args);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return o;
    }
}

2.2.2、编写 UserDAO接口

package com.atguigu.DAO;

import com.atguigu.bean.User;
import java.sql.Connection;

public interface UserDAO {
    //根据用户名查询用户信息
    User queryUserByUsername(Connection connection, String username);
    //根据用户名和密码查询用户信息
    User queryUserByUsernameAndPassword(Connection connection,String username,String password);
    //保存用户信息(就是插一条用户信息数据)
    void saveUser(Connection connection,User user);
}

2.2.3、编写 UserDAOImpl

package com.atguigu.DAO.impl;

import com.atguigu.DAO.BaseDAO;
import com.atguigu.DAO.UserDAO;
import com.atguigu.bean.User;
import java.sql.Connection;

public class UserDAOImpl extends BaseDAO<User> implements UserDAO {
    @Override
    public User queryUserByUsername(Connection connection, String username) {
        String sql = "select * from `t_user` where username = ?";
        User user = queryForOne(connection, sql, username);
        return user;
    }
    @Override
    public User queryUserByUsernameAndPassword(Connection connection, String username, String password) {
        String sql = "select * from `t_user` where username = ? and password = ?";
        User user = queryForOne(connection, sql, username, password);
        return user;
    }
    @Override
    public void saveUser(Connection connection, User user) {
        String sql = "insert into `t_user`(`id`,`username`,`password`,`email`) values(?,?,?,?)";
        update(connection,sql,user.getId(),user.getUsername(),user.getPassword(),user.getEmail());
    }
}

2.3、Service 层

该层编写的是具体的业务

比如,登录、注册、验证用户名是否重复

2.3.1、编写 UserService接口

package com.atguigu.service;

import com.atguigu.bean.User;

public interface UserService {
    //这里是业务层
    //注册业务
    void registerUser(User user);
    //登录业务
    User login(User user);
    //检查用户名是否已经存在,若已存在,说明不可用
    boolean existsUsername(String username);
}

2.3.2、编写 UesrServiceImpl

package com.atguigu.service.impm;

import com.atguigu.DAO.UserDAO;
import com.atguigu.DAO.impl.UserDAOImpl;
import com.atguigu.bean.User;
import com.atguigu.service.UserService;
import com.atguigu.utils.jdbcUtils;
import java.sql.Connection;

public class UesrServiceImpl implements UserService {
    
    private UserDAO userDAO = new UserDAOImpl();
    
    @Override
    public void registerUser(User user) {
        Connection conn = jdbcUtils.getconn();
        userDAO.saveUser(conn,user);
        jdbcUtils.close(conn);
    }
    @Override
    public User login(User user) {
        Connection conn = jdbcUtils.getconn();
        User user1 = userDAO.queryUserByUsernameAndPassword(conn, user.getUsername(), user.getPassword());
        jdbcUtils.close(conn);
        return user1;
    }
    @Override
    public boolean existsUsername(String username) {
        Connection conn = jdbcUtils.getconn();
        User user = userDAO.queryUserByUsername(conn, username);
        jdbcUtils.close(conn);
        return user != null;
    }
}

2.4、Web 层

web.xml 文件中的配置

    <servlet>
        <servlet-name>RegistServletservlet-name>
        <servlet-class>com.atguigu.web.RegistServletservlet-class>
    servlet>
    <servlet>
        <servlet-name>LoginServletservlet-name>
        <servlet-class>com.atguigu.web.LoginServletservlet-class>
    servlet>
    <servlet-mapping>
        <servlet-name>RegistServletservlet-name>
        <url-pattern>/registServleturl-pattern>
    servlet-mapping>
    <servlet-mapping>
        <servlet-name>LoginServletservlet-name>
        <url-pattern>/loginServleturl-pattern>
    servlet-mapping>

2.4.1、注册servlet

选择 POST 请求方式,是因为在转入网址之后,不会显现密码的内容

注册成功或者注册失败的时候,跳转页面使用的是 响应的请求重定向

package com.atguigu.web;

import com.atguigu.bean.User;
import com.atguigu.service.impm.UesrServiceImpl;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class RegistServlet extends HttpServlet {
    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");
        //验证验证码是否正确
        //本案例中将验证码写死,写成固定的字段abc,忽略大小写
        if("abc".equalsIgnoreCase(code)){
            //检查用户名是否可用
            UesrServiceImpl uesrService = new UesrServiceImpl();
            if(uesrService.existsUsername(username)){
                System.out.println("用户名已存在");
                response.sendRedirect("http://localhost:8080/book02/pages/user/regist.html");
            }else {
                //执行注册功能,保存数据
                User user = new User(username, password, email);
                uesrService.registerUser(user);
                //跳转到注册成功页面
                response.sendRedirect("http://localhost:8080/book02/pages/user/regist_success.html");
            }
        }else {
            System.out.println("验证码不正确");
            response.sendRedirect("http://localhost:8080/book02/pages/user/regist.html");
        }
    }
}

2.4.2、登录servlet

登录成功或者登录失败的时候,跳转页面使用的是 响应的请求重定向

package com.atguigu.web;

import com.atguigu.bean.User;
import com.atguigu.service.impm.UesrServiceImpl;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class LoginServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取请求的参数
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        UesrServiceImpl uesrService = new UesrServiceImpl();
        if(uesrService.login(new User(username,password,null)) != null){
            response.sendRedirect("http://localhost:8080/book02/pages/user/login_success.html");
        }else {
            System.out.println("登录失败");
            response.sendRedirect("http://localhost:8080/book02/pages/user/login.html");
        }
    }
}

2.5、html文件

登录页面的 html 文件中表单的提交链接

<form action="http://localhost:8080/book02/loginServlet" method="post">

注册页面的 html 文件中表单的提交链接

<form action="http://localhost:8080/book02/registServlet" method="post">

2.6、如果使用相对路径

  1. 添加 base 标签

 <base href="http://localhost:8080/book/">
  1. 修改 base 标签对页面中所有相对路径的影响(浏览器 F12,哪个报红,改哪个)
    以下是几个修改的示例:
 <link type="text/css" rel="stylesheet" href="static/css/style.css" > 
 <script type="text/javascript" src="static/script/jquery-1.7.2.js">
 script>
  1. 修改 login.html 表单的提交地址和请求方式
    JavaWeb书城项目(一)_第10张图片

3、优化页面

前面两个阶段已经处理好了注册页面和登录页面,但是注册、登陆失败的时候仅仅是返回注册页面、登录页面,没有动态的提示

第三部分是优化前面已经做好的部分,使其动态化

注意:
当进行到第三部分的时候,已经将前面第二部分涉及到的绝对路径,都更改为相对路径,并在每个 html 文件中设置了 base 标签,标签地址指向工程路径 http://localhost:8080/book02/

3.1、页面 jsp 动态化

1、在 html 页面顶行添加 page 指令
2、修改文件后缀名为:.jsp
3、使用 IDEA 搜索替换,将文件内部涉及到路径的地方,.html 为.jsp

JavaWeb书城项目(一)_第11张图片

JavaWeb书城项目(一)_第12张图片

3.2、抽取出页面相同部分

3.2.1、登录成功后的菜单

<div>
    <span>欢迎<span class="um_span">小草莓span>光临尚硅谷书城span>
    <a href="pages/order/order.jsp">我的订单a>
    <a href="index.jsp">注销a>  
    <a href="index.jsp">返回a>
div>

JavaWeb书城项目(一)_第13张图片

创建新文件,将上面相同的内容放进新文件
JavaWeb书城项目(一)_第14张图片
然后将原有文件中,内容的位置替换为如下:

<%--动态包含,登录成功后的欢迎菜单页面--%>
<jsp:include page="/pages/common/welcome_menu.jsp">jsp:include>

3.2.2、每个页面的页脚

相同内容如下:

<div id="bottom">
		<span>
			小草莓书城.LittleStrawberry ©2021
		span>
div>

然后将原有文件中,内容的位置替换为如下:

<%--动态包含,每个页面的页脚--%>
<jsp:include page="/pages/common/footer.jsp">jsp:include>

3.2.3、manager 模块的菜单

相同内容如下:

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

然后将原有文件中,内容的位置替换为如下:

<%--动态包含,manager 模块的菜单--%>
<jsp:include page="/pages/common/manager_menu.jsp">jsp:include>

3.2.4、head 中 css、jquery、base 标签

相同内容如下:

<base href="http://localhost:8080/book02/">
<link type="text/css" rel="stylesheet" href="static/css/style.css" >
<script type="text/javascript" src="static/script/jquery-1.7.2.js">script>

然后将原有文件中,内容的位置替换为如下:

<%--动态包含,head 中 css样式、jquery文件、base标签--%>
<jsp:include page="/pages/common/css_base_jquery.jsp">jsp:include>

动态 base 标签

此时要注意 动态 base 标签 的问题,如果直接将 上面 localhost 的地址放进 jsp 文件,那么在换一个 IP 访问项目的时候,就会有下面问题

JavaWeb书城项目(一)_第15张图片
比如换同一个网络下的手机进行访问该网页,会报错
JavaWeb书城项目(一)_第16张图片
更改如下:
以下是文件 css_base_jquery.jsp 的内容

<body>
<%
    //request.getScheme() 获取协议名称:http
    //request.getServerName() 获取服务器ip或域名
    //request.getServerPort() 获取服务器端口号
    //request.getContextPath() 获取当前工程路径
    String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+request.getContextPath()+"/";
%>

<%--将base标签的地址指向动态的地址--%>
<base href="<%=basePath%>">

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

更改之后,使用同一网段内的手机访问网址也可以显示出项目内容
JavaWeb书城项目(一)_第17张图片

3.3、表单回显(注册或登录失败的提示)

JavaWeb书城项目(一)_第18张图片

在第一部分表单验证的实现里面,仅仅实现了注册页面输入内容的提示

现在需要的是注册或登录时候提示的错误
如:登录时候用户或密码不正确,注册时候验证码不正确或用户名已存在

需求:

  1. 当用户登录失败的时候,在登录 login.jsp 文件里面的 span class=“errorMsg” 标签里面显示错误信息
  2. 并且在登录失败跳回登录页面的时候,输入框里面已经输入过的用户信息仍然显示在这里
  3. 以上两点类似出现在注册页面

代码:

此时需要将用户已经输入过的信息保存在 request 对象的域值中,将错误信息也保存到 请求域对象的域值当中,然后在 注册页面获取域值即可

前面在 Web 层里面的 servlet 程序里面,跳转页面使用的是请求重定向,但是请求重定向是两次请求,会清空请求域对象里面的阈值

所以现将请求重定向操作更改为请求中转,这样页面跳转的时候是一次请求,就能在跳转之后仍然获取到请求域对象保存的域值

下面是修改后 注册servlet 的代码

public class RegistServlet extends HttpServlet {
    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");
        //验证验证码是否正确
        //本案例中将验证码写死,写成固定的字段abc,忽略大小写
        if("abc".equalsIgnoreCase(code)){
            //检查用户名是否可用
            UesrServiceImpl uesrService = new UesrServiceImpl();
            if(uesrService.existsUsername(username)){
                //保存已经输入的用户信息,保存到请求对象的域值
                request.setAttribute("registUsername",username);
                request.setAttribute("registPassword",password);
                request.setAttribute("registEmail",email);
                request.setAttribute("registCode",code);
                request.setAttribute("registMsg","用户名已存在");
                //跳回注册页面
                request.getRequestDispatcher("/pages/user/regist.jsp").forward(request,response);
            }else {
                //执行注册功能,保存数据
                User user = new User(username, password, email);
                uesrService.registerUser(user);
                //跳转到注册成功页面
                request.getRequestDispatcher("/pages/user/regist_success.jsp").forward(request,response);
            }
        }else {
            //保存已经输入的用户信息,保存到请求对象的域值
            request.setAttribute("registUsername",username);
            request.setAttribute("registPassword",password);
            request.setAttribute("registEmail",email);
            request.setAttribute("registCode",code);
            request.setAttribute("registMsg","验证码不正确");
            //跳回注册页面
            request.getRequestDispatcher("/pages/user/regist.jsp").forward(request,response);
        }
    }
}

注册页面regist.jsp 获取请求域对象的域值,如下:
JavaWeb书城项目(一)_第19张图片
在每个输入框内设置 value 属性值,这样在跳转回到注册页面的时候就会显示已经输入过的信息

在这里插入图片描述
span 标签没有 value 属性,所以错误信息的获取,就是在 span 前后标签之间获取域值

此处需要注意一点:
上图中使用的是 EL 表达式,因为 EL 表达式输出 null 值的时候,输出的是空串

如果使用 jsp 语句,那么输出 null 值的时候,输出的是null
即在第一次访问注册页面时候,此时还没有设置请求域对象的域值,所以获取到的就是null,那么这里就要加一个判断语句,以防在第一次访问的时候输出 null
<%=request.getAttribute("registUsername")==null?"":request.getAttribute("registUsername")%>

同理,更改登录页面

下面是修改后 登录servlet 的代码

public class LoginServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取请求的参数
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        UesrServiceImpl uesrService = new UesrServiceImpl();
        if(uesrService.login(new User(username,password,null)) != null){
            request.setAttribute("loginUsername",username);
            request.setAttribute("loginPassword",password);
            //跳转到登录成功页面
            request.getRequestDispatcher("/pages/user/login_success.jsp").forward(request,response);
        }else {
            request.setAttribute("loginUsername",username);
            request.setAttribute("loginPassword",password);
            request.setAttribute("loginMsg","用户名或密码不正确");
            //跳回登录页面
            request.getRequestDispatcher("/pages/user/login.jsp").forward(request,response);
        }
    }
}

登录页面login.jsp 获取请求域对象的域值,如下:

JavaWeb书城项目(一)_第20张图片

注意,因为在首次访问登录页面的时候,提示的位置有文字,所以错误提示的地方还是需要进行一次判断
JavaWeb书城项目(一)_第21张图片
使用 jsp 语句的情况

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

使用 EL 表达式的情况

<span class="errorMsg">
	${empty requestScope.registMsg?"请输入用户名和密码":requestScope.registMsg }
span>

3.4、BaseServlet 的抽取

JavaWeb书城项目(一)_第22张图片

3.4.1、代码优化一:合并 Servlet 程序为 UserServlet 程序

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

所以要合并 LoginServlet 和 RegistServlet 程序为 UserServlet 程序

思路:
因为要处理两个页面的数据,所以不妨在 登录页面、注册页面 里面设置隐藏域,设置不同的值
在表单提交到 UserServlet 程序的时候, UserServlet 程序判断是哪个页面发送的请求,然后根据不同的请求来处理不同的业务

可以将请求的业务封装到自定义方法中,然后在 if 语句中调用方法

UserServlet 的代码
这里将封装的两个方法的方法名设置为 隐藏域 value 值一致,方便后续讲解反射获取方法名

public class UserServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        if ("login".equals(request.getParameter("postName"))){
            login(request,response);
        }else if("regist".equals(request.getParameter("postName"))){
            regist(request,response);
        }
    }
    protected void login(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取请求的参数
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        UesrServiceImpl uesrService = new UesrServiceImpl();
        if(uesrService.login(new User(username,password,null)) != null){
            request.setAttribute("loginUsername",username);
            request.setAttribute("loginPassword",password);
            //跳转到登录成功页面
            request.getRequestDispatcher("/pages/user/login_success.jsp").forward(request,response);
        }else {
            request.setAttribute("loginUsername",username);
            request.setAttribute("loginPassword",password);
            request.setAttribute("loginMsg","用户名或密码不正确");
            //跳回登录页面
            request.getRequestDispatcher("/pages/user/login.jsp").forward(request,response);
        }
    }
    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");
        //验证验证码是否正确
        //本案例中将验证码写死,写成固定的字段abc,忽略大小写
        if("abc".equalsIgnoreCase(code)){
            //检查用户名是否可用
            UesrServiceImpl uesrService = new UesrServiceImpl();
            if(uesrService.existsUsername(username)){
                //保存已经输入的用户信息,保存到请求对象的域值
                request.setAttribute("registUsername",username);
                request.setAttribute("registPassword",password);
                request.setAttribute("registEmail",email);
                request.setAttribute("registCode",code);
                request.setAttribute("registMsg","用户名已存在");
                //跳回注册页面
                request.getRequestDispatcher("/pages/user/regist.jsp").forward(request,response);
            }else {
                //执行注册功能,保存数据
                User user = new User(username, password, email);
                uesrService.registerUser(user);
                //跳转到注册成功页面
                request.getRequestDispatcher("/pages/user/regist_success.jsp").forward(request,response);
            }
        }else {
            //保存已经输入的用户信息,保存到请求对象的域值
            request.setAttribute("registUsername",username);
            request.setAttribute("registPassword",password);
            request.setAttribute("registEmail",email);
            request.setAttribute("registCode",code);
            request.setAttribute("registMsg","验证码不正确");
            //跳回注册页面
            request.getRequestDispatcher("/pages/user/regist.jsp").forward(request,response);
        }
    }
}

登录页面、注册页面 修改如下:

添加隐藏域 ,和修改请求地址
JavaWeb书城项目(一)_第23张图片
在这里插入图片描述

3.4.2、代码优化二:使用反射优化大量 else if 代码

在 UserServlet 程序中,是使用 if 语句进行判断用户在访问哪种功能页面,而实际开发中,用户可以访问的不仅仅只有注册和登录两个功能页面,可能还会有注销、修改、新增 等等多个页面,此时再使用 if 语句,就比较忙麻烦

使用反射调用运行时类的指定方法(将隐藏域的value值设置为servlet程序内封装的方法名一致,就可以获取到指定方法名)

以下代码是将 UserServlet 程序里面的 doPost()方法进行优化升级,用反射取代了 if 语句

public class UserServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        try {
            this.getClass().getDeclaredMethod(request.getParameter("postName"),HttpServletRequest.class,HttpServletResponse.class).invoke(this,request,response);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

3.4.3、代码优化三:抽取 BaseServlet 程序

JavaWeb书城项目(一)_第24张图片
BaseServlet 程序代码:
继承HttpServlet

将 BaseServlet 类设置为抽象的是防止其实例化,同时也可以加大复用程度

不需要配置BaseServlet在web.xml里面的servlet程序,因为我们根本不去访问这个servlet程序

public abstract class BaseServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        try {
            this.getClass().getDeclaredMethod(request.getParameter("postName"),HttpServletRequest.class,HttpServletResponse.class).invoke(this,request,response);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

修改 UserServlet 程序继承 BaseServlet 程序

在程序运行的时候,访问的是子类 UserServlet 程序,调用子类的 doPost() 方法,但是此时子类 UserServlet 程序里面没有重写此方法,那么就会向上去执行父类中重写的此方法

public class UserServlet extends BaseServlet {
	//里面只有两个封装的方法
	protected void login(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {}
	protected void regist(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {}
}

JavaWeb书城项目(一)_第25张图片

3.5、编写 WebUtils 工具类封装数据

BeanUtils 工具类,经常用于 把 Map 中的值注入到 JavaBean 中 ,或者是对象属性值的拷贝操作

因为获取请求参数有个返回 Map 的方法,所以可以使用 BeanUtils 工具类,一次性的把所有请求的参数注入到 JavaBean 中

BeanUtils 它不是 Jdk 的类。而是第三方的工具类。所以需要导包。

  1. 导入需要的 jar 包
    commons-beanutils-1.8.0.jar
    commons-logging-1.1.1.jar
  2. 编写 WebUtils 工具类使用

演示BeanUtils类方法使用

在登陆的方法里调用 BeanUtils 类的方法

BeanUtils.populate(Object bean, Map properties)
第一个参数就是要装载数据的 JavaBean 对象,第二个参数是要使用的数据,是 Map 类型

数据的输入不是根据对象的属性名,而是根据对象中创建的 set()方法

所以请求参数的时候,表单输入框的(name)属性要和 JavaBean 对象的(setXxx)方法一致,否则就无法将数据封装进对象

类似情况还出现在 EL 表达式输出 Bean 的属性

//判断是否登陆成功,是将用户请求的参数封装进User对象
//使用 BeanUtils 代替单个的请求参数并使用构造器
User user = new User();
BeanUtils.populate(user,request.getParameterMap());
System.out.println(user);

在这里插入图片描述

封装为 WebUtils 工具类

因为此方法会在多处使用,为了方便维护,抽取为 WebUtils 工具类

public class WebUtils {
    /**
     * @param t 传入对象t
     * @param map 将键值对的数据封装进对象t
     * @param 
     * @return 返回满载数据的t
     */
    public static <T> T mapToBean(T t, Map map){
        try {
            BeanUtils.populate(t, map);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return t;
    }
}

使用工具类

这样就算有多个请求参数,也只需要用一行代码就可以将所有参数封装进 JavaBean 对象

//判断是否登陆成功,需要将用户请求的参数封装进User对象
//使用 BeanUtils 代替单个的请求参数和使用构造器
User user = WebUtils.mapToBean(new User(), request.getParameterMap());

使用工具类,对代码进行优化
更改了 UserServlet 类

public class UserServlet extends BaseServlet {
    protected void login(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        UesrServiceImpl uesrService = new UesrServiceImpl();
        //判断是否登陆成功,需要将用户请求的参数封装进User对象
        //使用 BeanUtils 代替单个的请求参数并使用构造器
        //获取请求的参数,并封装进对象
        User user = WebUtils.mapToBean(new User(), request.getParameterMap());
        if(uesrService.login(user) != null){
            request.setAttribute("loginUsername",user.getUsername());
            request.setAttribute("loginPassword",user.getPassword());
            //跳转到登录成功页面
            request.getRequestDispatcher("/pages/user/login_success.jsp").forward(request,response);
        }else {
            request.setAttribute("loginUsername",user.getUsername());
            request.setAttribute("loginPassword",user.getPassword());
            request.setAttribute("loginMsg","用户名或密码不正确");
            //跳回登录页面
            request.getRequestDispatcher("/pages/user/login.jsp").forward(request,response);
        }
    }
    protected void regist(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取请求的参数,并封装进对象
        User user1 = WebUtils.mapToBean(new User(), request.getParameterMap());
        String code = request.getParameter("code");
        //验证验证码是否正确
        //本案例中将验证码写死,写成固定的字段abc,忽略大小写
        if("abc".equalsIgnoreCase(code)){
            //检查用户名是否可用
            UesrServiceImpl uesrService = new UesrServiceImpl();
            if(uesrService.existsUsername(user1.getUsername())){
                //保存已经输入的用户信息,保存到请求对象的域值
                request.setAttribute("registUsername",user1.getUsername());
                request.setAttribute("registPassword",user1.getPassword());
                request.setAttribute("registEmail",user1.getEmail());
                request.setAttribute("registCode",code);
                request.setAttribute("registMsg","用户名已存在");
                //跳回注册页面
                request.getRequestDispatcher("/pages/user/regist.jsp").forward(request,response);
            }else {
                //执行注册功能,保存数据
                uesrService.registerUser(user1);
                //跳转到注册成功页面
                request.getRequestDispatcher("/pages/user/regist_success.jsp").forward(request,response);
            }
        }else {
            //保存已经输入的用户信息,保存到请求对象的域值
            request.setAttribute("registUsername",user1.getUsername());
            request.setAttribute("registPassword",user1.getPassword());
            request.setAttribute("registEmail",user1.getEmail());
            request.setAttribute("registCode",code);
            request.setAttribute("registMsg","验证码不正确");
            //跳回注册页面
            request.getRequestDispatcher("/pages/user/regist.jsp").forward(request,response);
        }
    }
}

4、图书模块

JavaWeb书城项目(一)_第26张图片

MVC 概念

MVC 全称:Model 模型、 View 视图、 Controller 控制器

是 Web 层的分层

  1. View 视图:
    只负责数据和界面的显示,不接受任何与显示数据无关的代码,便于程序员和美工的分工合作—— JSP/HTML
  2. Controller 控制器:
    只负责接收请求,调用业务层的代码处理请求,然后派发页面,是一个“调度者”的角色——Servlet。 转到某个页面。或者是重定向到某个页面
  3. Model 模型:
    将与业务逻辑相关的数据封装为具体的 JavaBean 类,其中不掺杂任何与数据处理相关的代码—— JavaBean / domain / entity / pojo

JavaWeb书城项目(一)_第27张图片

4.1、编写图书模块的数据库表

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

4.2、编写图书模块的 JavaBean

public class Book {
    private int id;
    private String name;
    private double price;
    private String author;
    private int sales;
    private int stock;
    //设置图片的默认值
    private String img_path = "static/img/default.jpg";
}

注意,图片是有默认值的,那么在赋值的操作中可以判断一下

JavaWeb书城项目(一)_第28张图片

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

4.3.1、编写 UserDAO接口

public interface BookDAO {
    //插入图书信息数据
    void addBook(Connection connection, Book book);
    //修改图书数据
    int updateBook(Connection connection, Book book);
    //删除图书,根据id
    void deleteBookById(Connection connection, int id);
    //查询图书信息,根据id
    Book queryBookById(Connection connection, int id);
    //查看所有图书信息
    List<Book> queryBooks(Connection connection);
}

4.3.2、编写 BookDAOImpl

public class BookDAOImpl  extends BaseDAO<Book> implements BookDAO {
    @Override
    public void addBook(Connection connection, Book book) {
        String sql = "insert into `t_book`(`name`, `author`, `price`, `sales`, `stock`, `img_path`) values(?,?,?,?,?,?)";
        update(connection,sql,book.getName(),book.getAuthor(),book.getPrice(),book.getSales(),book.getStock(),book.getImg_path());
    }
    @Override
    public int updateBook(Connection connection, Book book) {
        String sql = "UPDATE `t_book` SET `name`=?,`author`=?,`price`=?, `sales`=?,`stock`=?,`img_path`=? where `id`=?";
        int update = update(connection, sql, book.getName(), book.getAuthor(), book.getPrice(), book.getSales(), book.getStock(), book.getImg_path(), book.getId());
        return update;
    }
    @Override
    public void deleteBookById(Connection connection, int id) {
        String sql = "delete from `t_book` where `id`=?";
        update(connection,sql,id);
    }
    @Override
    public Book queryBookById(Connection connection, int id) {
        String sql = "select * from `t_book` where `id`=?";
        return queryForOne(connection, sql, id);
    }
    @Override
    public List<Book> queryBooks(Connection connection) {
        String sql = "select * from `t_book`";
        return queryForList(connection, sql);
    }
}

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

4.4.1、编写 BookService 接口

public interface BookService {
    //插入图书信息数据
    void addBook(Book book);
    //修改图书数据
    void updateBook(Book book);
    //删除图书,根据id
    void deleteBookById(int id);
    //查询图书信息,根据id
    Book queryBookById(int id);
    //查看所有图书信息
    List<Book> queryBooks();
}

4.4.2、编写 BookServiceImpl

public class BookServiceImpl implements BookService {
    private BookDAO bookDAO = new BookDAOImpl();
    @Override
    public void addBook(Book book) {
        Connection conn = jdbcUtils.getconn();
        bookDAO.addBook(conn,book);
        jdbcUtils.close(conn);
    }
    @Override
    public void updateBook(Book book) {
        Connection conn = jdbcUtils.getconn();
        int i = bookDAO.updateBook(conn, book);
        if(i == 0){
            System.out.println("更改失败");
        }
        jdbcUtils.close(conn);
    }
    @Override
    public void deleteBookById(int id) {
        Connection conn = jdbcUtils.getconn();
        bookDAO.deleteBookById(conn,id);
        jdbcUtils.close(conn);
    }
    @Override
    public Book queryBookById(int id) {
        Connection conn = jdbcUtils.getconn();
        Book book = bookDAO.queryBookById(conn, id);
        jdbcUtils.close(conn);
        return book;
    }
    @Override
    public List<Book> queryBooks() {
        Connection conn = jdbcUtils.getconn();
        List<Book> books = bookDAO.queryBooks(conn);
        jdbcUtils.close(conn);
        return books;
    }
}

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

4.5.1、图书列表功能的实现

点击 “ 后台管理 ”manager.jsp,再点击 “ 图书管理 ”book_manager.jsp ,首先展示的就是 图书列表,展示库中所有图书信息

图解列表功能流程:

JavaWeb书城项目(一)_第29张图片
注意:
如果访问 jsp 无法直接获取到数据,那么可以让程序先访问 Servlet 程序,再通过请求中转转发

在配置文件 web.xml 中进行配置
下面是 BookServlet 程序的地址配置

JavaWeb书城项目(一)_第30张图片
为什么地址设置为 manager? 为了区别前后台程序
JavaWeb书城项目(一)_第31张图片

修改图书管理的超链接,更改至 BookServlet 程序

在超链接标签中,可以使用 ?methonName=query设置隐藏域参数

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

同时注意,这里图书管理的超链接,是 GET 的请求方式,所以在 BaseServlet 程序里面还要重写 doGet() 方法

public abstract class BaseServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request,response);
    }
    
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        try {
            this.getClass().getDeclaredMethod(request.getParameter("methodName"),HttpServletRequest.class,HttpServletResponse.class).invoke(this,request,response);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

编写 BookServlet 程序中关于图书列表展示的代码

public class BookServlet extends BaseServlet {
    protected void query(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        BookServiceImpl bookService = new BookServiceImpl();
        List<Book> books = bookService.queryBooks();
        request.setAttribute("booklist",books);
        request.getRequestDispatcher("/pages/manager/book_manager.jsp").forward(request,response);
    }
}

修改 pages/manager/book_manager.jsp 页面
使用 JSTL 标签实现对 request 域中的图书数据进行遍历输出

<c:forEach items="${requestScope.booklist}" 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>

JavaWeb书城项目(一)_第32张图片

4.5.2、添加图书功能的实现

图解添加流程
JavaWeb书城项目(一)_第33张图片

book_edit.jsp 页面,修改表单 提交的按钮,更改至 BookServlet 程序

并设置隐藏域
JavaWeb书城项目(一)_第34张图片
BookServlet 程序中,编写 add() 方法,完成如下操作:
1、获取请求参数,并封装到 Book 对象
2、调用 bookService.addBook() 方法
3、跳转到图书列表页面,显示添加成功后新的页面信息

public class BookServlet extends BaseServlet {
    protected void add(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        BookServiceImpl bookService = new BookServiceImpl();
        Book book = WebUtils.mapToBean(new Book(), request.getParameterMap());
        bookService.addBook(book);
        //添加成功,跳转到图书列表,并展示添加之后的图书数据
        //request.getRequestDispatcher("/manager/bookServlet?methodName=query").forward(request,response);
        //也可以选择直接调用query(request,response)方法
        //query(request,response);
        //------------以上两句都是请求转发,会有bug,需要使用请求重定向---------------
        response.sendRedirect(request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+request.getContextPath()+"/"+"/manager/bookServlet?methodName=query");
    }
}

注意两点:

1、获取请求参数并封装的时候,需要 JavaBean 对象里面的 set() 方法名字与 输入框的 name 属性值一致JavaWeb书城项目(一)_第35张图片
2、在上面代码中,如果使用请求中转,那么从用户输入,提交到add()方法,再到query()查询,都是一次请求。
当用户提交完请求,浏览器会记录下最后一次请求的全部信息。当用户按下功能键 F5,就会发起浏览器记录的最后一次 请求
那么此时执行最后一次请求,就会添加数据并且查询,就有了重复添加问题
所以在 BookServlet 程序中,从添加成功,跳转到图书列表,只可以使用请求重定向

不需要共享请求数据的跳转,通通使用重定向即可
在这里插入图片描述

4.5.3、删除图书功能的实现

图解删除流程

JavaWeb书城项目(一)_第36张图片

修改删除的超链接,更改至 BookServlet 程序

在超链接标签中,可以使用 ?methonName=delete设置隐藏域参数

因为删除的方法是根据对象的 id 进行删除,所以需要在跳转的时候设置请求参数:对象的 id

<a href="manager/bookServlet?methodName=delete&bookid=${book.id}">删除</a>

JavaWeb书城项目(一)_第37张图片
BookServlet 程序中,编写 delete() 方法

public class BookServlet extends BaseServlet {
    protected void delete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        BookServiceImpl bookService = new BookServiceImpl();
        // 获取请求的id
        Integer bookid = Integer.valueOf(request.getParameter("bookid"));
        bookService.deleteBookById(bookid);
        // 跳转回图书列表,使用请求重定向
        response.sendRedirect(request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+request.getContextPath()+"/"+"/manager/bookServlet?methodName=query");
    }
}

到此,删除的功能就完成了,但是还可以优化一下

1、在 BookServlet 程序中,第一步获取请求参数的 id 时候,可以在 WebUtils 工具类中编写一个方法,如果 id 强转失败,就返回默认值 0,这个功能后续也会用到
2、在删除的时候,浏览器弹出窗口提示 “ 确定删除XXX吗? ”

WebUtils 工具类中

public class WebUtils {
    /**
     * 如果转换失败,则返回默认值 defaultid
     */
    public static int parseInt(String id,int defaultid){
        try {
            return Integer.parseInt(id);
        } catch (NumberFormatException e) {
            e.printStackTrace();
        }
        return defaultid;
    }
}

BookServlet 程序中

public class BookServlet extends BaseServlet {
    protected void delete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        BookServiceImpl bookService = new BookServiceImpl();
        // 获取请求的 id
        int bookid = WebUtils.parseInt(request.getParameter("bookid"), 0);
        //调用 BookServiceImpl 的 deleteBookById() 方法
        bookService.deleteBookById(bookid);
        // 跳转回图书列表
        response.sendRedirect(request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+request.getContextPath()+"/"+"/manager/bookServlet?methodName=query");
    }
}

book_manager.jsp 页面中,添加删除弹窗确认操作
JavaWeb书城项目(一)_第38张图片

	<script type="text/javascript">
		$(function () {
			//删除超链接
			$("a.adeleteclass").click(function () {
				//获取当前点击的 删除超链接 所在的行标签tr
				var aa = $(this).parent().parent();
				//获取当前 删除超链接 所控制删除的书名
				var $1 = aa.children(":first").text()
				//confirm()
				//当用户点击了确定,该方法就会返回true,当用户点击了取消,就返回false
				return confirm("确定删除《"+$1+"》吗?");
				//当return false的时候, 删除超链接 的点击事件就不会执行
			})
		})
	</script>

4.5.4、修改图书功能的实现

表单回显操作

当你点击修改,跳转进 book_edit.jsp 页面,会显示你点击要修改的图书的全部信息

类似登录失败或注册失败时候的表单回显信息的操作

修改修改的超链接,更改至 BookServlet 程序

在超链接标签中,可以使用 ?methonName=getBook设置请求参数,使其执行 getBook 方法

因为表单回显操作是根据对象的 id 来查询当前的图书数据,所以需要在跳转的时候设置请求参数:对象的 id
JavaWeb书城项目(一)_第39张图片
BookServlet 程序中,编写 getBook() 方法

在请求对象的域值设置的是一个对象时候request.setAttribute("book",book);,在jsp文件中也可以获取到对象里面的各个属性值 ${requestScope.book.name}

public class BookServlet extends BaseServlet {
    protected void getBook(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        BookServiceImpl bookService = new BookServiceImpl();
        // 获取请求的 id,就是当前要修改的图书的id
        int bookid = WebUtils.parseInt(request.getParameter("bookid"), 0);
        //由 id 查询图书信息,就是当前要修改的图书的信息
        Book book = bookService.queryBookById(bookid);
        //设置为请求对象的域值
        request.setAttribute("book",book);
        request.getRequestDispatcher("/pages/manager/book_edit.jsp").forward(request,response);
    }
}

book_edit.jsp 页面,展示请求对象域值里封装的图书对象的信息
JavaWeb书城项目(一)_第40张图片
到此,表单回显功能实现

修改图书操作

book_edit.jsp 页面,点击提交的时候,既要实现添加操作,又要实现修改操作,区别这两个功能的是表单隐藏域的值,隐藏域的值可以决定表单提交到 servlet 程序后执行哪一个方法/功能

有三个解决方法

1、就是在添加按钮、修改的超链接点击的时候(发送请求的时候),设置各自的请求参数,然后在 book_edit.jsp 页面来获取这个参数,以区分两种不同的功能
在这里插入图片描述
JavaWeb书城项目(一)_第41张图片
在这里插入图片描述
2、因为添加超链接是直接跳转至 book_edit.jsp 页面,没有参数,而修改超链接是设置了请求参数bookid=${book.id}

那么可以判断一下请求参数里面有没有bookid参数,如果有就说明是修改操作,没有就说明是添加操作

value = "${empty param.bookid ? "add" : "update"}"
JavaWeb书城项目(一)_第42张图片
3、此方案与第二种方案类似,就是判断 请求对象的域(Request 域)中是否包含图书对象。如果有就说明是修改操作,没有就说明是添加操作
value = "${empty requestScope.book ? "add" : "update"}"
(因为修改操作经过了getBook方法,Request 域中有一个图书对象book )

注意:

  1. 因为在 BookServiceImpl 类中的更改方法 update 的SQL语句是根据 id 来修改,所以修改的超链接传递参数的时候还要传递图书对象的 id
  2. 在表单里面,可以将想从 jsp 页面传递给 servlet 的请求参数设置为隐藏域
  3. 在超链接里面,想传递给 servlet 程序的请求参数可以在链接后面用 ?key=value&key1=value1 的方式

JavaWeb书城项目(一)_第43张图片
BookServlet 程序中,编写 update() 方法

public class BookServlet extends BaseServlet {
    protected void update(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        BookServiceImpl bookService = new BookServiceImpl();
        // 获取请求参数,封装为对象
        Book book = WebUtils.mapToBean(new Book(), request.getParameterMap());
        book.setId(WebUtils.parseInt(request.getParameter("bookid"), 0));
        System.out.println(book);
        bookService.updateBook(book);
        // 跳转回图书列表
        response.sendRedirect(request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+request.getContextPath()+"/"+"/manager/bookServlet?methodName=query");
    }
}

你可能感兴趣的:(笔记,jquery,javascript)