javaweb学习(六)——从MVC角度来理解SMBMS项目

从MVC角度来理解SMBMS项目

登录功能

View

首先是V(View)层,也就是展示数据的前端界面,给servlet提供数据发起请求的一环:
Login.jsp

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

<html>
<head lang="en">
    <meta charset="UTF-8">
    <title>系统登录 - 超市订单管理系统</title>
    <link type="text/css" rel="stylesheet" href="${pageContext.request.contextPath}/css/style.css"/>
</head>
<body class="login_bg">
<section class="loginBox">
    <header class="loginHeader">
        <h1>超市订单管理系统</h1>
    </header>
    <section class="loginCont">
        <form class="loginForm" action="${pageContext.request.contextPath}/login.do" method="post" name="actionForm"
              id="actionForm">
            <div class="info">${error}</div>
            <div class="inputbox">
                <label for="userCode">用户名:</label>
                <input type="text" class="input-text" id="userCode" name="userCode" placeholder="请输入用户名" required/>
            </div>
            <div class="inputbox">
                <label for="userPassword">密码:</label>
                <input type="password" id="userPassword" name="userPassword" placeholder="请输入密码" required/>
            </div>
            <div class="subBtn">
                <input type="submit" value="登录"/>
                <input type="reset" value="重置"/>
            </div>
        </form>
    </section>
</section>
</body>
</html>

这部分不需要自己练习去写,因此这也不是我写的,我们只需要知道这里面的一些重点内容即可,比如这网页的标题是:系统登录 - 超市订单管理系统,通过一个form表单获取登录的用户名userCode和密码userPassword,用作后续的验证。

Controller

然后是C(Controller)层,也就是Servlet类所在的层,主要作用就是接收用户的请求 比如req的请求参数、Session信息等,还有控制视图的跳转的代码以及交给业务层处理对应的代码。
LoginServlet.java

public class LoginServlet extends HttpServlet {
    //Servlet控制层,调用业务层代码
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        System.out.println("LoginServlet Satrt...");

        //获取用户、密码
        String userCode = req.getParameter("userCode");
        String userPassword = req.getParameter("userPassword");

        //和数据库中的密码进行对比,调用业务层
        UserServiceImpl userService = new UserServiceImpl();
        User user = userService.login(userCode, userPassword);  //这里将登录的人查到了

        if(user!=null){//查有此人,可以登录
            //将用户信息放到Session中
            req.getSession().setAttribute(Constants.USER_SESSION, user);

            //跳转到内部主页
            resp.sendRedirect("jsp/frame.jsp");
        }else{//查无此人,转发会登录页面,提示登录错误
            req.setAttribute("error", "用户名或密码不正确");
            req.getRequestDispatcher("login.jsp").forward(req, resp);
        }
    }

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

这里servlet做的事情很简单,通过req.gerParameter获取前端输入的参数,并且调用业务层Service的代码,查询数据库中是否有对应的信息,如果有则将信息保存到Session中,并跳转到内部主页(或者此人的定制主页),如果没有则提示错误信息且转发到登陆界面Login.jsp。

Model

M(Model)层包括业务层和数据持久层,是代码量最多的一层,通常要做业务逻辑(Service)和CRUD(Dao——数据持久)。

业务层

做法通常是写一个Interface接口,然后在用一个实现类去实现对应的方法:
UserService.java

public interface UserService {

    //用户登录
    public User login(String userCode, String password);
}

UserServiceImpl.java

public class UserServiceImpl implements UserService{

    //业务层都会调用Dao层,所以要引用Dao层
    private UserDao userDao;
    public UserServiceImpl(){
        userDao = new UserDaoImpl();
    }

    @Override
    public User login(String userCode, String password) {

        Connection connection = null;
        User user = null;

        try {
            connection = BaseDao.gerConnection();

            //通过业务层调用对应的具体数据库操作
            user = userDao.getLoginUser(connection, userCode, password);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            BaseDao.closeResourse(connection, null, null);
        }
        return user;
    }

注意这里的特定写法,业务层都会调用Dao层,因此要先引用Daoprivate UserDao userDao;
然后在无参构造器里初始化这个Dao对象,这样程序调用业务层时,无参构造里的Dao对象也就被实例化了。回到我们的登录功能的业务逻辑,输入参数需要前面获取到的用户名和密码,然后调用操作数据库的公共类BaseDao,调用里面的gerConnection方法连接上数据库,然后通过try/catch调用userDao的数据库操作方法getLoginUser,就可以查到是否有对应的用户了。

Dao层

Date Access Object 数据访问对象,用来对应数据库实体,负责与数据库进行联络的一些任务都封装在此。首先要一个访问数据库的公共类,在里面实现获取数据库连接、查询方法、增删改方法以及关闭连接释放资源的方法。
BaseDao.java

package com.tunan.dao;

import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

/**
 * @author Tunan
 * @Date 2021/9/12 18:52
 * 操作数据库的公共类
 */
public class BaseDao {
    private static String driver;
    private static String url;
    private static String username;
    private static String password;

    //静态代码块,类加载时候就初始化了
    static {
        Properties properties = new Properties();
        //通过类加载器读取对应的资源
        InputStream is = BaseDao.class.getClassLoader().getResourceAsStream("db.properties");
        try {
            properties.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        }

        driver = properties.getProperty("driver");
        url = properties.getProperty("url");
        username = properties.getProperty("username");
        password = properties.getProperty("password");
    }

    //获取数据库的链接
    public static Connection gerConnection(){
        Connection connection = null;
        try {
            Class.forName(driver);
            connection = DriverManager.getConnection(url, username, password);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return connection;
    }

    //编写查询公共类
    public static ResultSet execute(Connection connection, PreparedStatement preparedStatement, ResultSet resultSet, String sql,Object[] params) throws SQLException {
        //预编译的SQL,后面直接执行即可
        preparedStatement = connection.prepareStatement(sql);

        for (int i = 0; i < params.length; i++) {
            //setObject占位符从1开始,但数组从0开始
            preparedStatement.setObject(i+1,params[i]);
        }

        resultSet = preparedStatement.executeQuery();
        return resultSet;

    }

    //编写增删改查公共方法
    public static int execute(Connection connection, PreparedStatement preparedStatement, String sql,Object[] params) throws SQLException {
        preparedStatement = connection.prepareStatement(sql);

        for (int i = 0; i < params.length; i++) {
            //setObject占位符从1开始,但数组从0开始
            preparedStatement.setObject(i+1,params[i]);
        }

        int updataRows = preparedStatement.executeUpdate();
        return updataRows;

    }


    //释放资源
    public static boolean closeResourse(Connection connection, PreparedStatement preparedStatement, ResultSet resultSet){
        boolean flag = true;
        if(resultSet != null) {
            try {
                resultSet.close();
                //GC垃圾回收机制回收
                resultSet = null;
            } catch (SQLException throwables) {
                throwables.printStackTrace();
                flag = false;
            }
        }

        if(preparedStatement != null) {
            try {
                preparedStatement.close();
                //GC垃圾回收机制回收
                preparedStatement = null;
            } catch (SQLException throwables) {
                throwables.printStackTrace();
                flag = false;
            }
        }

        if(connection != null) {
            try {
                connection.close();
                //GC垃圾回收机制回收
                connection = null;
            } catch (SQLException throwables) {
                throwables.printStackTrace();
                flag = false;
            }
        }

        return flag;
    }
}

然后是分模块的不同的Dao层,这里面就会有大量的代码重复

UserDaoImpl.java

public class UserDaoImpl implements UserDao{

    //得到要登陆的用户
    @Override
    public User getLoginUser(Connection connection, String userCode, String userPassword) throws SQLException {

        PreparedStatement pstm = null;
        ResultSet rs = null;
        User user = null;
        
        if(connection != null) {

            String sql = "select * from smbms_user where userCode=? and userPassword=?";
            Object[] params = {userCode, userPassword};

            rs = BaseDao.execute(connection, pstm, rs, sql, params);

            if(rs.next()){
                user = new User();
                user.setId(rs.getInt("id"));
                user.setUserCode(rs.getString("userCode"));
                user.setUserName(rs.getString("userName"));
                user.setUserPassword(rs.getString("userPassword"));
                user.setGender(rs.getInt("gender"));
                user.setBirthday(rs.getDate("birthday"));
                user.setPhone(rs.getString("phone"));
                user.setAddress(rs.getString("address"));
                user.setUserRole(rs.getInt("userRole"));
                user.setCreatedBy(rs.getInt("createdBy"));
                user.setCreationDate(rs.getTimestamp("creationDate"));
                user.setModifyBy(rs.getInt("modifyBy"));
                user.setModifyDate(rs.getTimestamp("modifyDate"));
            }

            BaseDao.closeResourse(null, pstm, rs);
        }

        return user;

    }
}

这里面的Sql使用了预编译类型的PreparedStatement ,通过占位符和BaseDao里面的preparedStatement.setObject方法进行传参,防止了Sql注入,但也使代码有些臃肿,后面学习的框架以及动态Sql就会解决这一点。查询到的rs是一个链表,将结果的属性全部获取,放入Session中预备着给网页后续再用。注意最后一步关闭连接释放资源。

总结

jsp和servlet虽然已经不那么常用,写起来也十分的冗余复杂,但是还是值得学习的,对后面框架学习的底层理解会有一定的帮助。通过梳理登录功能的MVC还是可以看出,每一块的功能十分明确,自己写一遍会增加印象和理解。但是开发的时候还是从底层向上开发,也就是先开发Dao层和业务层的代码,并且多做@Test测试!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值