模拟实现 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 ...
随机推荐
- HBASE强制删除表
1,先把hdfs的对应表的数据删除 hadoop fs -mv /hbase/<table_name> /tmp 2,修复meta信息 hbase hbck -fixMeta -fixAs ...
- Android中的数据持久化机制
Android中几种最简单但是却最通用的数据持久化技术:SharedPreference.实例状态Bundle和本地文件. Android的非确定性Activity和应用程序生存期使在会话间保留UI状 ...
- elasticsearch简单操作
现在,启动一个节点和kibana,接下来的一切操作都在kibana中Dev Tools下的Console里完成 创建一篇文档 将小黑的小姨妈的个人信息录入elasticsearch.我们只要输入 PU ...
- CentOS7上Docker简单安装及nginx部署
安装 如果原来安装过docker,先把原来的删掉,再安装(如果是首次安装docker忽略第一步,直接在第二步看起) 1.1先查看下已经安装了那些docker yum list installed | ...
- jieba中文分词
jieba中文分词¶ 中文与拉丁语言不同,不是以空格分开每个有意义的词,在我们处理自然语言处理的时候,大部分情况下,词汇是对句子和文章的理解基础.因此需要一个工具去把完整的中文分解成词. ji ...
- 解决微信浏览器中无法一键拨号问题tel
公众号中需要在某些页面显示手机号码,并且需要点击后拨号. 原以为 <a href="tel:10086">10086</a> 可以解决了, 没想到在微信浏览 ...
- 数位DP -启示录
http://poj.org/problem?id=3208 一个魔鬼数为包含连续三个666的的数字,给个n(n<5e7)求第n个魔鬼数. 预处理f[i][j],f[i][3]表示由前i位数字构 ...
- libguestfs手册(1): 架构
要编辑一个image,则运行下面的命令 guestfish -a ubuntutest.img ><fs> 会弹出一个命令行工具 运行run ><fs> run 我 ...
- 深入理解JVM(七)——性能监控工具
前言 工欲善其事必先利其器,性能优化和故障排查在我们大都数人眼里是件比较棘手的事情,一是需要具备一定的原理知识作为基础,二是需要掌握排查问题和解决问题的流程.方法.本文就将介绍利用性能监控工具,帮助开 ...
- 企业IT管理员IE11升级指南【12】—— 兼容视图列表介绍
企业IT管理员IE11升级指南 系列: [1]—— Internet Explorer 11增强保护模式 (EPM) 介绍 [2]—— Internet Explorer 11 对Adobe Flas ...