申明:本文采用自己 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. HBASE强制删除表

    1,先把hdfs的对应表的数据删除 hadoop fs -mv /hbase/<table_name> /tmp 2,修复meta信息 hbase hbck -fixMeta -fixAs ...

  2. Android中的数据持久化机制

    Android中几种最简单但是却最通用的数据持久化技术:SharedPreference.实例状态Bundle和本地文件. Android的非确定性Activity和应用程序生存期使在会话间保留UI状 ...

  3. elasticsearch简单操作

    现在,启动一个节点和kibana,接下来的一切操作都在kibana中Dev Tools下的Console里完成 创建一篇文档 将小黑的小姨妈的个人信息录入elasticsearch.我们只要输入 PU ...

  4. CentOS7上Docker简单安装及nginx部署

    安装 如果原来安装过docker,先把原来的删掉,再安装(如果是首次安装docker忽略第一步,直接在第二步看起) 1.1先查看下已经安装了那些docker yum list installed | ...

  5. jieba中文分词

      jieba中文分词¶   中文与拉丁语言不同,不是以空格分开每个有意义的词,在我们处理自然语言处理的时候,大部分情况下,词汇是对句子和文章的理解基础.因此需要一个工具去把完整的中文分解成词. ji ...

  6. 解决微信浏览器中无法一键拨号问题tel

    公众号中需要在某些页面显示手机号码,并且需要点击后拨号. 原以为 <a href="tel:10086">10086</a> 可以解决了, 没想到在微信浏览 ...

  7. 数位DP -启示录

    http://poj.org/problem?id=3208 一个魔鬼数为包含连续三个666的的数字,给个n(n<5e7)求第n个魔鬼数. 预处理f[i][j],f[i][3]表示由前i位数字构 ...

  8. libguestfs手册(1): 架构

    要编辑一个image,则运行下面的命令 guestfish -a ubuntutest.img ><fs> 会弹出一个命令行工具 运行run ><fs> run 我 ...

  9. 深入理解JVM(七)——性能监控工具

    前言 工欲善其事必先利其器,性能优化和故障排查在我们大都数人眼里是件比较棘手的事情,一是需要具备一定的原理知识作为基础,二是需要掌握排查问题和解决问题的流程.方法.本文就将介绍利用性能监控工具,帮助开 ...

  10. 企业IT管理员IE11升级指南【12】—— 兼容视图列表介绍

    企业IT管理员IE11升级指南 系列: [1]—— Internet Explorer 11增强保护模式 (EPM) 介绍 [2]—— Internet Explorer 11 对Adobe Flas ...