模拟实现 DBUtils 工具 , 技术原理浅析
申明:本文采用自己 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层代码的简单框架。

从上图中可以看出 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(); // 得到数据库的元信息集合
- 参数元信息:
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 工具 , 技术原理浅析的更多相关文章
- 网络路径排查工具使用/原理浅析(MTR、traceroute、tracepath、windows下besttrace)
在请求网络资源获取缓慢或者有丢包过程中.经常会使用到网络路径探测工具.linux 下最常用的有mtr.traceroute.tracepath 等. 你是否有一点疑惑,路径探测的原理到底是如何完成的, ...
- java数据库连接池技术原理(浅析)
在执行数据库SQL语句时,我们先要进行数据连接:而每次创建新的数据库的连接要消耗大量的资源,这样,大家就想出了数据库连接池技术.它的原理是,在运行过程中,同时打开着一定数量的数据库连接,形成数据连接池 ...
- Java学习笔记49(DBUtils工具类二)
上一篇文章是我们自己模拟的DBUtils工具类,其实有开发好的工具类 这里使用commons-dbutils-1.6.jar 事务的简单介绍: 在数据库中应用事务处理案例:转账案例 张三和李四都有有自 ...
- seo伪原创技术原理分析,php实现伪原创示例
seo伪原创技术原理分析,php实现伪原创示例 现在seo伪原创一般采用分词引擎以及动态同义词库,模拟百度(baidu),谷歌(google)等中文切词进行伪原创,生成后的伪原创文章更准确更贴近百度和 ...
- 沉淀,再出发:docker的原理浅析
沉淀,再出发:docker的原理浅析 一.前言 在我们使用docker的时候,很多情况下我们对于一些概念的理解是停留在名称和用法的地步,如果更进一步理解了docker的本质,我们的技术一定会有质的进步 ...
- 超级干货:动态防御WAF技术原理及编程实战!
本文带给大家的内容是动态防御WAF的技术原理及编程实战. 将通过介绍ShareWAF的核心技术点,向大家展示动态防御的优势.实现思路,并以编程实战的方式向大家展示如何在WAF产品开发过程中应用动态防御 ...
- Atitit.ide技术原理与实践attilax总结
Atitit.ide技术原理与实践attilax总结 1.1. 语法着色1 1.2. 智能提示1 1.3. 类成员outline..func list1 1.4. 类型推导(type inferenc ...
- Atitit 语音识别的技术原理
Atitit 语音识别的技术原理 1.1. 语音识别技术,也被称为自动语音识别Automatic Speech Recognition,(ASR),2 1.2. 模型目前,主流的大词汇量语音识别系统多 ...
- Atitit.gui api自动化调用技术原理与实践
Atitit.gui api自动化调用技术原理与实践 gui接口实现分类(h5,win gui, paint opengl,,swing,,.net winform,)1 Solu cate1 Sol ...
随机推荐
- 关于最小生成树,拓扑排序、强连通分量、割点、2-SAT的一点笔记
关于最小生成树,拓扑排序.强连通分量.割点.2-SAT的一点笔记 前言:近期在复习这些东西,就xjb写一点吧.当然以前也写过,但这次偏重不太一样 MST 最小瓶颈路:u到v最大权值最小的路径.在最小生 ...
- C#中IPAddress转换成整型int
string addr = "11.22.33.44"; System.Net.IPAddress IPAddr=System.Net.IPAddress.Parse(addr); ...
- Markdown常用快捷键
Markdown使用的符号:井号,星号,大于号,中括号,竖线,横杠,波浪线,反引号 # ,*, > ,[],|,-,~,` 井号 + 空格:根据空格的个数显示各标题的大小 标题一 标题二 标题三 ...
- JavaScript模板引擎Handlebars
Handlebars模板库简单介绍 Handlebars是JavaScript一个语义模板库,通过对view(模板)和data(ajax请求的数据,一般是json)的分离来快速构建Web模板.它采用& ...
- 如何解决PeopleSoft Process Scheduler发布问题
常见PeopleSoft进程调度程序发布问题 此发布问题中遇到的一些常见错误是: 将HTTP响应转换为UCS2时出错 XML文档对象创建失败. 无法处理来自Report Repository的HTTP ...
- Php中文件下载功能实现超详细流程分析
浏览器发送一个请求,请求访问服务器中的某个网页(如:down.php),该网页的代码如下 客户端从服务端下载文件的流程分析: 浏览器发送一个请求,请求访问服务器中的某个网页(如:down.php) ...
- C#线程 ---- 线程同步详解
线程同步 说明:接上一篇,注意分享线程同步的必要性和线程同步的方法. 测试代码下载:https://github.com/EkeSu/C-Thread-synchronization-C-.git 一 ...
- 20175324 mycp
具体描述: 编写MyCP.java 实现类似Linux下cp XXX1 XXX2的功能,要求MyCP支持两个参数: java MyCP -tx XXX1.txt XXX2.bin 用来把文本文件(内容 ...
- codeforces_Codeforces Round #541 (Div. 2)_abc
A. Sea Battle time limit per test 1 second memory limit per test 256 megabytes input standard input ...
- idea破解失效:cant obtain ticket from http://idea.iteblog.com/key.php
破解idea错误描述:cant obtain ticket from http://idea.iteblog.com/key.php idea破解失效 解决方案: 打开链接: http://ide ...