代码较多,请先略过代码,看懂逻辑在研究代码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框架? 加油吧骚年