模拟实现 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 ...
随机推荐
- 【QT】QApplication简介
1.QApplication QApplication类管理GUI程序的控制流和主要设置,是基于QWidget的,为此特化了QGuiApplication的一些功能,处理QWidget特有的初始化和结 ...
- python爬取房天下数据Demo
import requests from bs4 import BeautifulSoup res = requests.get('http://sh.esf.fang.com/chushou/3_3 ...
- 关于部署php遇到的坑
业务突然要启动一个久不使用的PHP项目, 发现部署到centos7上后 各种报错 就是不行. 我怀疑是apache或者php问题 就重新安装 编译安装也试过就是不行. 只能按笨办法 在测试环境安装了a ...
- Vue 中动态添加class(使用v-bind:class)
今天在Vue中动态修改类名,元素的样式就是不改变,类名也没有加上去,里面的问题具体我还是不太清楚,有可能是因为自己不认真,把 :class= 后面的内容的格式给整错了,下面将正确的做法记录一下,便于以 ...
- Python基础之函数参数
一.实参 1.实参分类: 2.实参基础代码: def fun01(a, b, c): print(a) print(b) print(c) # 位置传参:实参与形参的位置依次对应 fun01(1, 2 ...
- mysql 删除指定字符
mysql 删除指定字符 1.1 前言 实际需求中如果想删除指定的字符,一般需要使用到trim函数.trim函数默认删除字符的前后空格,如果想指定删除特定字符,则需要使用一下语句进行声明 ...
- android颜色color.xml设置
XML Code 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849 ...
- SQL Server 恢复数据库至指定时间点
发生数据库误删的情况下,及时恢复数据到误操作前的状态 工具/原料 SQL Server Management Studio 数据库完整备份及日志备份 必备条件 1 数据库右键属性,在选项中查看 ...
- C#单元测试,带你快速入门
注:本文示例环境 VS2017 XUnit 2.2.0 单元测试框架 xunit.runner.visualstudio 2.2.0 测试运行工具 Moq 4.7.10 模拟框架 为什么要编写单元测试 ...
- OSGi解决的问题
osgi最明显的缺陷 bundle尽管可以为隔离的服务建立独立生命周期管理的热部署方式,以及明确的服务导出和导入依赖能力,但是其最终基于jvm,无法对bundle对应的服务实现计算资源的隔离,一个服务 ...