JavaWeb中的MVC 下

代碼較多,請先略過代碼,看懂邏輯在研究代碼java

引入

回顧上一節中的項目,最終的層次結構:mysql

image-20191217093732634

在MVC上中,咱們分析了MVC設計模式具有的優勢,以及不足,並在其基礎上增了Service層用於處理業務邏輯,可是這還沒完,對於大型項目來講,程序結構依然是不夠清晰的,Service層不只要處理業務邏輯,還要處理數據庫操做;依然存在如下問題:sql

  • 耦合度高
  • 代碼複用性差
  • Service層的功能職責不單一

DAO

爲了解決這些問題,須要將Service層進一步的解耦,如何作到呢,經過增長數據訪問層DAO便可實現;數據庫

定義:

DAO,(Data Access Object),數據訪問對象,指提供了對數據的CRUD功能的對象;,負責與數據庫打交道,處於業務邏輯層和數據庫之間;設計模式

簡單的說:mvc

就是把本來在Service層中的數據庫操做封裝到DAO層中去;使得Service層能夠專一於業務邏輯的處理,下降各功能間的耦合度;oracle

增長DAO後的層次結構圖:

image-20191217094327970

DAO層的組成

DAO是針對數據訪問層的設計模式,其完整組成包括以下部分:框架

image-20191217095439214
看起來很是複雜,沒錯,DAO是一個用於解決數據庫操做的完整解決方案,若是按照上述結構來實現的話,對於大型商業項目而言很是的規範,可是對小型的須要,快速開發的項目而言,讓人望而生畏函數

老司機建議: 過分設計會讓人看不清本質,學習設計模式時必定要牢記你要解決的關鍵問題,帶着問題去看各部分的做用和重要性 學習

實例

爲了可以更清晰的認識到DAO的本質,這裏採用簡化後的DAO設計

DAO中有三個對象是必須的:

  • 數據訪問對象(Bean)
  • 數據庫鏈接類
  • DAO實現類

最終Service層須要的是DAO實現對象,數據庫鏈接對象是爲了將重複代碼進行抽取而存在的,Bean也是在原來系統中已經存在的

如今須要兩個新的類一個DAO,一個數據庫鏈接類,建立它們像下面這樣

image-20191217104510787

DBTool:

數據庫鏈接類,負責鏈接數據庫執行查詢,最後關閉資源

package com.kkb.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.kkb.models;

import com.kkb.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.kkb.srvices;

import com.kkb.exceptions.LoginException;
import com.kkb.models.UserBean;
import com.kkb.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框架? 加油吧騷年

相關文章
相關標籤/搜索