书城项目

几个月前刚接触JavaWeb的项目,现在回过头来对所学知识进行总结

1.项目介绍

本项目是基于原生底层技术,核心技术点主要有:
Servlet程序、Filter过滤器、Listener监听器、jsp页面、EL表达式、JSTL标签库、jQuery框架、Cookie技术、Session会话、JSON使用、Ajax请求。
目标:、理解、会用。并为后期框架、框架的学习打下坚实的基础

第一阶段–表单验证

用于在用户登录和注册时使用的逻辑

验证用户名:必须由字母,数字下划线组成,并且长度为 5 到 12位验证密码:必须由字母,数字下划线组成,并且长度为 5 到 12 位

验证确认密码:和密码相同

邮箱验证:xxxxx@xxx.com

验证码:现在只需要验证用户已输入。因为还没讲到服务器。验证码生成。

1、新建一个模块

59930788322

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

59930791512

3、验证实现如下:

<script type="text/javascript">	
				//给注册绑定单击事件
            $("#sub_btn").click(function () {
                //验证用户名:必须由字母,数字下划线组成,并且长度为5到12位
                //1 获取用户名输入框里的内容
                var usernameTest = $("#username").val();
                //2 创建正则表达式对象
                var usernamePatt = /^\w{5,12}$/;
                //3 使用test方法验证
                if (!usernamePatt.test(usernameTest)) {
                    //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.length != passwordText.length) {
                    //提示用户
                    $("span.errorMsg").text("确认密码和密码不一致!");

                    return false;
                }

                //邮箱验证:xxxx@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 codeTest = $("#code").val();

                //去掉验证码前后空格
                codeTest = $.trim(codeTest);

                if (codeTest == null || codeTest == "") {
                    //提示用户
                    $("span.errorMsg").text("验证码不能为空!");
                    return false;
                }

                //去掉错误信息
                $("span.errorMsg").text("");

            });


        });

    </script>

第二阶段–用户注册和登陆

1.JavaEE项目的三层架构

59930859153

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

web 层 com.atguigu.web/servlet/controller Controller接口
service 层 com.atguigu.service Service 接口包
com.atguigu.service.impl Service 接口实现类
dao 持久层 com.atguigu.dao Dao 接口包
com.atguigu.dao.impl Dao 接口实现类
实体 bean 对象 com.atguigu.pojo/entity/domain/bean JavaBean 类
测试包 com.atguigu.test/junit
工具类 com.atguigu.utils

搭建书城项目开发环境:

59930892588

1、先创建书城需要的数据库和表。

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@atguigu.com'); select * from t_user;

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

/**
 * @outhor Mr.JK
 * @create 2020-04-03  20:49
 */
public class User {
    private Integer id;
    private String username;
    private String password;
    private String email;

    public User() {
    }

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

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

    public Integer getId() {
        return id;
    }

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

    public String getUsername() {
        return username;
    }

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

    public String getPassword() {
        return password;
    }

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

    public String getEmail() {
        return email;
    }

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

3、编写工具类 JdbcUtils

3.1、导入需要的jar 包(数据库和连接池需要):

druid-1.1.9.jar

mysql-connector-java-5.1.7-bin.jar

以下是测试需要:

hamcrest-core-1.3.jar

junit-4.12.jar

3.2、在 src 源码目录下编写 jdbc.properties 属性配置文件

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

3.3、编写jdbcUtils工具类:

public class JdbcUtils {

    private static DruidDataSource dataSource;
    static {

        try {
            Properties properties = new Properties();
            //读取jdbc.properties属性配置文件
            InputStream inputStream = JdbcUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
            //从流中加载数据
            properties.load(inputStream);
            //创建 数据库连接池
            dataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(properties);

            //测试代码
            //System.out.println(dataSource.getConnection());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static Connection getConnection() {
        

       Connection conn = null;

       try {
            conn = dataSource.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
        }
        return conn;
    }
    public static void close(Connection conn) {
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

3.4、JdbcUtils 测试

public class JdbcUtilsTest {

@Test
public void testJdbcUtils(){
    for (int i = 0; i < 100; i++){
        Connection connection = JdbcUtils.getConnection(); 
        System.out.println(connection); 				
        JdbcUtils.close(connection);
        }
    }

}

4、编写BaseDao

4.1、导入 DBUtils 的 jar 包

​ commons-dbutils-1.3.jar

4.2、编写 BaseDao:

/**
 * @outhor Mr.JK
 * @create 2020-04-03  21:17
 */
public abstract class BaseDao {

    //使用DbUtils操作数据库
    private QueryRunner queryRunner = new QueryRunner();

    /**
     * update()方法用来执行,Insert/Update/Delete语句
     * @return 如果返回-1,说明执行失败 返回其他表示影响的行数
     */
    public int update(String sql,Object ...args){
        Connection connection = JdbcUtils.getConnection();

        try {
            return queryRunner.update(connection,sql,args);
        } catch (SQLException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }

    }

    /**
     * 查询返回一个javaBean的sql语句
     * @param type 返回的对象类型
     * @param sql   执行的sql语句
     * @param args  sql对应的参数值
     * @param <T>   返回的类型的泛型
     * @return
     */
    public <T> T  queryForOne(Class<T> type,String sql,Object ...args){
        Connection connection = JdbcUtils.getConnection();
        try {
            return queryRunner.query(connection,sql,new BeanHandler<T>(type),args);
        } catch (SQLException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    /**
     * 查询返回多个javaBean的sql语句
     * @param type 返回的对象类型
     * @param sql   执行的sql语句
     * @param args  sql对应的参数值
     * @param <T>   返回的类型的泛型
     * @return
     */
    public <T> List<T> queryForList(Class<T> type,String sql,Object ...args) {
        Connection connection = JdbcUtils.getConnection();
        try {
            return queryRunner.query(connection, sql, new BeanListHandler<T>(type), args);
        } catch (SQLException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    /**
     * 执行返回一行一列的sql语句
     * @param sql   执行的sql语句
     * @param args  sql对应的参数值
     * @return
     */
    public Object queryForSingleValue(String sql,Object ...args){
        Connection connection = JdbcUtils.getConnection();
        try {
            return queryRunner.query(connection,sql,new ScalarHandler(),args);
        } catch (SQLException e){
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }


}

5、编写 UserDao 和测试

​ UserDao 接口:

/**
 * @outhor Mr.JK
 * @create 2020-04-03  21:34
 */
public interface UserDao {

    /**
     * 更加用户名查询用户信息
     * @param username  用户名
     * @return  如果返回null,说明没有这个用户,反之亦然
     */
    public User queryUserByUsername(String username);

    /**
     *  根据用户名和密码查询用户信息
     * @param username
     * @param password
     * @return  如果返回null,说明用户名或密码错误,反之亦然
     */
    public User queryUserByUsernameAndPassword(String username,String password);


    /**
     * 保存、添加用户信息
     * @param user 返回-1表示操作失败,其他是sql语句影响的行数
     */
    public int saveUser(User user);


}

UserDaoImpl 实现类:

/**
 * @outhor Mr.JK
 * @create 2020-04-03  21:40
 */
public class UserDaoImpl extends BaseDao implements UserDao {

    @Override
    public User queryUserByUsername(String username) {
        String sql = "select `id`,`username`,`password`,`email` from t_user where username = ?";
        return queryForOne(User.class,sql,username);
    }

    @Override
    public User queryUserByUsernameAndPassword(String username, String password) {
        String sql = "select `id`,`username`,`password`,`email` from t_user where username = ? and password = ?";
        return queryForOne(User.class,sql,username,password);
    }

    @Override
    public int saveUser(User user) {
        String sql = "insert into t_user(`username`,`password`,`email`) values(?,?,?)";
        return update(sql,user.getUsername(),user.getPassword(),user.getEmail());
    }
}

UserDao 测试:

/**
 * @outhor Mr.JK
 * @create 2020-04-03  21:49
 */
public class UserDaoImplTest {
    UserDao userDao = new UserDaoImpl();
    @Test
    public void queryUserByUsername() {

        if(userDao.queryUserByUsername("admin") == null){
            System.out.println("用户名可用!");
        } else {
            System.out.println("用户名已存在!");
        }
    }

    @Test
    public void queryUserByUsernameAndPassword() {
        if(userDao.queryUserByUsernameAndPassword("admin","admin") == null){
            System.out.println("用户名或密码错误,登陆失败");
        } else {
            System.out.println("查询成功");
        }
    }

    @Test
    public void saveUser() {
        System.out.println(userDao.saveUser(new User(null,"lin","123456","lin@qq.com")));
    }
}

6、编写 UserService 和测试

UserService 接口:

/**
 * @outhor Mr.JK
 * @create 2020-04-04  10:08
 */
public interface UserService {
    /**
     * 注册用户
     * @param user
     */
    public void registUser(User user);

    /**
     * 登录
     * @param user
     * @return 如果返回null,说明登录失败,返回有值,说明登录成功
     */
    public User login(User user);

    /**
     * 检查 用户名是否可用
     * @param username
     * @return 返回true表示用户名已存在,返回false表示用户名可用
     */
    public boolean existUsername(String username);

}

UserServiceImpl实现类:

/**
 * @outhor Mr.JK
 * @create 2020-04-04  10:11
 */
public class UserServiceImpl implements UserService {

    private UserDao userdao = new UserDaoImpl();

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

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

    @Override
    public boolean existUsername(String username) {

        if (userdao.queryUserByUsername(username) == null){
            //等于null,说明没查到,表示可用
            return false;
        }
        return true;
    }

}

UserService 测试:

/**
 * @outhor Mr.JK
 * @create 2020-04-04  10:15
 */
public class UserServiceImplTest {
    UserService userService = new UserServiceImpl();

    @Test
    public void registUser() {
        userService.registUser(new User(null,"lin","123456","123@qq.com"));
    }

    @Test
    public void login() {
        System.out.println(userService.login(new User(null,"lin","123456","1")));
    }

    @Test
    public void existUsername() {
        if (userService.existUsername("lin")){
            System.out.println("用户名已存在!");
        }else {
            System.out.println("用户名可用!");
        }
    }
}

7、编写 web 层

7.1、实现用户注册的功能

7.1.1、图解用户注册的流程:

59931024799

7.1.2、修改 regist.html 和 regist_success.html 页面

1、添加 base 标签

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

2、修改 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>

3、修改注册表单的提交地址和请求方式

59931049158

7.1.3、编写 RegistServlet 程序

/**
 * @outhor Mr.JK
 * @create 2020-04-05  16:33
 */
public class RegistServlet extends BaseServlet {
    private UserService userService = new UserServiceImpl();
    
    /**
     * 处理注册的功能
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    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");


        User user = WebUtils.copyParmToBean(req.getParameterMap(), new User());

        // 2、检查 验证码是否正确 === 写死,要求验证码为:abcde
		if ("abcde".equalsIgnoreCase(code)) {
 
		//	3、检查用户名是否可用
 
		if (userService.existsUsername(username)) {
			System.out.println("用户名[" + username + "]已存在!");
			// 跳回注册页面
			req.getRequestDispatcher("/pages/user/regist.html").forward(req, resp);
		} else {
		//可用
 
		// 调用Sservice 保存到数据库
		userService.registUser(new User(null, username, password, email));
 
		//	跳到注册成功页面 regist_success.html
 
    	req.getRequestDispatcher("/pages/user/regist_success.html").forward(req, resp);

    	}
    } else {
    	System.out.println("验证码[" + code + "]错误");
        req.getRequestDispatcher("/pages/user/regist.html").forward(req, resp);
    }

}

7.2、用户登录功能的实现

7.2.1、图解用户登录

59931089602

7.2.2、修改 login.html 页面和 login_success.html 页面

1、添加 base 标签

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

2、修改 base 标签对页面中所有相对路径的影响(浏览器 F12,哪个报红,改哪个)

3、修改 login.html 表单的提交地址和请求方式

2,3如上

7.2.3、LoginServlet 程序

/**
 * @outhor Mr.JK
 * @create 2020-04-05  16:33
 */
public class LoginServlet extends BaseServlet {
    private UserService userService = new UserServiceImpl();
    
    /**
     * 处理登录的功能
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    protected void login(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.获取连接
        String username = req.getParameter("username");
        String password = req.getParameter("password");

        //调用userService.login登录处理业务
        User loginUser = userService.login(new User(null, username, password, null));

        if (loginUser == null){
            //调回登录页面
            req.getRequestDispatcher("/pages/user/login.jsp").forward(req,resp);
        }else {
            //登录成功页面
            //System.out.println("登录成功");
            req.getRequestDispatcher("/pages/user/login_success.jsp").forward(req,resp);
        }
    }
}

第三阶段–前后端整合

1.页面 jsp 动态化

1、在html 页面顶行添加page 指令。

2、修改文件后缀名为:.jsp

3、使用 IDEA 搜索替换.html 为.jsp(快捷键:Ctrl+Shift+R)

59931414760

59931415665

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

1、head 中 css、jquery、base 标签

<%

String basePath = request.getScheme() + "://"
+ request.getServerName() + ":"
+ request.getServerPort()
+ request.getContextPath() + "/";
%>
<%=basePath%>
<!--写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>

2、每个页面的页脚

<div id="bottom">
    <span>
   		 尚硅谷书城.Copyright &copy;2015	
    </span>
</div>

3、登录成功后的菜单

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

4、manager 模块的菜单

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

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

以登录回显为示例:

Servlet 程序端需要添加回显信息到Request 域中

59931442464

jsp 页面,需要输出回显信息

59931444125

4.BaseServlet的抽取

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

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

59931451755

UserServlet 程序:

public class UserServlet extends BaseServlet {
    private UserService userService = new UserServiceImpl();


    /**
     * 处理登录的功能
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    protected void login(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.获取连接
        String username = req.getParameter("username");
        String password = req.getParameter("password");

        //调用userService.login登录处理业务
        User loginUser = userService.login(new User(null, username, password, null));

        if (loginUser == null){
            //调回登录页面
            //把错误信息,和回显的表单项信息,保存到Request域中
            req.setAttribute("msg","用户名或密码错误!");
            req.setAttribute("username",username);
            //System.out.println("登录失败");
            req.getRequestDispatcher("/pages/user/login.jsp").forward(req,resp);
        }else {
            //登录成功页面
            //System.out.println("登录成功");

            //将登陆成功的信息保存报Session域
            req.getSession().setAttribute("user",loginUser);

            req.getRequestDispatcher("/pages/user/login_success.jsp").forward(req,resp);
        }
    }

    /**
     * 处理注册的功能
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    protected void regist(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获取Session中的验证码
        String token = (String)req.getSession().getAttribute(KAPTCHA_SESSION_KEY);
        //删除Session中的验证码
        req.getSession().removeAttribute(KAPTCHA_SESSION_KEY);


        //1.获取请求的参数
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        String email = req.getParameter("email");
        String code = req.getParameter("code");


        User user = WebUtils.copyParmToBean(req.getParameterMap(), new User());

        //2.检查 验证码是否正确 ==== 用谷歌验证码
        if (token != null && token.equalsIgnoreCase(code)){

            //3.检查 用户名是否可用
            if (userService.existUsername(username)){
                //不可用
                System.out.println("用户名[" + username + "]已存在!");
                //把回显信息,保存到Request域中
                req.setAttribute("msg","用户名已存在!");
                req.setAttribute("username",username);
                req.setAttribute("email",email);

                //跳回注册页面
                req.getRequestDispatcher("/pages/user/regist.jsp").forward(req,resp);

            }else{
                //可用,调用Service保存到数据库
                userService.registUser(new User(null,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 doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException,IOException {
    String action = req.getParameter("action"); if ("login".equals(action)) {
   		 login(req, resp);

    	} else if ("regist".equals(action)) {

    regist(req, resp);

    }

}

还要给
login.jsp 添加隐藏域和修改请求地址

59931541031

给 tegist.jsp 页面添加隐藏域 action,和修改请求地址

59931542911

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

protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("UTF-8");//关键 解决post乱码问题
        //解决响应的中文乱码
        resp.setContentType("text/html; charset=UTF-8");
        String action = req.getParameter("action");

        try {
            Method method = this.getClass().getDeclaredMethod(action,HttpServletRequest.class,HttpServletResponse.class);

            method.invoke(this,req,resp);
        } catch (Exception e) {
            e.printStackTrace();
           
    }

代码优化三:抽取BaseServlet 程序。

59931550222

BaseServlet 程序代码:

/**
 * @outhor Mr.JK
 * @create 2020-04-05  18:01
 */
public abstract class BaseServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("UTF-8");//关键 解决post乱码问题
        //解决响应的中文乱码
        resp.setContentType("text/html; charset=UTF-8");
        String action = req.getParameter("action");

        try {
            Method method = this.getClass().getDeclaredMethod(action,HttpServletRequest.class,HttpServletResponse.class);

            method.invoke(this,req,resp);
        } catch (Exception e) {
            e.printStackTrace();
          
    }

    }
}

修改 UserServlet 程序继承BaseServlet 程序。

59931556949

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

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

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

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

​ 1、导入需要的 jar 包:commons-beanutils-1.8.0.jar commons-logging-1.1.1.jar

​ 2、编写 WebUtils 工具类使用:

WebUtils 工具类:

**
 * @outhor Mr.JK
 * @create 2020-04-05  19:57
 */
public class WebUtils {
    public static <T> T  copyParmToBean(Map values, T bean){
        try {
            //使用第三方jar包,将数据封装到Bean对象
            BeanUtils.populate(bean,values);

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

第四阶段–修改回显

以登录为示例:

59931571342

第五阶段–图书模块

MVC 概念

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

MVC 最早出现在 JavaEE 三层中的 Web 层,它可以有效的指导 Web 层的代码如何有效分离,单独工作。

View 视图:只负责数据和界面的显示,不接受任何与显示数据无关的代码,便于程序员和美工的分工合作——

JSP/HTML。

Controller控制器:只负责接收请求,调用业务层的代码处理请求,然后派发页面,是一个“调度者”的角色——Servlet。转到某个页面。或者是重定向到某个页面。

Model 模型:将与业务逻辑相关的数据封装为具体的 JavaBean 类,其中不掺杂任何与数据处理相关的代码——

JavaBean/domain/entity/pojo。

MVC 是一种思想

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

59931631734

MVC 的作用还是为了降低耦合。让代码合理分层。方便后期升级和维护。

1、图书模块

1.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;

1.2、编写图书模块的 JavaBean

public class Book { 
    private Integer id; 
    private String name; 
    private String author;
	private BigDecimal price; 
    private Integer sales; 
    private Integer stock;
	private String imgPath = "static/img/default.jpg";
}

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

Dao 接口

/**
 * @outhor Mr.JK
 * @create 2020-04-06  11:28
 */
public interface BookDao {

    public int addBook(Book book);

    public int deleteBookById(Integer id);

    public int updateBook(Book book);

    public Book queryBookById(Integer id);

    public List<Book> queryBooks();
}

BookDaoImpl 实现类:

/**
 * @outhor Mr.JK
 * @create 2020-04-06  11:31
 */
public class BookDaoImpl extends BaseDao implements BookDao {
    @Override
    public int addBook(Book book) {
        String sql = "insert into t_book(`name`,`author`,`price`,`sales`,`stock`,`img_path`)values(?,?,?,?,?,?)";
        return update(sql,book.getName(),book.getAuthor(),book.getPrice(),book.getSales(),book.getStock(),book.getImgPath());
    }

    @Override
    public int deleteBookById(Integer id) {
        String sql = "delete from t_book where id = ?";
        return update(sql,id);
    }

    @Override
    public int updateBook(Book book) {
        String sql = "update t_book set `name`=?,`author`=?,`price`=?,`sales`=?,`stock`=?,`img_path`=? where id = ?";
        return update(sql,book.getName(),book.getAuthor(),book.getPrice(),book.getSales(),book.getStock(),book.getImgPath(),book.getId());

    }

    @Override
    public Book queryBookById(Integer id) {
        String sql = "select `id` , `name` , `author` , `price` , `sales` , `stock` , `img_path` from t_book where id = ?";
        return queryForOne(Book.class,sql,id);
    }

    @Override
    public List<Book> queryBooks() {
        String sql = "select `id` , `name` , `author` , `price` , `sales` , `stock` , `img_path` from t_book";

        return queryForList(Book.class,sql);
    }
}

BookDao 的测试:

/**
 * @outhor Mr.JK
 * @create 2020-04-06  11:42
 */
public class BookDaoImplTest {
    private BookDao bookDao = new BookDaoImpl();

    @Test
    public void addBook() {
        bookDao.addBook(new Book(null, "帅哥", "坤哥", new BigDecimal(999), 1, 1, null));
    }

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

    @Test
    public void updateBook() {
        bookDao.updateBook(new Book(21, "帅哥秘籍", "坤哥", new BigDecimal(999), 1, 1, null));

    }

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

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

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

BookService 接口

/**
 * @outhor Mr.JK
 * @create 2020-04-06  13:09
 */
public interface BookService {

    public void addBook(Book book);

    public void deleteBookById(Integer id);

    public void updateBook(Book book);

    public Book queryBookById(Integer id);

    public List<Book> queryBooks();
}

BookServiceImpl 实现类:

/**
 * @outhor Mr.JK
 * @create 2020-04-06  13:11
 */
public class BookServiceImpl implements BookService {

    private BookDao bookDao = new BookDaoImpl();

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

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

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

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

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

BookService 的测试:

/**
 * @outhor Mr.JK
 * @create 2020-04-06  13:37
 */
public class BookServiceImplTest {
    private BookService bookService = new BookServiceImpl();

    @Test
    public void addBook() {
        bookService.addBook(new Book(null, "帅哥", "坤哥", new BigDecimal(999), 1, 1, null));

    }

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

    @Test
    public void updateBook() {
        bookService.updateBook(new Book(21, "帅哥秘籍", "坤哥", new BigDecimal(999), 1, 1, null));

    }

    @Test
    public void queryBookById() {
        System.out.println(bookService.queryBookById(27));

    }

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

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

1.5.1、图书列表功能的实现
1、图解列表功能流程:

59931775127

2、BookServlet 程序中添加 list 方法
protected void list(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.通过BookServlet查询全部图书
        List<Book> books = bookService.queryBooks();
        //2.把全部图书保存到Request域中
        req.setAttribute("books",books);
        //3.请求转发到/pages/manager/book_manager.jsp
        req.getRequestDispatcher("/pages/manager/book_manager.jsp").forward(req,resp);

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

59931785268

4、修改 pages/manager/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>

	
</head>

<body>
	
	<div id="header">
			<img class="logo_img" alt="" src="../../static/img/logo.gif" >
			<span class="wel_word">图书管理系统</span>

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

	</div>
	
	<div id="main">
		<table>
			<tr>
				<td>名称</td>
				<td>价格</td>
				<td>作者</td>
				<td>销量</td>
				<td>库存</td>
				<td colspan="2">操作</td>
			</tr>
			<c:forEach items="${requestScope.page.items}" var="book">
				<tr>
					<td>${book.name}</td>
					<td>${book.price}</td>
					<td>${book.author}</td>
					<td>${book.sales}</td>
					<td>${book.stock}</td>
					<td><a href="manager/bookServlet?action=getBook&id=${book.id}&pageNo=${requestScope.page.pageNo}">修改</a></td>
					<td><a class="deleteClass" href="manager/bookServlet?action=delete&id=${book.id}&pageNo=${requestScope.page.pageNo}">删除</a></td>
				</tr>
			</c:forEach>

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

		<%--静态包含分页条--%>
		<%@include file="/pages/common/page_nav.jsp"%>

	</div>


	<%--	静态包含页脚内容--%>
	<%@include file="/pages/common/footer.jsp"%>


</body>
</html>
1.5.2、前后台的简单介绍

59931800845

1.5.3、添加图书功能的实现
1.5.3.1、添加图书流程细节:

59931805614

1.5.3.2、问题说明:表单重复提交:

当用户提交完请求,浏览器会记录下最后一次请求的全部信息。当用户按下功能键 F5,就会发起浏览器记录的最后一次请求。

1.5.3.3、BookServlet 程序中添加 add 方法
protected void add(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        int pageNo = WebUtils.parseInt(req.getParameter("pageNo"),0);
        pageNo+=1;

        //1.获取请求的参数==封装成为Book对象
        Book book = WebUtils.copyParmToBean(req.getParameterMap(), new Book());

        //2.调用BookService.addBook()保存图书
        bookService.addBook(book);

        //3.跳出图书列表页面
        //  /manager/bookServlet?action=list

        //这里跳转要用重定向,不能用请求转发,会有bug,F5刷新,会一直添加
        resp.sendRedirect(req.getContextPath() + "/manager/bookServlet?action=page&pageNo=" + pageNo);

    }
1.5.3.4、修改 book_edit.jsp 页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="cmn-hans">
<head>
<meta charset="UTF-8">
<title>编辑图书</title>

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

	<style type="text/css">
	h1 {
		text-align: center;
		margin-top: 200px;
	}
	
	h1 a {
		color:red;
	}
	
	input {
		text-align: center;
	}
</style>
</head>
<body>
		<div id="header">
			<img class="logo_img" alt="" src="../../static/img/logo.gif" >
			<span class="wel_word">编辑图书</span>

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

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


		<%--	静态包含页脚内容--%>
		<%@include file="/pages/common/footer.jsp"%>


</body>
</html>
1.5.4、删除图书功能的实现
1.5.4.1、图解删除流程:

59931822216

1.5.4.2、BookServlet 程序中的 delete 方法:
protected void delete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.获取请求的参数id,图书编号
        int id = WebUtils.parseInt(req.getParameter("id"),0);

        //2.调用bookServlet.deleteBookById();删除图书
        bookService.deleteBookById(id);

        //3.重定向回图书列表管理页面
        resp.sendRedirect(req.getContextPath() + "/manager/bookServlet?action=page&pageNo=" + req.getParameter("pageNo"));

    }
1.5.4.3、给 WebUtils 工具类添加转换 int 类型的工具方法
/**
     * 将字符串转换为int类型的
     * @param strInt
     * @param defaultValue
     * @return
     */
    public static int parseInt(String strInt,int defaultValue){
        if (strInt == null){//前台客户端传参一定会是null,null转integer会报错
            return defaultValue;
        }

        try {
            return Integer.parseInt(strInt);
        } catch (Exception e) {
            //e.printStackTrace();

        }

        return defaultValue;
    }
1.5.4.4、修改删除的连接地址:

59931839768

1.5.4.5、给删除添加确认提示操作:
<script type="text/javascript">
    $(function () {
        // 给删除的a 标签绑定单击事件,用于删除的确认提示操作
        $("a.deleteClass").click(function () {
        // 在事件的function 函数中,有一个this 对象。这个this 对象,是当前正在响应事件的dom 对象。
        /**
        * confirm 是确认提示框函数
        * 参数是它的提示内容
        * 它有两个按钮,一个确认,一个是取消。
        * 返回true 表示点击了,确认,返回false 表示点击取消。
        */
        return confirm("你确定要删除【" + $(this).parent().parent().find("td:first").text() + "】?"); 	
           // return false// 阻止元素的默认行为===不提交请求
        });
    });
</script>
1.5.5、修改图书功能的实现
1.5.5.1:图解修改图书细节:

59931849359

1.5.5.2、更新【修改】的请求地址:

59931855776

1.5.5.3、BookServlet 程序中添加 getBook 方法:
protected void getBook(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.获取请求的参数图书编号
        int id = WebUtils.parseInt(req.getParameter("id"),0);
        //2.调用bookServlet.queryBookId查询图书编号
        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);

    }
1.5.5.4、在 book_edit.jsp 页面中显示修改的数据
<div id="main">
			<form action="manager/bookServlet" method="post" >
				<input type="hidden" name="pageNo" value="${param.pageNo}" />
				<input type="hidden" name="action" value="${empty param.id ? "add":"update"}" />
				<input type="hidden" name="id" value="${requestScope.book.id}" />
				<table>
					<tr>
						<td>名称</td>
						<td>价格</td>
						<td>作者</td>
						<td>销量</td>
						<td>库存</td>
						<td colspan="2">操作</td>
					</tr>		
					<tr>
						<td><input name="name" type="text" value="${requestScope.book.name}"/></td>
						<td><input name="price" type="text" value="${requestScope.book.price}"/></td>
						<td><input name="author" type="text" value="${requestScope.book.author}"/></td>
						<td><input name="sales" type="text" value="${requestScope.book.sales}"/></td>
						<td><input name="stock" type="text" value="${requestScope.book.stock}"/></td>
						<td><input type="submit" value="提交"/></td>
					</tr>	
				</table>
			</form>
			
	
		</div>
1.5.5.5、在 BookServlet 程序中添加 update 方法:
protected void update(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.获取请求的参数==封装称为Book对象
        Book book = WebUtils.copyParmToBean(req.getParameterMap(), new Book());

        //2.调用BookServlet.update
        bookService.updateBook(book);

        //3.重定向回图书列表管理页面
        resp.sendRedirect(req.getContextPath() + "/manager/bookServlet?action=page&pageNo=" + req.getParameter("pageNo"));

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

59931876110


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

书城项目后篇 Previous
学成在线:项目总结 Next