zoukankan      html  css  js  c++  java
  • JavaWeb中的MVC 下

    代码较多,请先略过代码,看懂逻辑在研究代码

    引入

    回顾上一节中的项目,最终的层次结构:

    image-20191217093732634

    在MVC上中,我们分析了MVC设计模式具备的优点,以及不足,并在其基础上增了Service层用于处理业务逻辑,但是这还没完,对于大型项目来说,程序结构依然是不够清晰的,Service层不仅要处理业务逻辑,还要处理数据库操作;依然存在以下问题:

    • 耦合度高
    • 代码复用性差
    • Service层的功能职责不单一

    DAO

    为了解决这些问题,需要将Service层进一步的解耦,如何做到呢,通过增加数据访问层DAO即可实现;

    定义:

    DAO,(Data Access Object),数据访问对象,指提供了对数据的CRUD功能的对象;,负责与数据库打交道,处于业务逻辑层和数据库之间;

    简单的说:

    就是把原本在Service层中的数据库操作封装到DAO层中去;使得Service层可以专注于业务逻辑的处理,降低各功能间的耦合度;

    增加DAO后的层次结构图:

    image-20191217094327970

    DAO层的组成

    DAO是针对数据访问层的设计模式,其完整组成包括如下部分:

    image-20191217095439214
    看起来非常复杂,没错,DAO是一个用于解决数据库操作的完整解决方案,如果按照上述结构来实现的话,对于大型商业项目而言非常的规范,但是对小型的需要,快速开发的项目而言,让人望而生畏

    老司机建议: 过度设计会让人看不清本质,学习设计模式时一定要牢记你要解决的关键问题,带着问题去看各部分的作用和重要性

    实例

    为了能够更清晰的认识到DAO的本质,这里采用简化后的DAO设计

    DAO中有三个对象是必须的:

    • 数据访问对象(Bean)
    • 数据库连接类
    • DAO实现类

    最终Service层需要的是DAO实现对象,数据库连接对象是为了将重复代码进行抽取而存在的,Bean也是在原来系统中已经存在的

    现在需要两个新的类一个DAO,一个数据库连接类,创建它们像下面这样

    image-20191217104510787

    DBTool:

    数据库连接类,负责连接数据库执行查询,最后关闭资源

    package com.yyh.test;
    
    import java.sql.*;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    
    public class DBTool {
    
        //默认参数
        public   String ip = "127.0.0.1";
        public   int port = 3306;
        public   String
                user="root",
                password="admin",
                charset ="utf8",
                dbName="db1";
        private static boolean DriverLoaded=false;
    
        //使用默认参数链接数据库
        public DBTool() throws ClassNotFoundException {
            if(DriverLoaded)return;
            try {
                Class.forName("com.mysql.jdbc.Driver");
                System.out.println("DBTools message:数据库驱动加载成功!");
            } catch (ClassNotFoundException e) {
                System.out.println("DBTools Error:驱动程序加载失败!");
                throw e;
            }
            DriverLoaded=true;
        }
    
        //自定义参数初始化
        public DBTool(String ip, int port, String user, String password, String dbName) throws ClassNotFoundException {
            this();
            this.ip = ip;
            this.port = port;
            this.user = user;
            this.password = password;
            this.dbName = dbName;
        }
    
        //自定义参数初始化
        public DBTool(String user, String password, String dbName) throws ClassNotFoundException {
            this();
            this.user = user;
            this.password = password;
            this.dbName = dbName;
        }
    
    
        //获取一个链接
        public Connection getConnection() throws SQLException {
            String url = String.format("jdbc:mysql://%s:%s/%s?characterEncoding=%s&user=%s&password=%s&useSSL=false",ip,port,dbName,charset,user,password);
            try {
                return DriverManager.getConnection(url);
            } catch (SQLException e) {
                System.out.println("DBTools Error 数据库连接失败!");
                throw e;
            }
        }
    
        //执行查询语句
        public List<Map<String,Object>> executeQuery(String sql, Object...args) throws SQLException {
            ArrayList<Map<String, Object>> res = new ArrayList<>();
    
            ResultSet resultSet = null;
            PreparedStatement preparedStatement = null;
            Connection connection = null;
            try {
                connection = getConnection();
                preparedStatement = getPreparedStatement(connection, sql, args);
                resultSet = preparedStatement.executeQuery();
                while (resultSet.next()) {
                    resultSet.getMetaData().getColumnCount();
                    HashMap<String, Object> map = new HashMap<>();
                    for (int i = 1; i <= resultSet.getMetaData().getColumnCount() ; i++) {
                        map.put(resultSet.getMetaData().getColumnName(i),resultSet.getObject(i));
                    }
                    res.add(map);
                }
            } catch (SQLException e) {
                e.printStackTrace();
                throw e;
            } finally {
                if(resultSet != null)
                    resultSet.close();
                if(preparedStatement != null)
                    preparedStatement.close();
                if(connection != null)
                    connection.close();
            }
            return res;
        }
    
        //sql参数预处理
        private PreparedStatement getPreparedStatement(Connection connection, String sql, Object[] args) throws SQLException {
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            int count = sql.length() - sql.replace("?", "").length();
            if(count != args.length){
                throw new SQLException("DBTool Error: 参数个数不匹配");
            }
            for (int i = 0; i < args.length; i++) {
                preparedStatement.setObject(i+1,args[i]);
            }
            return preparedStatement;
        }
    
        //执行更新语句
        public boolean executeUpdate(String sql,Object...args) throws SQLException {
    
            try {
                Connection connection = getConnection();
                PreparedStatement preparedStatement = getPreparedStatement(connection, sql, args);
                int i = preparedStatement.executeUpdate();
                if (i>0){return true;}
            } catch (SQLException e) {
                e.printStackTrace();
                throw e;
            }
            return false;
        }
    }
    

    UserDao:

    负责为Service层提供需要的CURD方法,本质就是封装了SQL的执行,和结果的解析

    package com.yyh.models;
    
    import com.yyh.test.DBTool;
    
    import java.sql.SQLException;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Map;
    
    public class UserDao {
        private DBTool tools;
    
        //构造函数
        public UserDao() throws ClassNotFoundException {
            tools = new DBTool();
        }
    
        //新增用户
        public boolean insertUser(UserBean user) throws SQLException {
            String sql = "insert into user values(null,?,?)";
            return tools.executeUpdate(sql, user.getName(), user.getPwd());
    
        }
    
        //删除用户
        public boolean deleteUser(UserBean user) throws SQLException {
            String sql = "delete from user where id = ?";
            return tools.executeUpdate(sql, user.getId());
    
        }
    
        //更新用户
        public boolean updateUser(UserBean user) throws SQLException {
            String sql = "update user set name = ? , pwd = ?  where id = ?";
            return tools.executeUpdate(sql, user.getName(), user.getPwd(), user.getId());
        }
    
        //查询所有用户
        public List<UserBean> queryAllUser() throws SQLException {
            ArrayList<UserBean> beans = new ArrayList<>();
    
            List<Map<String, Object>> maps = tools.executeQuery("select *from user");
            //转List
            for (Map<String, Object> temp : maps) {
                UserBean bean = getUserBean(temp);
                beans.add(bean);
            }
            return beans;
        }
    
        //map 转 bean 方法
        private UserBean getUserBean(Map<String, Object> temp) {
            UserBean bean = new UserBean();
            bean.setId((Integer) temp.get("id"));
            bean.setName((String) temp.get("name"));
            bean.setPwd((String) temp.get("pwd"));
            return bean;
        }
    
        //通过ID查询
        public UserBean queryUserByID(Integer id) throws SQLException {
            List<Map<String, Object>> maps = tools.executeQuery("select *from user where id = ?", id);
            //转List
            for (Map<String, Object> temp : maps) {
                UserBean bean = getUserBean(temp);
                return bean;
            }
            return null;
        }
    
        //登录认证
        public UserBean checkLogin(UserBean login) throws SQLException {
            List<Map<String, Object>> maps = tools.executeQuery("select *from user where name = ? and pwd = ?", login.getName(), login.getPwd());
            for (Map<String, Object> temp : maps) {
                UserBean bean = getUserBean(temp);
                return bean;
            }
            return null;
        }
    
        //通过名字查询
        public UserBean queryUserByName(String name) throws SQLException {
            String sql = "select *from user where name = ?";
            List<Map<String, Object>> maps = tools.executeQuery(sql, name);
            if (!maps.isEmpty()){
                return getUserBean(maps.get(0));
            }
            return null;
        }
    }
    

    UserService:

    替换原来使用JDBC的地方,改为使用UserDao来完成数据库操作

    package com.yyh.srvices;
    
    import com.yyh.exceptions.LoginException;
    import com.yyh.models.UserBean;
    import com.yyh.models.UserDao;
    
    import java.sql.*;
    
    //用来处理与用户相关的业务逻辑
    public class UserService {
    
        private UserDao dao;
    
        public UserService() throws ClassNotFoundException {
            this.dao = new UserDao();
        }
    
        //用于检查登录的方法
        public UserBean checkLogin(UserBean reqBean) throws LoginException {
            //判断参数是否有效
            if(reqBean.getName() == null || reqBean.getPwd() == null ||
                reqBean.getName().equals("") || reqBean.getPwd().equals("")
            ){
                throw new LoginException("用户名或密码不能为空!");
            }else {
                try {
                    UserBean bean = dao.checkLogin(reqBean);
                    if(bean != null)
                        return bean;
                    else
                        throw new LoginException("用户名或密码错误!");
                } catch (SQLException e) {
                    e.printStackTrace();
                    throw new LoginException("数据库炸了!");
                }
            }
        }
    }
    

    如此,就利用DAO(数据访问对象),来对Service层进行了解耦合,代码的可维护性提高了,但是相应的程序的复杂度也提高了

    强调:

    DAO中的方法不是固定的要根据具体业务需求来设计

    MVC+DAO执行流程:

    image-20191217122357207

    工厂和接口呢?

    设计模式就像是把双刃剑,带来了扩展性,维护性,等等优势等的同时,设计模式的加入也会使程序变得更加复杂

    我们要做的是在合适的项目中采用合适的设计模式

    上述案例中没有使用工厂和接口,那啥时候用呢?

    分析:

    当项目发展到后期,公司赚到大钱了,想要替换更强大的Oracle数据库,oracle的sql有细微的差别,于是不得不重新写一套新的DAO实现,写完后你又不得不查找所有使用了UserDao的地方,全部修改一遍,你第一次哭出声来...

    image-20191217112222623

    聪明的你,不会让自己再遇到这种情况,于是你就........辞职了!

    总结:

    由于两者之间方法全都一样,仅仅是SQL语句不同,所以为它们抽取一个接口,再定义一个用于创建对象的工厂类,今后要更换实现类的时候,修改工厂类即可更换Dao的实现类

    整体结构如图:

    image-20191217114358098

    如果最后在提供一个配置文件,让Factory到配置文件中获取需要创建的Dao类型,一切就完美了!

    到这里MVC+Service+DAO的设计模式就完事了

    一个Servlet处理多个不同功能的请求

    等等,我隐约记得第一篇中的什么问题没解决?

    这个问题其实解决方案很简单:

    ​ 先将需要处理的请求映射到Servlet,然后在Servlet中根据请求路径来选择对应的处理方法,

    ​ 当然如何将路径匹配到对应的方法也有不同的方法

    1. 直接在Servlet中判断路径然后调用方法,(比较low)
    2. 通过反射,查找与路径匹配的方法(方法名称相同,局限)
    3. 通过反射+注解,方法名称可以随意,用注解提供路径即可(灵活,高端)

    SpringMVC采用的就是第三种方法

    是不是想自己写一个MVC框架? 加油吧骚年

  • 相关阅读:
    Android NDK pthreads详细使用
    Android 音视频深入 十七 FFmpeg 获取RTMP流保存为flv (附源码下载)
    Android事件分发机制
    Gradle之FTP文件下载
    JVM内存回收机制
    Git如何把本地代码推送到远程仓库
    Android 进程间通讯方式
    微信小程序之文件系统初探
    时间选择器组件之关于table走过的弯路
    腾讯地图JavaScript API GL实现文本标记的碰撞避让
  • 原文地址:https://www.cnblogs.com/yangyuanhu/p/12053840.html
Copyright © 2011-2022 走看看