申明:本文采用自己 C3P0 连接池工具进行测试

自定义的 JDBCUtils 可以获取 Connection:
package com.test.utils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement; import javax.sql.DataSource; import com.mchange.v2.c3p0.ComboPooledDataSource; public class JDBCUtils {
private static ComboPooledDataSource dataSource = new ComboPooledDataSource(); public static Connection getConnection() {
try {
return dataSource.getConnection();
} catch (SQLException e) {
throw new RuntimeException("数据连接获取失败!");
}
} public static DataSource getDataSource() {
return dataSource;
} /**
* 释放资源
* @param conn
* @param st
* @param rs
*/
public static void colseResource(Connection conn,Statement st,ResultSet rs) {
closeResultSet(rs);
closeStatement(st);
closeConnection(conn);
} /**
* 释放连接 Connection
* @param conn
*/
public static void closeConnection(Connection conn) {
if(conn !=null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
//等待垃圾回收
conn = null;
} /**
* 释放语句执行者 Statement
* @param st
*/
public static void closeStatement(Statement st) {
if(st !=null) {
try {
st.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
//等待垃圾回收
st = null;
} /**
* 释放结果集 ResultSet
* @param rs
*/
public static void closeResultSet(ResultSet rs) {
if(rs !=null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
//等待垃圾回收
rs = null;
}
}

一、DBUtils 简介

  1.1 什么是 DBUtils:

    Dbutils是由Apache公司提供。

    主要是封装了JDBC的代码,简化dao层的操作。帮助java程序员,开发Dao层代码的简单框架。

    框架的作用:帮助程序员,提高程序的开发效率。
  1.2 为什么需要 Dbutils (为什么不用 JDBC):
    在使用 DBUtils 之前,我们Dao层使用的技术是 JDBC,那么分析一下JDBC的弊端:

            - 数据库链接对象、sql语句操作对象,封装结果集对象,这三大对象会重复定义
            - 封装数据的代码重复,而且操作复杂,代码量大
            - 释放资源的代码重复
  二、基础知识准备
  2.1 元信息获取
    数据库元信息包括:数据库产品本身和表等的定义信息。
    元信息包括:
      - 数据库的元信息:
      - 参数元信息:执行的 SQL 语句中的占位符元信息(个数,参数类型等)。
      - 结果集元信息:字段的类型
   2.2 数据库的元信息获取
    创建 Connection 之后,通过 Connection 对象的 getMetaData() 方法进行数据库元信息的获取
    效果图:
    

    从上图中可以看出 Connection 的 getMetaData() 方法可以获取用户使用的是什么数据库的 Connecton

    当框架知道了数据库的连接是哪个数据库来的,则可以用相应的 SQL 语句进行数据库操作。

   2.3 参数的元信息获取

    参数的元信息是指:SQL语句中的参数个数、参数所对应的字段类型等。

    搞清楚元信息怎么获取之前,要思考一下为什么要获取参数的元信息,看下图为一个正常的 JDBC 数据查询操作:

    

    站在程序员的角度来说,我们当然能看出 SQL 语句中需要多少个参数,因此向 Statement 中 set 参数的时候,传入相应对的占位符位置号:1,2,……

    那么如果站在框架的角度,当用户扔一个 sql 语句给框架,让框架给我执行 SQL 语句,

    比如 “ select * from student where name like ? and age in (? ,?)” 这样的 sql 语句,框架怎么知道这个 sql 里面有几个占位符呢?

    这里就要解释一下:Statement 接口的牛逼之处,只要使用 “?” 作为占位符,通过 getParameterMetaData() 方法就能获取到

    sql 语句中的占位符 “?”的个数。来一个极端的例子:

    

    2.4 结果集元信息的获取

    获取结果集中的列表的字段个数、类型等信息

    

    总结:  

      元信息获取方法:

        - 数据库元信息:

          DatabaseMetaData metaData = conn.getMetaData();  // 得到数据库的元信息集合

        - 参数元信息:

          ParameterMetaData pmd = st.getParameterMetaData();   // 得到参数的元信息集合
        - 结果集元信息:

          ResultSetMetaData metaData = rs.getMetaData();   // 得到结果集的元信息集合

 三、DBUtils 工具框架编写

  3.1 框架不依赖数据源:

    自定义一个 BDAssist 类来模拟 DBUtils,创建一个私有的 DataSource 属性,创建构造函数,当调用者调用的时候,把数据源传进来,

    框架拿到数据源的具体实例,框架中使用接口指向这个具体实例,对数据库的一系列操作都是用接口的方法操作,从而摆脱了具体实例的依赖。 

  private DataSource dataSource;

  public DBAssist(DataSource dataSource){
    this.dataSource = dataSource;
  }

    数据的增加、删除、修改、查询分为: DML (增加、删除、修改)和 查询操作

    思考用户向框架里仍一条带有参数的 sql 语句,调用 DML 方法的时候,框架就知道怎么操作,并且把用户传递的参数和 sql 语句一一对应起来

    那么可以先定义一个 update(String sql,Object...params) 方法,负责把 sql 语句和参数值接收进来,在执行 sql 语句之前,判断参数是否和 sql 语句中

    的 “?” 占位符数量一致,如果一致继续对占位符进行设置值,最后执行 sql 语句即可:

  /**
* 能够执行 DML 语句:insert update delete
*/
public void update(String sql,Object...params) {
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null; try {
// 与数据源无关
conn = dataSource.getConnection(); st = conn.prepareStatement(sql);
int paramsCount = st.getParameterMetaData().getParameterCount(); // 设置参数
if(paramsCount>0) {
// 判断参数是否有效且和sql语句中的占位符个数相等
if(params==null || params.length !=paramsCount) {
throw new RuntimeException("传递的参数数量不匹配!");
} // 设置 sql 语句中的参数占位符
for (int i = 0; i < paramsCount; i++) {
st.setObject(i+1, params[i]);
}
}
st.executeUpdate(); } catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}finally {
colseResource(conn, st, rs);
}
}

  3.2 查询操作

    对于查询比较麻烦的是框架执行完 sql 语句之后,需要将查询的结果封装到一个 javabean 里返回给用户。

    那么可以定义一个框架的 query(String sql,Object...params) 方法,那不同的 sql 查询语句返回不同的数据对象,框架自己怎么知道需要返回的是什么 javabean呢?

    如果是查询一条记录的 sql 语句,如果查到了,框架给用户返回的是一个 javabean 对象。

    如果是查询多条记录的 sql 语句,如果查到了,框架给用户返回的是一个 javabean 对象的集合,那么集合需要用什么容器来装呢?

    上面这些问题的答案估计只有用户自己知道,好比,框架就是一个生产固型塑料的机器,工人在机器入口放入原材料就可以在机器出口等着成型的塑料。

    那工人要求机器生产球型的塑料,机器就能作出球型的塑料,工人要求机器生产矩型的塑料,机器就能作出矩型的塑料,而且两种情况下,原料都是一样的。

    说明机器一定是得到了某一个指令,要求机器做出什么形状。所以能看出谁用谁知道框架应该要输出的结果集是什么。

  用户1 用框架说我给你指明给我返回 ArrayList 结果集,

  用户2 用框架说我给你指明给我返回 HashMap 结果集,

  用户3 用框架说我给你指明给我返回 LinkedList 结果集,

  ……

  框架要是只要是一个结果集就都做一个可以返回这个结果集的功能,那估计框架设计者就不干了,做框架就是做标准,总是牵着用户的鼻子走,遇到“奇葩”用户

  那不得增加功能到死,所以,框架得面向接口设计,都给我实现一个接口,框架里面我都是使用接口中的方法操作程序的,所以谁来“各种花式”需求,都给我实现接口,那这个框架架爱怎么用就怎么用,自己用的姿势舒服就行。

具体代码实现:

DBAssist 代码实现:

package com.test.DBAssist;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement; import javax.sql.DataSource; import org.junit.Test; public class DBAssist {
private DataSource dataSource; public DBAssist(DataSource dataSource){
this.dataSource = dataSource;
} /**
* 能够执行 DML 语句:insert update delete
*/
public void update(String sql,Object...params) {
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null; try {
// 与数据源无关
conn = dataSource.getConnection(); st = conn.prepareStatement(sql);
int paramsCount = st.getParameterMetaData().getParameterCount(); // 设置参数
if(paramsCount>0) {
// 判断参数是否有效且和sql语句中的占位符个数相等
if(params==null || params.length !=paramsCount) {
throw new RuntimeException("传递的参数数量不匹配!");
} // 设置 sql 语句中的参数占位符
for (int i = 0; i < paramsCount; i++) {
st.setObject(i+1, params[i]);
}
}
st.executeUpdate(); } catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}finally {
colseResource(conn, st, rs);
}
} /**
* 能够执行 DML 语句:insert update delete
*/
public Object query(String sql,ResultHandler handler,Object...params) {
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null; try {
// 与数据源无关
conn = dataSource.getConnection(); st = conn.prepareStatement(sql);
int paramsCount = st.getParameterMetaData().getParameterCount(); // 设置参数
if(paramsCount>0) {
// 判断参数是否有效且和sql语句中的占位符个数相等
if(params==null || params.length !=paramsCount) {
throw new RuntimeException("传递的参数数量不匹配!");
} // 设置 sql 语句中的参数占位符
for (int i = 0; i < paramsCount; i++) {
st.setObject(i+1, params[i]);
}
}
rs = st.executeQuery();
return handler.handler(rs); } catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}finally {
colseResource(conn, st, rs);
}
} /**
* 释放资源
* @param conn
* @param st
* @param rs
*/
private void colseResource(Connection conn,Statement st,ResultSet rs) {
closeResultSet(rs);
closeStatement(st);
closeConnection(conn);
} /**
* 释放连接 Connection
* @param conn
*/
private void closeConnection(Connection conn) {
if(conn !=null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
//等待垃圾回收
conn = null;
} /**
* 释放语句执行者 Statement
* @param st
*/
private void closeStatement(Statement st) {
if(st !=null) {
try {
st.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
//等待垃圾回收
st = null;
} /**
* 释放结果集 ResultSet
* @param rs
*/
private void closeResultSet(ResultSet rs) {
if(rs !=null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
//等待垃圾回收
rs = null;
}
}

ResultHandler 接口代码实现:

package com.test.DBAssist;

import java.sql.ResultSet;

// 抽象策略
public interface ResultHandler { /**
* 把结果集中的数据封装到 ResultHandler接口的具体对象中
* @param rs
* @return
*/
Object handler(ResultSet rs);
}

ResultHandler 接口的实现类代码实现(BeanHandler 类,返回单个 javabean):

package com.test.DBAssist;

import java.lang.reflect.Field;
import java.sql.ResultSet; import com.test.daomain.Student;
//抽象策略的具体实现
/**
* 只对封装一条记录的结果集
* 返回值:封装好的javabean
*
*/
public class BeanHandler implements ResultHandler {
private Class clazz; public BeanHandler(Class clazz) {
this.clazz = clazz;
} @Override
public Object handler(ResultSet rs) {
try {
// 判断是否能查询到结果
if(rs.next()) {
Object bean = clazz.newInstance();
// 封装数据
// 要求javabean 的字段名和数据库的列名是一致的 int count = rs.getMetaData().getColumnCount(); for (int i = 0; i < count; i++) {
// 获取到结果集的列名称(与javabean 的属性名一致)
String columnName = rs.getMetaData().getColumnName(i+1);
// 获取列值
Object columnValue = rs.getObject(columnName); // 得到javabean的对应字段
Field field = clazz.getDeclaredField(columnName);
field.setAccessible(true);
field.set(bean,columnValue);
}
return bean;
}
return null;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("封装数据失败!");
} } }

ResultHandler 接口的实现类代码实现(BeanHandler 类,返回 javabean 类型的 list 集合):

package com.test.DBAssist;

import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List; public class BeanListHandler implements ResultHandler {
private Class clazz; public BeanListHandler(Class clazz) {
this.clazz = clazz;
} @Override
public Object handler(ResultSet rs) {
try {
List list = new ArrayList();
// 判断是否能查询到结果
while(rs.next()) {
Object bean = clazz.newInstance();
// 封装数据
// 要求javabean 的字段名和数据库的列名是一致的 int count = rs.getMetaData().getColumnCount(); for (int i = 0; i < count; i++) {
// 获取到结果集的列名称(与javabean 的属性名一致)
String columnName = rs.getMetaData().getColumnName(i+1);
// 获取列值
Object columnValue = rs.getObject(columnName); // 得到javabean的对应字段
Field field = clazz.getDeclaredField(columnName);
field.setAccessible(true);
field.set(bean,columnValue);
}
list.add(bean);
}
return list;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("封装数据失败!");
} } } 

模拟实现 DBUtils 工具 , 技术原理浅析的更多相关文章

  1. 网络路径排查工具使用/原理浅析(MTR、traceroute、tracepath、windows下besttrace)

    在请求网络资源获取缓慢或者有丢包过程中.经常会使用到网络路径探测工具.linux 下最常用的有mtr.traceroute.tracepath 等. 你是否有一点疑惑,路径探测的原理到底是如何完成的, ...

  2. java数据库连接池技术原理(浅析)

    在执行数据库SQL语句时,我们先要进行数据连接:而每次创建新的数据库的连接要消耗大量的资源,这样,大家就想出了数据库连接池技术.它的原理是,在运行过程中,同时打开着一定数量的数据库连接,形成数据连接池 ...

  3. Java学习笔记49(DBUtils工具类二)

    上一篇文章是我们自己模拟的DBUtils工具类,其实有开发好的工具类 这里使用commons-dbutils-1.6.jar 事务的简单介绍: 在数据库中应用事务处理案例:转账案例 张三和李四都有有自 ...

  4. seo伪原创技术原理分析,php实现伪原创示例

    seo伪原创技术原理分析,php实现伪原创示例 现在seo伪原创一般采用分词引擎以及动态同义词库,模拟百度(baidu),谷歌(google)等中文切词进行伪原创,生成后的伪原创文章更准确更贴近百度和 ...

  5. 沉淀,再出发:docker的原理浅析

    沉淀,再出发:docker的原理浅析 一.前言 在我们使用docker的时候,很多情况下我们对于一些概念的理解是停留在名称和用法的地步,如果更进一步理解了docker的本质,我们的技术一定会有质的进步 ...

  6. 超级干货:动态防御WAF技术原理及编程实战!

    本文带给大家的内容是动态防御WAF的技术原理及编程实战. 将通过介绍ShareWAF的核心技术点,向大家展示动态防御的优势.实现思路,并以编程实战的方式向大家展示如何在WAF产品开发过程中应用动态防御 ...

  7. Atitit.ide技术原理与实践attilax总结

    Atitit.ide技术原理与实践attilax总结 1.1. 语法着色1 1.2. 智能提示1 1.3. 类成员outline..func list1 1.4. 类型推导(type inferenc ...

  8. Atitit 语音识别的技术原理

    Atitit 语音识别的技术原理 1.1. 语音识别技术,也被称为自动语音识别Automatic Speech Recognition,(ASR),2 1.2. 模型目前,主流的大词汇量语音识别系统多 ...

  9. Atitit.gui api自动化调用技术原理与实践

    Atitit.gui api自动化调用技术原理与实践 gui接口实现分类(h5,win gui, paint opengl,,swing,,.net winform,)1 Solu cate1 Sol ...

随机推荐

  1. HTML 5将给开发者带来什么?

    在新的时代里,相信网页技术会伴随HTML 5的来临进入大洗牌的局面,HTML 5旨在解决Web中的交互,媒体,本地操作等问题,一些浏览器已经尝试支持HTML 5的一些功能,而开发者们有望最终从那些We ...

  2. FTP连接超时

    今天程序在连接FTP服务器,突然无法连接,用Windows 的 Explorer能正常连接,但用 WebRequest.WebResponse连接时,总是抛出连接超时异常. 后查找相关资料,原因是:程 ...

  3. sunTime

    哈哈哈  开通了博客,有缘的你看能不能来到这里,或许我们认识呢

  4. LeetCode笔记:140. Word Break II

    题目描述 给定一个非空的字符串s,一个非空的字符串list作为字典.通过在s中添加空格可以将s变为由list中的word表示的句子,要求返回所有可能组成的句子.设定list中的word不重复,且每一个 ...

  5. victory-native的使用

    Victory用于构建交互数据可视化的可组合React组件的生态系统 想写又不想写,真尴尬...

  6. 两层fragment嵌套时出现空白,(收藏别人的)

    完美解决 两层Fragment,内层空白 转载:http://blog.csdn.net/bingospunky/article/details/51352400 目录(?)[+] 前言 两层Frag ...

  7. 在 Android 的文字编辑控件 (TEdit) 中, 如何按下 Enter 就隐藏虚拟键盘

    在 Windows 的应用中,我们常常为了让使用者能够快速输入,在Edit元件中的onKeyUp或者 onKeyDown 事件中主动侦测使用者输入的字元是否有换行符号 (Enter),当使用者按下了E ...

  8. Mesos源码分析(10): MesosSchedulerDriver的启动及运行一个Task

      MesosSchedulerDriver的代码在src/sched/sched.cpp里面实现.     Driver->run()调用start()     首先检测Mesos-Maste ...

  9. Android 音视频开发(一) : 通过三种方式绘制图片

    版权声明:转载请说明出处:http://www.cnblogs.com/renhui/p/7456956.html 在 Android 音视频开发学习思路 里面,我们写到了,想要逐步入门音视频开发,就 ...

  10. 请求ajax失败的原因(进入到error)

    原因: dataType 定义类型和返回类型不一致,我定义的json格式数据. {data:[],num:0} 这种是不规则的字符串,不是严格的json格式 应该改成{"data" ...