书城项目(一)

  • 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、表单验证的实现


现将文件拷贝进新的模块

然后完成以下需求


字母、数字、下划线组成,且长度是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 &copy;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 项目的三层架构


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

2.1、数据库层

第三方 jar 包放在 web 目录的 WEB-INF 文件夹下

2.1.1、创建数据库

数据库存放用户注册的数据

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 属性配置文件

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

如果获取了数据库连接池的连接没有及时释放,就只能获取最大连接数的连接

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

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 {@Overridepublic User queryUserByUsername(Connection connection, String username) {String sql = "select * from `t_user` where username = ?";User user = queryForOne(connection, sql, username);return user;}@Overridepublic 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;}@Overridepublic 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();@Overridepublic void registerUser(User user) {Connection conn = jdbcUtils.getconn();userDAO.saveUser(conn,user);jdbcUtils.close(conn);}@Overridepublic User login(User user) {Connection conn = jdbcUtils.getconn();User user1 = userDAO.queryUserByUsernameAndPassword(conn, user.getUsername(), user.getPassword());jdbcUtils.close(conn);return user1;}@Overridepublic 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>RegistServlet</servlet-name><servlet-class>com.atguigu.web.RegistServlet</servlet-class></servlet><servlet><servlet-name>LoginServlet</servlet-name><servlet-class>com.atguigu.web.LoginServlet</servlet-class></servlet><servlet-mapping><servlet-name>RegistServlet</servlet-name><url-pattern>/registServlet</url-pattern></servlet-mapping><servlet-mapping><servlet-name>LoginServlet</servlet-name><url-pattern>/loginServlet</url-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 标签,永远固定相对路径跳转的结果--><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 表单的提交地址和请求方式

3、优化页面

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

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

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

3.1、页面 jsp 动态化

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

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>&nbsp;&nbsp;<a href="index.jsp">返回</a>
</div>

创建新文件,将上面相同的内容放进新文件

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

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

3.2.2、每个页面的页脚

相同内容如下:

<div id="bottom"><span>小草莓书城.LittleStrawberry &copy;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 访问项目的时候,就会有下面问题


比如换同一个网络下的手机进行访问该网页,会报错

更改如下:
以下是文件 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>

更改之后,使用同一网段内的手机访问网址也可以显示出项目内容

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

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

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

需求:

  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 获取请求域对象的域值,如下:

在每个输入框内设置 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 获取请求域对象的域值,如下:

注意,因为在首次访问登录页面的时候,提示的位置有文字,所以错误提示的地方还是需要进行一次判断

使用 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 的抽取

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

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

添加隐藏域 ,和修改请求地址

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


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

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 <T>* @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、图书模块

MVC 概念

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

是 Web 层的分层

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

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

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

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

4.3.1、编写 UserDAO接口

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

4.3.2、编写 BookDAOImpl

public class BookDAOImpl  extends BaseDAO<Book> implements BookDAO {@Overridepublic 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());}@Overridepublic 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;}@Overridepublic void deleteBookById(Connection connection, int id) {String sql = "delete from `t_book` where `id`=?";update(connection,sql,id);}@Overridepublic Book queryBookById(Connection connection, int id) {String sql = "select * from `t_book` where `id`=?";return queryForOne(connection, sql, id);}@Overridepublic 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);//删除图书,根据idvoid deleteBookById(int id);//查询图书信息,根据idBook queryBookById(int id);//查看所有图书信息List<Book> queryBooks();
}

4.4.2、编写 BookServiceImpl

public class BookServiceImpl implements BookService {private BookDAO bookDAO = new BookDAOImpl();@Overridepublic void addBook(Book book) {Connection conn = jdbcUtils.getconn();bookDAO.addBook(conn,book);jdbcUtils.close(conn);}@Overridepublic void updateBook(Book book) {Connection conn = jdbcUtils.getconn();int i = bookDAO.updateBook(conn, book);if(i == 0){System.out.println("更改失败");}jdbcUtils.close(conn);}@Overridepublic void deleteBookById(int id) {Connection conn = jdbcUtils.getconn();bookDAO.deleteBookById(conn,id);jdbcUtils.close(conn);}@Overridepublic Book queryBookById(int id) {Connection conn = jdbcUtils.getconn();Book book = bookDAO.queryBookById(conn, id);jdbcUtils.close(conn);return book;}@Overridepublic 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 ,首先展示的就是 图书列表,展示库中所有图书信息

图解列表功能流程:


注意:
如果访问 jsp 无法直接获取到数据,那么可以让程序先访问 Servlet 程序,再通过请求中转转发

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


为什么地址设置为 manager? 为了区别前后台程序

修改图书管理的超链接,更改至 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>

4.5.2、添加图书功能的实现

图解添加流程

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

并设置隐藏域

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 属性值一致
2、在上面代码中,如果使用请求中转,那么从用户输入,提交到add()方法,再到query()查询,都是一次请求。
当用户提交完请求,浏览器会记录下最后一次请求的全部信息。当用户按下功能键 F5,就会发起浏览器记录的最后一次 请求
那么此时执行最后一次请求,就会添加数据并且查询,就有了重复添加问题
所以在 BookServlet 程序中,从添加成功,跳转到图书列表,只可以使用请求重定向

不需要共享请求数据的跳转,通通使用重定向即可

4.5.3、删除图书功能的实现

图解删除流程

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

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

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

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


BookServlet 程序中,编写 delete() 方法

public class BookServlet extends BaseServlet {protected void delete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {BookServiceImpl bookService = new BookServiceImpl();// 获取请求的idInteger 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();// 获取请求的 idint 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 页面中,添加删除弹窗确认操作

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

4.5.4、修改图书功能的实现

表单回显操作

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

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

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

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

因为表单回显操作是根据对象的 id 来查询当前的图书数据,所以需要在跳转的时候设置请求参数:对象的 id

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,就是当前要修改的图书的idint 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 页面,展示请求对象域值里封装的图书对象的信息

到此,表单回显功能实现

修改图书操作

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

有三个解决方法

1、就是在添加按钮、修改的超链接点击的时候(发送请求的时候),设置各自的请求参数,然后在 book_edit.jsp 页面来获取这个参数,以区分两种不同的功能



2、因为添加超链接是直接跳转至 book_edit.jsp 页面,没有参数,而修改超链接是设置了请求参数bookid=${book.id}

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

value = "${empty param.bookid ? "add" : "update"}"

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 的方式


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

JavaWeb书城项目(一)相关推荐

  1. JavaWeb书城项目

    尚硅谷书城项目:自己整理的笔记以及全部实现过程,原理. 链接: 点击获取资源 提取码: ih2c 再次感谢尚硅谷,我爱尚硅谷!!!! 目录 第一阶段:对注册页面的信息进行验证: 第二阶段:用户管理模块 ...

  2. JavaWeb网上书城项目总结(初步1.0)

    JavaWeb网上书城项目总结 目录 项目背景与目标 成员组成 模块划分 数据库设计 功能分析+源码 经验总结 (逐步放上博客,先总结) 成员组成 组长:林俊豪(本人) 组员:温尧皓.麦乙迪.邓梓鹏. ...

  3. JAVAWEB之小说书城项目

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

  4. JavaWeb 尚硅谷书城项目

    书城项目第一阶段:表单验证 需求:         验证用户名:必须由字母,数字下划线组成,并且长度为 5 到 12 位         验证密码:必须由字母,数字下划线组成,并且长度为 5 到 12 ...

  5. java web网上书城_javaweb网上书城项目

    [实例简介] javaweb网上书城项目,采用ssh框架,mysql数据库. [实例截图] [核心代码] bookstore └── ssh_book ├── WebContent │   ├── M ...

  6. 【Java - 项目开发】网上书城项目

    网上书城项目 创作日期:2021-12-23 第一阶段 登录注册的验证(表单验证) 技术方法: 使用 jQuery 技术对登录中的用户名.密码进行非空验证 使用 jQuery 技术和正则表达式对注册中 ...

  7. Java项目:网上电子书城项目(java+SSM+JSP+maven+Mysql)

    源码获取:博客首页 "资源" 里下载! 项目描述: spring mvc +jsp实现的简单书城项目,可以在支付宝沙箱内实现支付 运行环境: jdk8+tomcat9+mysql+ ...

  8. 网上书城java负责_网上书城项目总结(servlet_jsp+javaBean)

    网上书城项目总结 1 项目大纲设计: 需求分析 系统设计 详细设计 权限设计 2 技术选型: Servlet+jsp+javaBean Listener+Filter+jstl+fileupload+ ...

  9. JavaWeb完整项目要用到的专业技能

    完成JavaWeb项目用到哪些专业技能?在经典的JavaWeb的开发模式中,我们使用Jsp技术来作为展现层的实现,其实也就是所谓的前端.Web开发中经典的MVC模式,Model-View-Contro ...

最新文章

  1. 对象冒充_使用您的精神探照灯进行冒充冒名顶替综合症
  2. 理解 Azure 虚拟机的性能监视
  3. mysql alert on delete cascade_如何在mysql中删除级联使用?(How do I use on delete cascade in mysql?)...
  4. IDFA的值什么时候会发生改变
  5. SQL知识你了解多少?
  6. 我的nginx+php是如何配置的?
  7. 登顶Github趋势榜,非监督GAN算法U-GAT-IT大幅改进图像转换效果
  8. 吴恩达神经网络和深度学习-学习笔记-2-激活函数
  9. PHP工程师学习计划
  10. WPF 弹框 并自动关闭
  11. 尔雅 科学通史(吴国盛) 个人笔记及课后习题 2018 第十章 20世纪的科学技术变革
  12. c语言中比较两束大小,【 C 语言吧 · 文学 · 西游记 】
  13. c语言海报,C语言 竞选海报
  14. GA-SVM算法python实现
  15. Naive Bayes——Naive在哪?
  16. 防抄袭软件防抄袭系统反抄袭系统-推荐
  17. “AI”加持,一起“沉浸式体验”企业的走心服务
  18. 【MD5】MD5校验文件的正确性
  19. 修改手机服务器密码是不是获取不了通讯录,手机运营商服务密码改了,别人还会读取到通讯录吗...
  20. 权限管理SpringBoot+SpringSecurity系列文章 - 导读

热门文章

  1. 格子染色(容斥原理)
  2. 郑义宣就任韩国现代汽车集团会长
  3. 【微信】公众号群发相关使用
  4. P50发布,鸿蒙OS用户突破4000万!
  5. 一张足够长厚5mm的纸折叠多少次高度可以超过珠穆朗玛峰
  6. WEB开启防调试模式
  7. 【Teradata】数据库初始化(sysinit和dip工具)
  8. 【miscellaneous】最新HEVC/H.265 4K视频,显卡解码测试
  9. 中国剩余定理【数论】
  10. WPS文件格式如何进行转换 et格式转换word