DBUtils源码分析
其实,在这篇文章里,我只是分析了dbutis的query的运作流程。
至于类为什么要这样设计,蕴含的设计模式等等高级知识点咱们在下节再探讨。
先看看最简单的DBUtils是如何工作的。
数据库里有一张表,student,里面就三个属性 姓名,学号,出生日期( xm,xh,birth)其中前两个是vchar,birth是date;
package dbutils;
import model.Student;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.ResultSetHandler;
import org.junit.Test;
import java.sql.SQLException;
public class BeanExample {
@Test
public void testBean() {
QueryRunner qr = new QueryRunner(new MyDBSource());
String sql = "select * from student where xh=?";
Object params[] = { "02" };
Student s=null;
try {
ResultSetHandler<Student> rsh=new BeanHandler<>(Student.class);
s = qr.query(sql,rsh,params);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(s.getXm());
}
}
其中,MyDBSource就是个提供数据源的工具而已。
public class MyDBSource implements DataSource {
private static String driverClassName = "com.mysql.jdbc.Driver";
private static String url = "jdbc:mysql://localhost:3306/webexample";
private static String userName = "root";
private static String passWord = "root";
@Override
public Connection getConnection() throws SQLException {
try {
Class.forName(driverClassName);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return DriverManager.getConnection(url, userName, passWord);
}
//....
}
我们再看看BeanHandler的内部。
//BeanHandler.java
public BeanHandler(Class<T> type) {
this(type, ArrayHandler.ROW_PROCESSOR);
}
public BeanHandler(Class<T> type, RowProcessor convert) {
this.type = type;
this.convert = convert;
}
//ArrayHandler.java
public class ArrayHandler implements ResultSetHandler<Object[]> {
static final RowProcessor ROW_PROCESSOR = new BasicRowProcessor();
//....
}
//BasicRowProcessor.java
public class BasicRowProcessor implements RowProcessor {
private static final BeanProcessor defaultConvert = new BeanProcessor();
public BasicRowProcessor() {
this(defaultConvert);
}
//...
}
ok,我们可以看到在BeanHandler中的convert,最终是BeanProcessor类型。
再下来就是我们的重头戏了。
s = qr.query(sql,rsh,params);
我们看时序图
1.1 prepareStatement(conn, sql);
protected PreparedStatement prepareStatement(Connection conn, String sql)
throws SQLException {
return conn.prepareStatement(sql);
}
就是生成prepareStatement
1.2 fillStatement(stmt, params)
大家就是看名字也该知道,fillStatement就是填参数
其核心代码如下:
for (int i = 0; i < params.length; i++) {
if (params[i] != null) {
stmt.setObject(i + 1, params[i]);
}
//...
}
当然fillStatement在核心代码上面还有一块,就是检查sql中的问号数量与params的长度是否相等。
1.3 handle(rs)
BeanHandler.java
public T handle(ResultSet rs) throws SQLException {
return rs.next() ? this.convert.toBean(rs, this.type) : null;
}
在最开始分析BeanHandler的构造函数时,我们就已经知道了convert是BeanProcessor。
1.3.1 toBean(rs, this.type)
public <T> T toBean(ResultSet rs, Class<T> type) throws SQLException {
PropertyDescriptor[] props = this.propertyDescriptors(type);
ResultSetMetaData rsmd = rs.getMetaData();
int[] columnToProperty = this.mapColumnsToProperties(rsmd, props);
return this.createBean(rs, type, props, columnToProperty);
}
1.3.1.1 propertyDescriptors(type)
这是运用内省(Introspector)获得类型(就是代码里的Student.class)的属性(property)
1.3.1.2 mapColumnsToProperties(rsmd, props)
这一步比较麻烦,为什么说麻烦呢。
数据库里,一张表上有字段,现在这些字段存放在ResultSetMetaData里面
在我们的bean里面,有属性(property),现在存放在props这个数组里。
columnToProperty这里面就放的是字段与属性的对应关系。
例如 columnToProperty[3]=4 就是说ResultSetMetaData里的第三个字段对应于bean的PropertyDescriptor里面的第四个属性。
1.3.1.3 createBean(rs, type, props, columnToProperty)
//对源码略微有删改
//但绝对不影响核心思想
private <T> T createBean(ResultSet rs, Class<T> type,
PropertyDescriptor[] props, int[] columnToProperty)
throws SQLException {
T bean = this.newInstance(type);
for (int i = 1; i < columnToProperty.length; i++) {
PropertyDescriptor prop = props[columnToProperty[i]];
Class<?> propType = prop.getPropertyType();
Object value = null;
if(propType != null)
value = this.processColumn(rs, i, propType);
this.callSetter(bean, prop, value);
}
return bean;
}
在createBean里面
this.processColumn(rs, i, propType)
就是获得value,那么我们已经知道resultset,还有这个value在resultset中的序列号还有value类型,那么该如何获取呢?
代码我就不贴了,大家自己看源码吧,看上5秒钟就能知道内部逻辑了。
1.3.1.3.1 callSetter(bean, prop, value)
这里面的核心就是下面的代码
// Don't call setter if the value object isn't the right type
if (this.isCompatibleType(value, params[0])) {
setter.invoke(target, new Object[]{value});
} else {
throw new SQLException(
"Cannot set " + prop.getName() + ": incompatible types, cannot convert "
+ value.getClass().getName() + " to " + params[0].getName());
// value cannot be null here because isCompatibleType allows null
}
如果value是个String,params却是个double那就得抛出异常了。
DBUtils源码分析的更多相关文章
- mybatis 源码分析(四)一二级缓存分析
本篇博客主要讲了 mybatis 一二级缓存的构成,以及一些容易出错地方的示例分析: 一.mybatis 缓存体系 mybatis 的一二级缓存体系大致如下: 首先当一二级缓存同时开启的时候,首先命中 ...
- Flask框架 (四)—— 请求上下文源码分析、g对象、第三方插件(flask_session、flask_script、wtforms)、信号
Flask框架 (四)—— 请求上下文源码分析.g对象.第三方插件(flask_session.flask_script.wtforms).信号 目录 请求上下文源码分析.g对象.第三方插件(flas ...
- ABP源码分析一:整体项目结构及目录
ABP是一套非常优秀的web应用程序架构,适合用来搭建集中式架构的web应用程序. 整个Abp的Infrastructure是以Abp这个package为核心模块(core)+15个模块(module ...
- HashMap与TreeMap源码分析
1. 引言 在红黑树--算法导论(15)中学习了红黑树的原理.本来打算自己来试着实现一下,然而在看了JDK(1.8.0)TreeMap的源码后恍然发现原来它就是利用红黑树实现的(很惭愧学了Ja ...
- nginx源码分析之网络初始化
nginx作为一个高性能的HTTP服务器,网络的处理是其核心,了解网络的初始化有助于加深对nginx网络处理的了解,本文主要通过nginx的源代码来分析其网络初始化. 从配置文件中读取初始化信息 与网 ...
- zookeeper源码分析之五服务端(集群leader)处理请求流程
leader的实现类为LeaderZooKeeperServer,它间接继承自标准ZookeeperServer.它规定了请求到达leader时需要经历的路径: PrepRequestProcesso ...
- zookeeper源码分析之四服务端(单机)处理请求流程
上文: zookeeper源码分析之一服务端启动过程 中,我们介绍了zookeeper服务器的启动过程,其中单机是ZookeeperServer启动,集群使用QuorumPeer启动,那么这次我们分析 ...
- zookeeper源码分析之三客户端发送请求流程
znode 可以被监控,包括这个目录节点中存储的数据的修改,子节点目录的变化等,一旦变化可以通知设置监控的客户端,这个功能是zookeeper对于应用最重要的特性,通过这个特性可以实现的功能包括配置的 ...
- java使用websocket,并且获取HttpSession,源码分析
转载请在页首注明作者与出处 http://www.cnblogs.com/zhuxiaojie/p/6238826.html 一:本文使用范围 此文不仅仅局限于spring boot,普通的sprin ...
随机推荐
- Mac入门——快捷键
快捷键: 截图 Shift+Command+3 截取全屏幕至桌面 Shift+Command+4 截取部分屏幕至桌面 Shift+Command+4+空格 截取窗口或原件至桌面 Shift+ ...
- Spark Scheduler模块源码分析之DAGScheduler
本文主要结合Spark-1.6.0的源码,对Spark中任务调度模块的执行过程进行分析.Spark Application在遇到Action操作时才会真正的提交任务并进行计算.这时Spark会根据Ac ...
- Java进阶(四十二)Java中多线程使用匿名内部类的方式进行创建3种方式
Java中多线程使用匿名内部类的方式进行创建3种方式 package cn.edu.ujn.demo; // 匿名内部类的格式: public class ThreadDemo { public st ...
- memcached实战系列(一)memcached安装
下载并安装Memcached服务器端 我用的是cenos6.5 64位系统. libevent是个程序库,它将Linux的epoll.BSD类操作系统的kqueue等事件处理功能封装成统一的接口,具有 ...
- ROS_Kinetic_x 基於ROS和Gazebo的RoboCup中型組仿真系統(多機器人協作)
國防科學技術大學發布了RoboCup中型組仿真平臺,基於ROS和Gazebo設計. 該平臺可以用於多機器人協作研究.參考資料如下: ROS新聞:1 http://www.ros.org/news ...
- [struts2学习笔记] 第二节 使用Maven搞定管理和构造Struts 2 Web应用程序的七个步骤
本文地址:http://blog.csdn.net/sushengmiyan/article/details/40303897 官方文档:http://struts.apache.org/releas ...
- JS和Jquery操作label标签
获取label值: label标签在JS和Jquery中使用不能像其他标签一样用value获取它的值: 代码如下: var label=document.getElementById("s ...
- (NO.00005)iOS实现炸弹人游戏(十一):怪物之火精灵
大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 从本篇开始我们一次介绍一下游戏中敌人的制作过程.看过第一篇的小 ...
- 给EditText的drawableRight属性的图片设置点击事件
这个方法是通用的,不仅仅适用于EditText,也适用于TextView.AutoCompleteTextView等控件. Google官方API并没有给出一个直接的方法用来设置右边图片的点击事件,所 ...
- 05 Activity 现场保护
我们接电话然后返回的 Activity将会被毁了重新创建 所以我们需要在摧毁记录一些信息还原Activity 摧毁时: //在Activi被摧毁的调用此方法 @Override protected v ...