关于图片失效的问题,后面题主尽量重新填坑,因为我也忘了那些地方都是些什么图

第一阶段:表单验证

1、新建一个模块

2、把书城的静态资源拷贝到bookStore工程下

3、登录注册页面表单验证

在用户登录注册页面对输入数据进行一些规则限制

<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>尚硅谷会员注册页面</title><link href="../../static/css/style.css" type="text/css" rel="stylesheet"><script type="text/javascript" src="../../static/script/jquery-1.7.2.js"></script><script type="text/javascript">// 页面加载完成之后$(function () {//给注册绑定单击事件$("#sub_btn").click(function () {//验证用户名:必须由字母数字下划线组成,并且长度为5到12位var username = $("#username").val();var patt = /^[0-9a-zA-Z_-]{5,12}$/;if(!patt.test(username)){$("span.errorMsg").text("用户名不合法!");return false;}//验证密码:必须由字母数字下划线组成,并且长度为5到12位var passtext = $("#password").val();var patt = /^\w{5,12}$/;if(!patt.test(passtext)){$("span.errorMsg").text("请重新输入密码!");return false;}//验证确认密码:和密码相同var pwd = $("#repwd").val();if(passtext != pwd){$("span.errorMsg").text("两次密码输入不一致!");return false;}//邮箱验证:xxxxx@xxx.comvar emitext = $("#email").val();var emailpatt = /^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/if(!emailpatt.test(emitext)){$("span.errorMsg").text("您的邮箱格式有误!");return false;}//验证码:现在只需要验证用户已输入。var codetext = $("#code").val();codetext = $.trim(codetext);if(codetext == null || codetext == ""){$("span.errorMsg").text("请输入验证码!");return false;}$("span.errorMsg").text();return  false;});});</script><style type="text/css">.login_form{height:420px;margin-top: 25px;}</style></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="userServlet" method="post"><input type="hidden" name="action" value="regist"><label>用户名称:</label><input class="itxt" type="text" placeholder="请输入用户名"autocomplete="off" tabindex="1" name="username" id="username" /><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" name="code" 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></body>
</html>

第二阶段:用户登录注册

1、JavaEE三层架构

  • Web层/视图展现层

    • 获取请求参数,封装成Bean对象
    • 调用Service业务层处理业务
    • 响应数据给客户端请求转发、重定向
    • Servlet程序,Webwork,Strtus1.x,Strtus2.x,SpringMVC
  • Service业务层
    • 处理业务逻辑
    • 调用持久层保存到数据库
    • Spring框架
  • Dao持久层
    • Dao持久层,只负责跟数据库交互
    • 执行CRUD操作
      • Create 添加
      • Read 读/查
      • Update 修改
      • Delete 删除
    • JDBC,DbUtils,JdbcTemplate,Mybatis,Hiberante,JPA

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tTETvfQX-1647832234568)(file:///C:/Users/ZHANGY~1/AppData/Local/Temp/企业微信截图_1638522597892.png)]

分层的目的是为了降低代码耦合度,方便日后项目的维护和升级

搭建书城项目开发环境:

web层 com.company.web/servlet/controller

service层 com.company.service Service接口包

​ com.company.service.impl Service接口实现类

dao层 com.company.dao Dao接口包

​ com.company.dao.impl Dao接口实现类

实体类对象 com.company.bean/pojo/entity/domain JavaBean类

测试包 com.company.test/junit

工具类 com.company.utils

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

drop database if exists book;create database  book;
use book;create table t_user(`id` int primary key  auto_increment,`username` varchar(20) not null unique,`password` varchar(32) not null ,`email` varchar(200)
);insert into t_user(`username`,`password`,`email`) values('admin','admin','admin@company');

3、编写数据库对应的JavaBean对象

package com.yitong.bean;/*** ClassName: User* Description:* date: 2021/12/6 9:56** @author zhangyingying* @since JDK 1.8*/
public class User {private int id;private String username;private String password;private String email;public User() {}public User(int id, String username, String password, String email) {this.id = id;this.username = username;this.password = password;this.email = email;}public int getId() {return id;}public void setId(int 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;}@Overridepublic String toString() {return "User{" +"id=" + id +", username='" + username + '\'' +", password='" + password + '\'' +", email='" + email + '\'' +'}';}
}

4、编写工具类JdbcUtils

package com.yitong.utils;import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;/*** ClassName: JdbcUtils* Description:* date: 2021/12/6 10:01** @author zhangyingying* @since JDK 1.8*/
public class JdbcUtils {//使用德鲁伊提供的数据库连接池获取连接private static DruidDataSource dataSource;//加载创建数据库连接池static {try {//新建配置文件对象Properties properties = new Properties();//读取jdbc.properties配置文件InputStream inputStream = JdbcUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");//从流中加载数据到Properties对象中properties.load(inputStream);//创建数据库连接池(使用德鲁伊提供的数据库连接池工厂,创建数据库连接池)dataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(properties);} catch (Exception e) {e.printStackTrace();}}/*获取数据库连接池中的连接*/public static Connection getConnection(){Connection conn = null;try {//通过数据库连接池,获取数据库连接conn = dataSource.getConnection();} catch (SQLException e) {e.printStackTrace();}//返回连接return conn;}/*关闭连接,放回数据库连接池*/public static void close(Connection conn){if (conn != null){try {conn.close();} catch (SQLException e) {e.printStackTrace();}}}
}

5、编写Base Dao类

package com.yitong.dao;import com.yitong.utils.JdbcUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;/*** ClassName: BaseDao* Description:* date: 2021/12/6 14:55** @author zhangyingying* @since JDK 1.8*///抽象类
public abstract class BaseDao {//使用apache提供的DbUtils操作数据库private QueryRunner queryRunner = new QueryRunner();/*** @Description: update()方法用来执行:Insert/update/Delete语句* @author: zhangyingying* @date: 2021/12/6 15:10* @return :如果返回-1,说明执行失败,返回其他表示影响的行数*///这里没有考虑事务问题public int update(String sql,Object ... args){//连接数据库Connection connection = JdbcUtils.getConnection();try {//使用dbutils提供的通用更新方法更新int update = queryRunner.update(connection, sql, args);return update;} catch (SQLException e) {e.printStackTrace();}finally {//关闭连接JdbcUtils.close(connection);}return -1;}/*** @Description:查询一条数据返回值,BeanHandler* @author: zhangyingying* @date: 2021/12/6 16:18* @param: * @Param: type* @Param: sql* @Param: args* @return:T*/public <T> T queryForOne(Class<T> type,String sql,Object ... args){Connection connection = JdbcUtils.getConnection();try {T query = queryRunner.query(connection, sql, new BeanHandler<T>(type), args);return query;} catch (SQLException e) {e.printStackTrace();}finally{JdbcUtils.close(connection);}return null;}/*** @Description:查询多条数据返回值,BeanListHandler* @author: zhangyingying* @date: 2021/12/6 16:18* @param: * @Param: type* @Param: sql* @Param: args* @return:java.util.List<T>*///Class<T> type,用于接收对象.class类型。因为不确定传过来的对象类型,因此用泛型T来表示public <T> List<T> queryForList(Class<T> type,String sql,Object ... args){Connection connection = JdbcUtils.getConnection();try {//new BeanListHandler<T>(type)通过反射机制获取对象类型List<T> query = queryRunner.query(connection, sql, new BeanListHandler<T>(type), args);return query;} catch (SQLException e) {e.printStackTrace();}finally{JdbcUtils.close(connection);}return null;}/** @Description:返回一行一列的sql语句* @author: zhangyingying* @date: 2021/12/6 17:34* @param:* @Param: sql执行的SQL语句* @Param: arg   sql对应的参数值* @return:java.lang.Object*/public Object queryForSingleValue(String sql,Object ... arg){Connection connection = JdbcUtils.getConnection();try {Object query = queryRunner.query(connection, sql, new ScalarHandler(), arg);return query;} catch (SQLException e) {e.printStackTrace();}finally {JdbcUtils.close(connection);}return null;}
}

6、编写UserDao接口类、实现类和测试类

UserDao的接口类

package com.yitong.dao;import com.yitong.bean.User;/*** ClassName: UserDao* Description:* date: 2021/12/7 11:08** @author zhangyingying* @since JDK 1.8*/
public interface UserDao {//根据用户名查用户信息,如果返回null说明没有这个用户,反之亦然//用于注册时查找用户名是否被使用public User queryUserByUsername(String username);//根据用户名和密码查用户信息,返回null说明没有这个用户//用于登录时核对用户信息public User queryUserByUsernameAndPassword(String username,String password);//保存用户信息,返回-1表示操作失败public int saveUser(User user);
}

UserDao实现类

package com.yitong.dao.impl;import com.yitong.bean.User;
import com.yitong.dao.BaseDao;
import com.yitong.dao.UserDao;/*** ClassName: UserDaoImpl* Description:* date: 2021/12/7 11:16** @author zhangyingying* @since JDK 1.8*/
//继承BaseDao才能使用BaseDao中的方法
public class UserDaoImpl extends BaseDao implements UserDao {@Overridepublic User queryUserByUsername(String username) {String sql = "select * from t_user where username = ?";User user = queryForOne(User.class, sql, username);return user;}@Overridepublic User queryUserByUsernameAndPassword(String username, String password) {String sql = "select * from t_user where username = ? and password = ?";User user = queryForOne(User.class, sql, username,password);return user;}@Overridepublic int saveUser(User user) {String sql = "insert into t_user (`username`,`password`,`email`) values (?,?,?)";int update = update(sql, user.getUsername(), user.getPassword(), user.getEmail());return update;}
}

UserDao的测试类

package com.yitong.test;import com.yitong.bean.User;
import com.yitong.dao.UserDao;
import com.yitong.dao.impl.UserDaoImpl;
import org.junit.Test;/*** ClassName: UserDaoTest* Description:* date: 2021/12/7 11:28** @author zhangyingying* @since JDK 1.8*/public class UserDaoTest {//实现类可以由对应的接口类接收UserDao userDao = new UserDaoImpl();@Testpublic void UserDaoTest(){String username = "admin";User user1 = userDao.queryUserByUsername(username);if (user1 == null) {System.out.println("该用户不存在!");}else{System.out.println(user1);}String name = "root";String password = "root";User user2 = userDao.queryUserByUsernameAndPassword(name,password);if (user2 == null) {System.out.println("该用户不存在");}else{System.out.println(user2);}//这里的username不能和数据库里的重复User user = new User("zhangsan","123456","root@qq.com");int i = userDao.saveUser(user);System.out.println(i);}
}

7、编写UserService接口类实现类和测试类

UserService接口类

package com.yitong.service;import com.yitong.bean.User;/*** ClassName: UserService业务层* Description:* date: 2021/12/7 14:37** @author zhangyingying* @since JDK 1.8*/
public interface UserService {//注册新用户public void registerUser(User user);//登录,如果返回null是登录失败,返回有值表示登录成功。public User login(User user);//检查用户名是否可用,返回true表示用户名已存在,返回false表示用户名可用public boolean existUsername(String username);
}

UserServiceImpl接口实现类

package com.yitong.service.impl;import com.yitong.bean.User;
import com.yitong.dao.UserDao;
import com.yitong.dao.impl.UserDaoImpl;
import com.yitong.service.UserService;/*** ClassName: UserSeviceImpl* Description:* date: 2021/12/7 14:37** @author zhangyingying* @since JDK 1.8*/
public class UserServiceImpl implements UserService {//需要操作数据库就需要使用到Dao层的一些类方法。private UserDao userDao = new UserDaoImpl();//注册@Overridepublic void registerUser(User user) {userDao.saveUser(user);}//登录@Overridepublic User login(User user) {return userDao.queryUserByUsernameAndPassword(user.getUsername(),user.getPassword());}@Overridepublic boolean existUsername(String username) {User user = userDao.queryUserByUsername(username);if(user == null){//如果等于null,说明没查到,表示用户名可用return false;}return true;}
}

service层测试类

package com.yitong.test;import com.yitong.bean.User;
import com.yitong.service.UserService;
import com.yitong.service.impl.UserServiceImpl;
import org.junit.Test;/*** ClassName: UserServiceTest* Description:* date: 2021/12/7 15:13** @author zhangyingying* @since JDK 1.8*/
public class UserServiceTest {UserService userService = new UserServiceImpl();@Testpublic void registerUser(){userService.registerUser(new User("zhouqi","lisi","lisi@qq.com"));}@Testpublic void login( ) {userService.login(new User("root","root",null));}@Testpublic void existUsername(){userService.existUsername("lisi");}
}

8、实现用户的注册功能

项目第二阶段:用户注册和登录的实现。

需求1:用户注册:当用户输入完成,就会点击提交,把参数都发送给服务器,去注册保存。

  1. 获取请求参数
  2. 检查验证码是否正确
    • 正确

      • 检查用户名是否可用

        • 可用

          • 调用Service层保存到数据库
          • 跳到注册成功页面
        • 不可用
          • 跳回注册页面
    • 不正确
      • 跳回注册页面

功能需求

​ 访问注册页面

​ 填写注册信息,提交给服务器

​ 服务器应该保存用户注册失败,用户名已存在

​ 当用户不存在,注册成功

RegisteServlet

package com.yitong.controller;import com.yitong.bean.User;
import com.yitong.service.UserService;
import com.yitong.service.impl.UserServiceImpl;import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;/*** ClassName: RegistServlet* Description:* date: 2021/12/7 16:22* @author zhangyingying* @since JDK 1.8*/
public class RegistServlet extends HttpServlet {private UserService  userService = new UserServiceImpl();/** @Description: 密码不能被人看见因此使用doPost* @author: zhangyingying* @date: 2021/12/8 11:54* @Param: req* @Param: resp* @return:void*/@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//        1. 获取请求参数//用户名String username = req.getParameter("username");//密码String password = req.getParameter("password");//邮箱String email = req.getParameter("email");//验证码String code = req.getParameter("code");
//        2. 检查验证码是否正确========要求验证码为abcdeif("abcde".equalsIgnoreCase(code)){//           正确
//             检查用户名是否可用if(userService.existUsername(username)){//               不可用System.out.println("用户名:" + username + "不可用");
//                 跳回注册页面req.getRequestDispatcher("/pages/user/regist.html").forward(req,resp);}else{//               可用
//                 调用Service层保存到数据库
//                 跳到注册成功页面userService.registerUser(new User(username,password,email));req.getRequestDispatcher("/pages/user/regist_success.html").forward(req,resp);}}else{//            不正确
//              跳回注册页面System.out.println("验证码[" + code + "]错误");req.getRequestDispatcher("/pages/user/regist.html").forward(req,resp);}}
}

9、IDEA中Debug调试的使用

9.1、Debug调试

Debug调试代码,首先两个元素:断电+Debug启动服务器

  • 断点,是需要在代码需要停止的行的左边上单击,就可以添加或取消。
  • Debug启动Tomcat运行代码

9.2、调试工具栏

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nob5fzUv-1647832234569)(C:\Users\zhangyingying\AppData\Roaming\Typora\typora-user-images\image-20211208144948405.png)]

​ 1 2 4 3 5 6

  1. 表示让代码往下执行一行
  2. 可以进入当前方法体内(自己写的代码,非框架源码)
  3. 跳出当前方法体外
  4. 强制进入当前方法体内
  5. 停在光标所在行(相当于临时断点)

9.3、变量窗口

变量窗口:可以查看当前方法范围内所有有效的变量。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z693AQTS-1647832234570)(C:\Users\zhangyingying\AppData\Roaming\Typora\typora-user-images\image-20211208152312767.png)]

9.4、方法调用栈窗口

  1. 方法调用栈可以查看当前线程有哪些方法调用信息。
  2. 下一行调用上一行的方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zjsWCDTt-1647832234570)(C:\Users\zhangyingying\AppData\Roaming\Typora\typora-user-images\image-20211208152334749.png)]

9.5、其他常用调试按钮:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cswfj7xI-1647832234570)(C:\Users\zhangyingying\AppData\Roaming\Typora\typora-user-images\image-20211208151537440.png)]

第七个按钮:程序一直跑,直到下一个断点停下来。如果没有断点,就一直往下走

最后一个按钮:临时禁用所有断点

10、实现用户的登录功能

需求2:用户登录

  1. 获取请求参数
  2. 调用Xxx.Service.xxx()处理业务。
    • userService.login()登录
  3. 根据login()方法返回结果判断登录方式是否成功
    • 成功

      • 跳到成功登录页面
    • 失败
      • 跳回登录页面

​ 功能需求:

​ 访问登陆页面

​ 填写用户名密码后提交

​ 服务器判断用户是否存在

​ 如果登录失败返回用户名或者密码错误信息

package com.yitong.controller;import com.yitong.bean.User;
import com.yitong.service.UserService;
import com.yitong.service.impl.UserServiceImpl;import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;/*** ClassName: LoginServlet* Description:* date: 2021/12/8 10:29** @author zhangyingying* @since JDK 1.8*/
public class LoginServlet extends HttpServlet {private UserService userService = new UserServiceImpl();@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//1. 获取请求参数String username = req.getParameter("username");String password = req.getParameter("password");
//2. 调用Xxx.Service.xxx()处理业务。
//          userService.login()登录User login = userService.login(new User(username, password, null));//3. 根据login()方法返回结果判断登录方式是否成功if (login == null){//     失败
//       跳回登录页面req.getRequestDispatcher("/pages/user/login.html").forward(req,resp);}else {//     成功
//       跳到成功登录页面req.getRequestDispatcher("/pages/user/login_success.html").forward(req,resp);}}
}

第三阶段:优化

1、页面jsp动态化

  1. 在HTML页面顶行添加page指令
  2. 修改文件后缀名为:.jsp
  3. 使用IDEA搜索替换.html后缀为.jsp(快捷键为:Ctrl + Shift + R)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fx4nr37G-1647832234571)(file:///C:/Users/ZHANGY~1/AppData/Local/Temp/企业微信截图_16394518187103.png)]

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

2.1、head中的css、jquery、base标签

head.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--动态获取路径信息--%>
<%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>

页面中静态包含

<%--静态包含base标签,css样式,jQuery文件--%>
<%@include file="/pages/common/head.jsp"%>

2.2、每个页面的页脚

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

2.3、登录成功后的菜单

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>

页面静态包含

<%--静态包含base标签,css样式,jQuery文件--%>
<%@include file="/pages/common/head.jsp"%>

2.4、manager模块的菜单

manager_menu.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<div><a href="book_manager.jsp">图书管理</a><a href="order_manager.jsp">订单管理</a><a href="../../index.jsp">返回商城</a>
</div>

页面静态包含

<%--静态包含manager管理模块的菜单--%>
<%@include file="/pages/common/manager_menu.jsp"%>

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cMj5S0zh-1647832234571)(file:///C:/Users/ZHANGY~1/AppData/Local/Temp/企业微信截图_16394628072267.png)]

Servlet程序端添加回显信息显示到request域中,以注册为例

注册servlet

        if("abcde".equalsIgnoreCase(code)){//           正确
//             检查用户名是否可用if(userService.existUsername(username)){//把回显信息保存到request域中req.setAttribute("msg","用户名已存在!");req.setAttribute("username",username);req.setAttribute("email",email);
//               不可用System.out.println("用户名:" + username + "不可用");
//                 跳回注册页面req.getRequestDispatcher("/pages/user/regist.jsp").forward(req,resp);}else{//               可用
//                 调用Service层保存到数据库
//                 跳到注册成功页面userService.registerUser(new User(username,password,email));req.getRequestDispatcher("/pages/user/regist_success.jsp").forward(req,resp);}}else{//            不正确//把回显信息保存到request域中req.setAttribute("msg","验证码错误!");req.setAttribute("username",username);req.setAttribute("email",email);
//              跳回注册页面System.out.println("验证码[" + code + "]错误");req.getRequestDispatcher("/pages/user/regist.jsp").forward(req,resp);}

注册信息页面

<%@ 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 () {// 给注册绑定单击事件$("#sub_btn").click(function () {// 验证用户名:必须由字母,数字下划线组成,并且长度为5到12位//1 获取用户名输入框里的内容var usernameText = $("#username").val();//2 创建正则表达式对象var usernamePatt = /^\w{5,12}$/;//3 使用test方法验证if (!usernamePatt.test(usernameText)) {//4 提示用户结果$("span.errorMsg").text("用户名不合法!");return false;}// 验证密码:必须由字母,数字下划线组成,并且长度为5到12位//1 获取用户名输入框里的内容var passwordText = $("#password").val();//2 创建正则表达式对象var passwordPatt = /^\w{5,12}$/;//3 使用test方法验证if (!passwordPatt.test(passwordText)) {//4 提示用户结果$("span.errorMsg").text("密码不合法!");return false;}// 验证确认密码:和密码相同//1 获取确认密码内容var repwdText = $("#repwd").val();//2 和密码相比较if (repwdText != passwordText) {//3 提示用户$("span.errorMsg").text("确认密码和密码不一致!");return false;}// 邮箱验证:xxxxx@xxx.com//1 获取邮箱里的内容var emailText = $("#email").val();//2 创建正则表达式对象var emailPatt = /^[a-z\d]+(\.[a-z\d]+)*@([\da-z](-[\da-z])?)+(\.{1,2}[a-z]+)+$/;//3 使用test方法验证是否合法if (!emailPatt.test(emailText)) {//4 提示用户$("span.errorMsg").text("邮箱格式不合法!");return false;}// 验证码:现在只需要验证用户已输入。因为还没讲到服务器。验证码生成。var codeText = $("#code").val();//去掉验证码前后空格// alert("去空格前:["+codeText+"]")codeText = $.trim(codeText);// alert("去空格后:["+codeText+"]")if (codeText == null || codeText == "") {//4 提示用户$("span.errorMsg").text("验证码不能为空!");return false;}// 去掉错误信息$("span.errorMsg").text("");});});</script><style type="text/css">.login_form{height:420px;margin-top: 25px;}</style></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"><%=request.getAttribute("msg")==null?"":request.getAttribute("msg")%></span></div><div class="form"><form action="registServlet" method="post"><label>用户名称:</label><input class="itxt" type="text" placeholder="请输入用户名"value="<%=request.getAttribute("username")==null?"":request.getAttribute("username")%>"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="请输入邮箱地址"value="<%=request.getAttribute("email")==null?"":request.getAttribute("email")%>"autocomplete="off" tabindex="1" name="email" id="email" /><br /><br /><label>验证码:</label><input class="itxt" type="text" name="code" style="width: 150px;" id="code" value="abcde"/><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><%--静态包含页脚内容--%><%@include file="/pages/common/footer.jsp"%></body>
</html>

4、优化代码

4.1、合并LoginServlet和RegistServlet程序为UserServlet

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uBnrZkKE-1647832234571)(file:///C:/Users/ZHANGY~1/AppData/Local/Temp/企业微信截图_16394656534980.png)]

package com.yitong.controller;import com.yitong.bean.User;
import com.yitong.service.UserService;
import com.yitong.service.impl.UserServiceImpl;import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;public class UserServlet extends HttpServlet {private UserService userService = new UserServiceImpl();protected void regist(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//1. 获取请求参数//用户名String username = req.getParameter("username");//密码String password = req.getParameter("password");//邮箱String email = req.getParameter("email");//验证码String code = req.getParameter("code");//2. 检查验证码是否正确========要求验证码为abcdeif("abcde".equalsIgnoreCase(code)){//正确//检查用户名是否可用if(userService.existUsername(username)){//把回显信息保存到request域中req.setAttribute("msg","用户名已存在!");req.setAttribute("username",username);req.setAttribute("email",email);//不可用System.out.println("用户名:" + username + "不可用");//跳回注册页面req.getRequestDispatcher("/pages/user/regist.jsp").forward(req,resp);}else{//可用//调用Service层保存到数据库//跳到注册成功页面userService.registerUser(new User(username,password,email));req.getRequestDispatcher("/pages/user/regist_success.jsp").forward(req,resp);}}else{//不正确//把回显信息保存到request域中req.setAttribute("msg","验证码错误!");req.setAttribute("username",username);req.setAttribute("email",email);//跳回注册页面System.out.println("验证码[" + code + "]错误");req.getRequestDispatcher("/pages/user/regist.jsp").forward(req,resp);}}protected void login(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//1. 获取请求参数String username = req.getParameter("username");String password = req.getParameter("password");//2. 调用Xxx.Service.xxx()处理业务。//userService.login()登录User login = userService.login(new User(username, password, null));//3. 根据login()方法返回结果判断登录方式是否成功if (login == null){//失败//把错误信息和回显的表单项信息保存到Request域中req.setAttribute("msg","用户名或密码错误!");req.setAttribute("username",username);//跳回登录页面req.getRequestDispatcher("/pages/user/login.jsp").forward(req,resp);}else {//成功//跳到成功登录页面req.getRequestDispatcher("/pages/user/login_success.jsp").forward(req,resp);}}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String action = req.getParameter("action");if ("regist".equals(action)){regist(req,resp);}else if ("login".equals(action)){login(req,resp);}}
}

给login.jsp和regist.jsp页面添加隐藏域和修改请求地址

<form action="userServlet" method="post"><input type="hidden" name="action" value="login"/>
<form action="userServlet" method="post"><input type="hidden" name="action" value="regist"/>

4.2、使用反射优化大量else if代码

实际项目中一个模块可能会有很对功能,每一个功能都在doPost中用else if判断再执行相应的方法回造成else if代码大量冗余。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T6a96hEw-1647832234571)(file:///C:/Users/ZHANGY~1/AppData/Local/Temp/企业微信截图_16394665424892.png)]

因此我们通过使用反射获取要执行的对应的方法对其进行执行。不用再大量的写else if语句判断。

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String action = req.getParameter("action");try {//通过action业务鉴别字符串,获取相应的业务,方法反射对象Method declaredMethod = this.getClass().getDeclaredMethod(action,HttpServletRequest.class,HttpServletResponse.class);//调用目标业务方法declaredMethod.invoke(this,req,resp);} catch (Exception e) {e.printStackTrace();}
}

4.3、抽取BaseServlet程序

由于所有的Servlet都需要进行相同的反射操作调用相应的方法执行,所以将doPost抽取出来写成BaseServlet类,需要实现反射调用的类继承BaseServlet就可以完成代码的精简。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-chrsy2fZ-1647832234572)(file:///C:/Users/ZHANGY~1/AppData/Local/Temp/企业微信截图_16394694498391.png)]

package com.yitong.controller;import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;public abstract class BaseServlet extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String action = req.getParameter("action");try {//通过action业务鉴别字符串,获取相应的业务,方法反射对象Method declaredMethod = this.getClass().getDeclaredMethod(action,HttpServletRequest.class,HttpServletResponse.class);//调用目标业务方法declaredMethod.invoke(this,req,resp);} catch (Exception e) {e.printStackTrace();}}
}

从此只需要继承BaseServlet而无需继承HttpServlet

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

BeanUtils工具类,它可以一次性的把所有请求的参数注入到JavaBean中。

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

BeanUtils它不是JDK的类,而是一种第三方工具类。所以需要导包。

  1. 导入需要的jar包

    commons-beanutils-1.8.0.jar

    commons-logging-1.1.1.jar

  2. 使用BeanUtils类方法实现注入

    ​ 通过属性名和参数名对应的关系实现注入的,属性名是实体类中的定义的属性。参数名是表单中对应的name值。但注入找的是set方法,一旦set方法中的属性名和参数名不对应,尽管属性名和参数名相同,还是会注入失败。

    BeanUtils.populate(bean,request.getParameterMap());
    
  3. 编写WebUtil工具类使用

   package com.yitong.utils;import com.yitong.bean.User;import org.apache.commons.beanutils.BeanUtils;import javax.servlet.http.HttpServletRequest;import java.lang.reflect.InvocationTargetException;import java.util.Map;public class WebUtils {//把Map值注入到对应的JavaBean属性中,这样写适用范围更广,耦合度更低,扩展性更强,使用更加灵活。public static void copyParamToBean(HttpServletRequest req, Object bean){try {System.out.println("注入之前" + bean);BeanUtils.populate(bean,req.getParameterMap());System.out.println("注入之后" + bean);} catch (Exception e) {e.printStackTrace();}}}

调用

WebUtils.copyParamToBean(request,user);

​ 我们将Map的值注入到JavaBean中是很常见的。

java代码分为三层,dao层,Service层,servlet层。如果将参数写为HttpServletRequest req,Dao层和Service层没有这个API,不能使用这个方法,带来了Servlet层API的耦合度高。但是写成Map在三层都能使用这个方法

package com.yitong.utils;import com.yitong.bean.User;
import org.apache.commons.beanutils.BeanUtils;import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;public class WebUtils {//把Map值注入到对应的JavaBean属性中,这样写适用范围更广,耦合度更低,扩展性更强,使用更加灵活。public static void copyParamToBean(Map value, Object bean){try {System.out.println("注入之前" + bean);BeanUtils.populate(bean,value);System.out.println("注入之后" + bean);} catch (Exception e) {e.printStackTrace();}}
}

调用时

WebUtils.copyParamToBean(req.getParameterMap(),user);

优化

package com.yitong.utils;import com.yitong.bean.User;
import org.apache.commons.beanutils.BeanUtils;import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;public class WebUtils {public static Object copyParamToBean(Map value, Object bean){try {System.out.println("注入之前" + bean);BeanUtils.populate(bean,value);System.out.println("注入之后" + bean);} catch (Exception e) {e.printStackTrace();}return bean;}
}
User user = (User) WebUtils.copyParamToBean(req.getParameterMap(), new User());

再优化

package com.yitong.utils;import com.yitong.bean.User;
import org.apache.commons.beanutils.BeanUtils;import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;public class WebUtils {public static <T> T copyParamToBean(Map value, T bean){try {System.out.println("注入之前" + bean);BeanUtils.populate(bean,value);System.out.println("注入之后" + bean);} catch (Exception e) {e.printStackTrace();}return bean;}
}
User user = WebUtils.copyParamToBean(req.getParameterMap(), new User());

第四阶段:使用EL表达式修改表单回显

login.jsp

EL表达式null就是空串,所以空串可以不用判断,我们将表达式改为EL表达式

<span class="errorMsg"><%--<%=request.getAttribute("msg")==null?"请输入用户名和密码":request.getAttribute("msg")%>--%>${empty requestScope.msg ? "请输入用户名和密码":requestScope.msg}
</span><input class="itxt" type="text" placeholder="请输入用户名"autocomplete="off" tabindex="1" name="username"<%--value="<%=request.getAttribute("username")==null?"":request.getAttribute("username")%>"--%>value="${requestScope.username}"/>

regist.jsp

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

第五阶段:图书模块

1、MVC概念

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

MVC是一种思想

MVC的理念是将软件代码拆分成为组件,单独开发,组合使用(目的还是为了降低耦合度)。

让各层的代码尽可能的独立工作,而不产生依赖,方便后期升级和维护。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EihtKy8K-1647832234572)(file:///C:/Users/ZHANGY~1/AppData/Local/Temp/企业微信截图_16395317789938.png)]

2、图书模块

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

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

2.2、编写图书模块的JavaBean

package com.yitong.bean;import java.math.BigDecimal;/*** ClassName: Book* Description:* date: 2021/12/15 9:53** @author zhangyingying* @since JDK 1.8*/
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;//要求给定的图书封面路径不能为空if(imgPath != null && !"".equals(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) {//要求给定的图书封面路径不能为空if(imgPath != null && !"".equals(imgPath)){this.imgPath = imgPath;}}@Overridepublic String toString() {return "Book{" +"id=" + id +", name='" + name + '\'' +", author='" + author + '\'' +", price=" + price +", sales=" + sales +", stock=" + stock +", imgPath='" + imgPath + '\'' +'}';}
}

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

BookDao接口

package com.yitong.dao;import com.yitong.bean.Book;import java.util.List;/*** ClassName: BookDao* Description:* date: 2021/12/15 10:02** @author zhangyingying* @since JDK 1.8*/
public interface BookDao {//public int addBook(Book book);public int deleteBookById(int id);public int updateBook(Book book);public Book queryBookById(int id);public List<Book> queryBooks();
}

BookDaoImpl接口实现类

package com.yitong.dao.impl;import com.yitong.bean.Book;
import com.yitong.dao.BookDao;import java.math.BigDecimal;
import java.util.List;/*** ClassName: BookDaoImpl* Description:* date: 2021/12/15 10:05** @author zhangyingying* @since JDK 1.8*/
public class BookDaoImpl extends BaseDao implements BookDao {//新增书籍@Overridepublic int addBook(Book book) {String sql = "insert into t_book (`name`,`author`,`price`,`sales`,`stock`,`img_path`) values (?,?,?,?,?,?)";int add = update(sql, book.getName(), book.getAuthor(), book.getPrice(), book.getSales(), book.getStock(), book.getImgPath());return add;}//删除@Overridepublic int deleteBookById(int id) {String sql = "delete from t_book where id = ?";int delete = update(sql, id);return delete;}//更改书籍信息@Overridepublic int updateBook(Book book) {String sql = "update t_book set `name`=? ,`author`=?,`price`=?,`sales`=?,`stock`=?,`img_path`=? where id=?";int add = update(sql, book.getName(), book.getAuthor(), book.getPrice(), book.getSales(), book.getStock(),book.getImgPath(),book.getId());return add;}//根据id查书籍@Overridepublic Book queryBookById(int id) {String sql="select `name`,`author`,`price`,`sales`,`stock`,`img_path` imgPath from t_book where id = ?";Book book = queryForOne(Book.class, sql, id);return book;}//查询所有书籍@Overridepublic List<Book> queryBooks() {String sql="select `name`,`author`,`price`,`sales`,`stock`,`img_path` imgPath from t_book";List<Book> books = queryForList(Book.class, sql);return books;}
}

BookDao测试类

package com.yitong.test;import com.yitong.bean.Book;
import com.yitong.dao.BookDao;
import com.yitong.dao.impl.BookDaoImpl;
import org.junit.Test;import java.math.BigDecimal;
import java.util.List;/*** ClassName: BookDaoTest* Description:* date: 2021/12/15 10:46** @author zhangyingying* @since JDK 1.8*/
public class BookDaoTest {private BookDao bookDao = new BookDaoImpl();@Testpublic void addBook() {Book book = new Book(20, "MySQL从入门到入狱", "李四", new BigDecimal(9.99), 9999, 2, "static/img/default.jpg");int i = bookDao.addBook(book);System.out.println(i);}@Testpublic void deleteBookById() {int i = bookDao.deleteBookById(21);System.out.println(i);}@Testpublic void updateBook() {int i = bookDao.updateBook(new Book(19, "JAVA从入门到跑路", "张三", new BigDecimal(9.99), 9999, 2, "static/img/default.jpg"));System.out.println(i);}@Testpublic void queryBookById() {Book book = bookDao.queryBookById(13);System.out.println(book);}@Testpublic void queryBooks() {List<Book> books = bookDao.queryBooks();for (Book book : books) {System.out.println(book);}}
}

2.4、编写图书模块Service和测试Service

BookService接口类

package com.yitong.service;import com.yitong.bean.Book;import java.util.List;/*** ClassName: BookService* Description:* date: 2021/12/15 11:09** @author zhangyingying* @since JDK 1.8*/
public interface BookService {public int addBook(Book book);public int deleteBook(int id);public int updateBook(Book book);public Book queryBookById(int id);public List<Book> queryBook();
}

BookServiceImpl接口实现类

package com.yitong.service.impl;import com.yitong.bean.Book;
import com.yitong.dao.BookDao;
import com.yitong.dao.impl.BookDaoImpl;
import com.yitong.service.BookService;import java.util.List;/*** ClassName: BookServiceImpl* Description:* date: 2021/12/15 11:09** @author zhangyingying* @since JDK 1.8*/
public class BookServiceImpl implements BookService {private BookDao bookDao = new BookDaoImpl();@Overridepublic int addBook(Book book) {bookDao.addBook(book);return bookDao.addBook(book);}@Overridepublic int deleteBook(int id) {return bookDao.deleteBookById(id);}@Overridepublic int updateBook(Book book) {return bookDao.updateBook(book);}@Overridepublic Book queryBookById(int id) {return bookDao.queryBookById(id);}@Overridepublic List<Book> queryBook() {return bookDao.queryBooks();}
}

BookServiceTest测试类

package com.yitong.test;import com.yitong.bean.Book;
import com.yitong.service.BookService;
import com.yitong.service.impl.BookServiceImpl;
import org.junit.Test;import java.math.BigDecimal;
import java.util.List;import static org.junit.Assert.*;/*** ClassName: BookServiceTest* Description:* date: 2021/12/15 11:23** @author zhangyingying* @since JDK 1.8*/
public class BookServiceTest {private BookService bookService = new BookServiceImpl();@Testpublic void addBook() {bookService.addBook(new Book(null,"国哥在手,天下我有!", "1125", new BigDecimal(1000000),100000000,0,null));}@Testpublic void deleteBook() {bookService.deleteBook(21);}@Testpublic void updateBook() {bookService.updateBook(new Book(42,"国哥在手,天下我有!", "1125", new BigDecimal(1000000),100000000,0,null));}@Testpublic void queryBookById() {System.out.println(bookService.queryBookById(30));}@Testpublic void queryBook() {List<Book> books = bookService.queryBook();for (Book book : books) {System.out.println(book);}}
}

2.5、编写图书模块的WEB层和页面联调测试

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ivzrugaW-1647832234572)(file:///C:/Users/ZHANGY~1/AppData/Local/Temp/企业微信截图_16396228971970.png)]

BaseServlet

package com.yitong.controller;import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;public abstract class BaseServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//URL只能使用get方法提交,我们用doGet方法调用doPost方法doPost(req,resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//从表单中获取action的值,从而调用相应的方法String action = req.getParameter("action");try {//通过action业务鉴别字符串,获取相应的业务,方法反射对象Method declaredMethod = this.getClass().getDeclaredMethod(action,HttpServletRequest.class,HttpServletResponse.class);//调用目标业务方法declaredMethod.invoke(this,req,resp);} catch (Exception e) {e.printStackTrace();}}
}

BookServlet

package com.yitong.controller;import com.yitong.bean.Book;
import com.yitong.service.BookService;
import com.yitong.service.impl.BookServiceImpl;
import com.yitong.utils.WebUtils;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;public class BookServlet extends BaseServlet{private BookService bookService = new BookServiceImpl();//查询所有书籍并显protected void list(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//通过BookService查询全部图书List<Book> books = bookService.queryBook();//把全部图书保存到Request域中req.setAttribute("books",books);//请求转发到/pages/manager/book_manager.jsp页面req.getRequestDispatcher("/pages/manager/book_manager.jsp").forward(req,resp);}

book_manager.jsp

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>图书管理</title><%--静态包含base标签,css样式,jQuery文件--%><%@include file="/pages/common/head.jsp"%>
</head>
<body><div id="header"><img class="logo_img" alt="" src="../../static/img/logo.gif" ><span class="wel_word">图书管理系统</span><%--静态包含manager管理模块的菜单--%><%@include file="/pages/common/manager_menu.jsp"%></div><div id="main"><table><tr><td>名称</td><td>价格</td><td>作者</td><td>销量</td><td>库存</td><td colspan="2">操作</td></tr><c:forEach items="${requestScope.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="book_edit.jsp">修改</a></td><td><a href="#">删除</a></td></tr></c:forEach><tr><td></td><td></td><td></td><td></td><td></td><td></td><td><a href="book_edit.jsp">添加图书</a></td></tr>   </table></div><%--静态包含页脚内容--%><%@include file="/pages/common/footer.jsp"%>
</body>
</html>

修改【图书管理】请求地址

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<div>//修改请求地址<a href="manager/bookServlet?action=list">图书管理</a><a href="pages/manager/order_manager.jsp">订单管理</a><a href="index.jsp">返回商城</a>
</div>

前后台请求地址区别

项目一般分为前后台

前台:是给普通用户使用,一般不需要权限检查就可以访问资源,或者功能都属于前台功能。比如淘宝不登陆就可以访问的首页(包含商品浏览)前台地址为:client/bookServlet/

后台:是写给管理员使用的。一般都需要权限检查才能访问到的资源,或页面或功能,是后台。后台的地址为:manager/bookServlet/

2.6、添加图书

​ 表单重复提交:当用户提交完请求,浏览器会记录下最后一次请求的全部信息,当用户按F5刷新的时候,数据就会再次提交,浏览器再次发送请求给后台就会再次执行添加操作。

​ 点击添加图书按钮,跳转到添加页面,填写完对应信息之后点击提交,执行add方法将数据添加到数据库中。然后通过重定向跳回书籍列表页面。

添加书籍信息页面

<%@ 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"%>
<style type="text/css">h1 {text-align: center;margin-top: 200px;}h1 a {color:red;}input {text-align: center;}
</style>
</head>
<body><div id="header"><img class="logo_img" alt="" src="../../static/img/logo.gif" ><span class="wel_word">编辑图书</span><%--静态包含manager管理模块的菜单--%><%@include file="/pages/common/manager_menu.jsp"%></div><div id="main"><form action="manager/bookServlet" method="get"><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></div><%--静态包含页脚内容--%><%@include file="/pages/common/footer.jsp"%>
</body>
</html>

添加图书Servlet

protected void add(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {Book book = WebUtils.copyParamToBean(req.getParameterMap(), new Book());bookService.addBook(book);/* 请求转发的 / 表示工程名,重定向的 / 表示端口号 *///重定向resp.sendRedirect(req.getContextPath() + "/manager/bookServlet?action=list");
}

2.7、删除图书信息

删除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.deleteclass").click(function (){//在时间的function函数中,有一个this对象,这个this对象,是当前正在相应事件的dom对象。/** confirm是是确认提示框函数* 参数是它的提示内容* 它有两个按钮,一个确认,一个是取消* 返回true表示点击了确认按钮,返回false表示点击取消*/return confirm("你确定要删除" + $(this).parent().parent().find("td:first").text() + "吗?");});});</script>
</head>
<body><div id="header"><img class="logo_img" alt="" src="../../static/img/logo.gif" ><span class="wel_word">图书管理系统</span><%--静态包含manager管理模块的菜单--%><%@include file="/pages/common/manager_menu.jsp"%></div><div id="main"><table><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="book_edit.jsp">修改</a></td><td><a class="deleteclass" href="manager/bookServlet?action=delete&id=${book.id}">删除</a></td></tr></c:forEach> </table></div><%--静态包含页脚内容--%><%@include file="/pages/common/footer.jsp"%>
</body>
</html>

删除Servlet

protected void delete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String id = req.getParameter("id");int i = WebUtils.parseInt(id, 0);/*        int i = 0;try {i = Integer.parseInt(id);}catch (Exception e){e.printStackTrace();}*/bookService.deleteBook(i);//删除后重定向到展示所有信息的页面resp.sendRedirect(req.getContextPath() + "/manager/bookServlet?action=list");
}

2.8、修改图书信息

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nGr9Vx0p-1647832234580)(file:///C:/Users/ZHANGY~1/AppData/Local/Temp/企业微信截图_16399641294841.png)]

​ jsp页面不能直接携带数据到下一个页面,因此在图书列表管理页面不能直接到图书信息修改页面,它要去数据库中获取当前修改的图书对应信息。所以要经过servlet将数据从数据库中获取出来。展示到页面

2.8.1、将要修改的图书信息展示到修改页面

修改列表页面信息

<%--a标签中的跳转请求中斜杠的用法开头加斜杠表示在端口号后面加请求地址开头不加斜杠表示在工程名后加请求地址将修改请求的连接改为将要修改的信息展示
--%>
<td><a href="manager/bookServlet?action=getBook&id=${book.id}">修改</a></td>
<td><a class="deleteclass" href="manager/bookServlet?action=delete&id=${book.id}">删除</a></td>
</tr>
</c:forEach>

BookServlet程序中添加getBook方法

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

在book_edit.jsp页面中显示修改的数据

<div id="main"><form action="manager/bookServlet" method="get"><input type="hidden" name="action" value="add"/><table><tr><td>名称</td><td>价格</td><td>作者</td><td>销量</td><td>库存</td><td colspan="2">操作</td></tr><tr><td><input name="name" type="text" value="${requestScope.book.name}"/></td><td><input name="price" type="text" value="${requestScope.book.price}"/></td><td><input name="author" type="text" value="${requestScope.book.author}"/></td><td><input name="sales" type="text" value="${requestScope.book.sales}"/></td><td><input name="stock" type="text" value="${requestScope.book.stock}"/></td><td><input type="submit" value="提交"</td></tr></table></form>
</div>

2.8.2、将修改后的数据存储到数据库并跳回图书列表页面

遇到的问题是:book_edit.jsp页面,既要做添加此操作又要做修改操作。。而到底是添加还是修改是由一个隐藏域来决定的。如何动态修改隐藏域让它的值即可实现添加。又可以实现修改操作

解决方案一:可以在请求发起时,附带上当前要操作的值。并注入到隐藏域中。

book_manager.jsp

<div id="main"><table><tr><td>名称</td><td>价格</td><td>作者</td><td>销量</td><td>库存</td><td colspan="2">操作</td></tr><c:forEach items="${requestScope.books}" var="book"><tr><td>${book.name}</td><td>${book.price}</td><td>${book.author}</td><td>${book.sales}</td><td>${book.stock}</td><%--a标签中的跳转请求中斜杠的用法开头加斜杠表示在端口号后面加请求地址开头不加斜杠表示在工程名后加请求地址--%><td><a href="manager/bookServlet?action=getBook&id=${book.id}&method=update">修改</a></td><td><a class="deleteclass" href="manager/bookServlet?action=delete&id=${book.id}">删除</a></td></tr></c:forEach><tr><td></td><td></td><td></td><td></td><td></td><td></td><td><a href="pages/manager/book_edit.jsp?method=add">添加图书</a></td></tr>    </table>
</div>

book_edit.jsp

<div id="main"><form action="manager/bookServlet" method="get"><%--可以通过请求的method属性来判断这个页面用来复用那个功能--%><input type="hidden" name="action" value="${param.method}"><table><tr><td>名称</td><td>价格</td><td>作者</td><td>销量</td><td>库存</td><td colspan="2">操作</td></tr>        <tr><td><input name="name" type="text" value="${requestScope.book.name}"/></td><td><input name="price" type="text" value="${requestScope.book.price}"/></td><td><input name="author" type="text" value="${requestScope.book.author}"/></td><td><input name="sales" type="text" value="${requestScope.book.sales}"/></td><td><input name="stock" type="text" value="${requestScope.book.stock}"/></td><td><input type="submit" value="提交"/></td></tr> </table></form>
</div>

解决方案二:可以通过判断当前请求参数中是否包含有id参数,如果有说明是修改操作,如果没有说明是添加操作。

<div id="main"><form action="manager/bookServlet" method="get"><%--判断请求数据的id是否为空,如果为空就为添加信息,不为空就是修改信息--%><input type="hidden" name="action" value="${empty param.id ? "add" : "update"}"><table><tr><td>名称</td><td>价格</td><td>作者</td><td>销量</td><td>库存</td><td colspan="2">操作</td></tr>     <tr><td><input name="name" type="text" value="${requestScope.book.name}"/></td><td><input name="price" type="text" value="${requestScope.book.price}"/></td><td><input name="author" type="text" value="${requestScope.book.author}"/></td><td><input name="sales" type="text" value="${requestScope.book.sales}"/></td><td><input name="stock" type="text" value="${requestScope.book.stock}"/></td><td><input type="submit" value="提交"/></td></tr> </table></form>
</div>

解决方案三:可以通过判断,Request域中是否包含有修改的图书信息对象,如果没有说明是添加操作,如果有说明是修改操作。

<div id="main"><form action="manager/bookServlet" method="get"><%--判断request是否为空,如果为空就为添加信息,不为空就是修改信息--%><input type="hidden" name="action" value="${empty requestScope.book ? "add" : "update"}"><table><tr><td>名称</td><td>价格</td><td>作者</td><td>销量</td><td>库存</td><td colspan="2">操作</td></tr>        <tr><td><input name="name" type="text" value="${requestScope.book.name}"/></td><td><input name="price" type="text" value="${requestScope.book.price}"/></td><td><input name="author" type="text" value="${requestScope.book.author}"/></td><td><input name="sales" type="text" value="${requestScope.book.sales}"/></td><td><input name="stock" type="text" value="${requestScope.book.stock}"/></td><td><input type="submit" value="提交"/></td></tr> </table></form>
</div>

在BookServlet程序中添加update方法

//更新书籍信息
protected void update(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//获取请求数据Map parameterMap = req.getParameterMap();//注入到javaBean中Book book = WebUtils.copyParamToBean(parameterMap, new Book());//调用bookService.updateBook(book)bookService.updateBook(book);//重定向到书籍列表resp.sendRedirect(req.getContextPath() + "/manager/bookServlet?action=list");
}

在修改图书信息的时候需要id,因此我们在请求update时需要把id传回去,我们把id属性写在隐藏域中,这个时候就可以更新了。

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

3、图书分页分析

3.1、分页模块的分析

由分页的视图分析出分页的对象模型Page模型

pageNo 当前页码

pageTotal 总页码

pageTotalCount 总记录数

pageSize 每页显示数量

items 当前页数据

这些属性的值都怎么确定?

  • pageNo:当前页码是由客户端进行传递

  • pageSize:每页显示数量由两种因素决定

    • 客户端进行传递
    • 由页面布局决定
  • pageTotalCount:总记录数可以由sql语句求得。

    sql语句是:select count(*) from 表名

  • pageTotal:总页码可以由总记录数/每页数量得到。

    注:总记录数%每页的数量>0,则总页码+1

  • item是当前页数据,也可以由sql语句求得。

    sql语句的范围都是前闭后开区间,可以理解为1-4就是0-3,类比数组下标。不包含最后一个。

    sql:select * from 表名 limit begin , pageSize;

    begin可以由公式求得:(pageNo-1)x pageSize;

    假设,当前是第1页,每页5条记录,则begin = (1-1)x 5 得0;

    假设,当前是第2页,每页5条记录,则begin = (2-1)x 5 得5;

    假设,当前是第3页,每页5条记录,则begin = (3-1)x 5 得10;

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wj7mzJRv-1647832234580)(C:\Users\zhangyingying\AppData\Roaming\Typora\typora-user-images\image-20211220145754282.png)]

JavaWeb书城项目(尚硅谷视频整理自用)相关推荐

  1. 4.Java学习笔记第四节——程序流程控制(尚硅谷视频整理)

    文章目录 一.分支语句 1.       if-else 结构 1)如何从键盘获取不同类型的变量 2.      switch-case 结构 二.循环结构 1.      for 循环 一.分支语句 ...

  2. Spring框架(基于尚硅谷视频)

    该笔记源于尚硅谷视频对Spring的讲解(适合新手,大佬划走),视频连接如下: https://www.bilibili.com/video/BV1Vf4y127N5?p=27&spm_id_ ...

  3. 2、Spring Boot尚硅谷笔记整理高级篇-消息

    1.Spring Boot尚硅谷笔记整理高级篇-缓存 2.Spring Boot尚硅谷笔记整理高级篇-消息 3.Spring Boot尚硅谷笔记整理高级篇-检索 4.Spring Boot尚硅谷笔记整 ...

  4. Vue移动端项目---尚硅谷外卖

    文章目录 Vue移动端项目--尚硅谷外卖 项目目录结构介绍 移动端适配 头部和底部导航 登录注册页面 密码登录 短信登录 Profile页面 未登录 已登录 Msite页面 首页导航 ShopList ...

  5. JavaWeb书城项目

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

  6. JavaWeb书城项目(一)

    书城项目(一) 1.表单验证的实现 代码 2.用户注册和登陆 JavaEE 项目的三层架构 2.1.数据库层 2.1.1.创建数据库 2.1.2.定义 JavaBean 类 2.1.3.编写工具类 J ...

  7. Docker_尚硅谷视频学习笔记

    文章目录 1 Docker 简介 前提知识+课程定位 Docker 是什么? 问题:为什么会有docker出现 docker理念 总结 能干嘛 之前的虚拟机技术 容器虚拟化技术 开发/运维(DevOp ...

  8. Mybatis学习笔记(尚硅谷版整理)

    Mybatis [尚硅谷]2022版MyBatis教程(细致全面,快速上手)_哔哩哔哩_bilibili 笔记 Mybatis框架搭建 步骤 创建maven工程,引入依赖 创建Mybatis的核心配置 ...

  9. javascript全笔记-基础版(尚硅谷视频李立超老师)

    目录 一.JavaScript简介 1.1 JS简介 1.2 JS的HelloWorld 1.3 JS代码编写的位置 二.JS的基础语法 2.1 JS的基本语法 2.2 字面量与变量 2.3 标识符 ...

  10. javaWeb学习笔记(尚硅谷旧版+新版)

    HTML基础 目录 HTML基础 HTML书写规范 HTML标签 简介 ​ 特殊字符​ a标签 列表标签 img标签 table标签 跨行跨列表格 iframe标签 表单标签 关于GET和POST请求 ...

最新文章

  1. 树的分类,特性与遍历
  2. 错误C4146的解决方法
  3. 英特尔紧急召回SB主板,Intel 6系列芯片组设计缺陷
  4. Java中遍历数组使用foreach循环还是for循环?
  5. 组装台式计算机需要哪些配件,组装电脑需要哪些配件 电脑装机教程图解
  6. ASP.NET MVC教程六:两个配置文件详解
  7. mysql to data_mysql str_to_date 字符串转换为日期
  8. bind函数:给对象绑定方法。
  9. 【魔改蜗牛星际】A单主板变“皇帝板”扩展到8个SATA口
  10. Java中几种输出当前时间的代码
  11. php两个手机号正则表达式_php中手机号码正则表达式代码
  12. php如何实现运算符重载,JS怎样实现运算符重载
  13. win7 计算机定时关机脚本,Win7制作定时关机bat脚本|Win7定时关机程序脚本
  14. 做了一个电驴 p2p资源搜索小软件
  15. ASPUPload3.0注册机
  16. 2021布吉高级中学高考成绩查询,2021北京市地区高考成绩排名查询,北京市高考各高中成绩喜报榜单...
  17. 裸辞计算机考研,一位工作三年,裸辞,跨考,347学姐的考研经验 - 考研 - 小木虫 - 学术 科研 互动社区...
  18. mantis系统php版本,PHP_mantis安装、配置和使用中的问题小结,开发需要,安装mantis(Mantis Bug Tra - phpStudy...
  19. heuristic manner
  20. STEP7主站与远程I/O组网_过路老熊_新浪博客

热门文章

  1. 安卓开发示例代码总结(持续更新中...)
  2. 安卓蓝牙键盘按键映射_安卓手游键盘映射工具(凤凰系统键盘映射) v2.0 最新版...
  3. 一分六钱用计算机怎么算,交行信用卡分期付款计算器:5000元分6期手续费
  4. 商务与经济统计学 第五章案例题
  5. 「Java工具类」Apache的FileUtils和IOUtils工具类
  6. 软件测试管理者会遇到那些问题?
  7. Java实现DFA算法敏感词过滤
  8. 杂记 什么是ABC记谱法
  9. python进阶学习之路
  10. 配合蓝牙打印的encoding-indexes.js文件内容: