代碼較多,請先略過代碼,看懂邏輯在研究代碼java
回顧上一節中的項目,最終的層次結構:mysql
在MVC上中,咱們分析了MVC設計模式具有的優勢,以及不足,並在其基礎上增了Service層用於處理業務邏輯,可是這還沒完,對於大型項目來講,程序結構依然是不夠清晰的,Service層不只要處理業務邏輯,還要處理數據庫操做;依然存在如下問題:sql
爲了解決這些問題,須要將Service層進一步的解耦,如何作到呢,經過增長數據訪問層DAO便可實現;數據庫
DAO,(Data Access Object),數據訪問對象,指提供了對數據的CRUD功能的對象;,負責與數據庫打交道,處於業務邏輯層和數據庫之間;設計模式
簡單的說:mvc
就是把本來在Service層中的數據庫操做封裝到DAO層中去;使得Service層能夠專一於業務邏輯的處理,下降各功能間的耦合度;oracle
DAO是針對數據訪問層的設計模式,其完整組成包括以下部分:框架
看起來很是複雜,沒錯,DAO是一個用於解決數據庫操做的完整解決方案,若是按照上述結構來實現的話,對於大型商業項目而言很是的規範,可是對小型的須要,快速開發的項目而言,讓人望而生畏函數
老司機建議: 過分設計會讓人看不清本質,學習設計模式時必定要牢記你要解決的關鍵問題,帶着問題去看各部分的做用和重要性 學習
爲了可以更清晰的認識到DAO的本質,這裏採用簡化後的DAO設計
DAO中有三個對象是必須的:
最終Service層須要的是DAO實現對象,數據庫鏈接對象是爲了將重複代碼進行抽取而存在的,Bean也是在原來系統中已經存在的
如今須要兩個新的類一個DAO,一個數據庫鏈接類,建立它們像下面這樣
數據庫鏈接類,負責鏈接數據庫執行查詢,最後關閉資源
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; } }
負責爲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; } }
替換原來使用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中的方法不是固定的要根據具體業務需求來設計
設計模式就像是把雙刃劍,帶來了擴展性,維護性,等等優點等的同時,設計模式的加入也會使程序變得更加複雜
咱們要作的是在合適的項目中採用合適的設計模式
上述案例中沒有使用工廠和接口,那啥時候用呢?
當項目發展到後期,公司賺到大錢了,想要替換更強大的Oracle數據庫,oracle的sql有細微的差異,因而不得不從新寫一套新的DAO實現,寫完後你又不得不查找全部使用了UserDao的地方,所有修改一遍,你第一次哭出聲來...
聰明的你,不會讓本身再遇到這種狀況,因而你就........辭職了!
因爲二者之間方法全都同樣,僅僅是SQL語句不一樣,因此爲它們抽取一個接口,再定義一個用於建立對象的工廠類,從此要更換實現類的時候,修改工廠類便可更換Dao的實現類
總體結構如圖:
若是最後在提供一個配置文件,讓Factory到配置文件中獲取須要建立的Dao類型,一切就完美了!
到這裏MVC+Service+DAO的設計模式就完事了
等等,我隱約記得第一篇中的什麼問題沒解決?
這個問題其實解決方案很簡單:
先將須要處理的請求映射到Servlet,而後在Servlet中根據請求路徑來選擇對應的處理方法,
固然如何將路徑匹配到對應的方法也有不一樣的方法
SpringMVC採用的就是第三種方法
是否是想本身寫一個MVC框架? 加油吧騷年