其实,在这篇文章里,我只是分析了dbutis的query的运作流程。

至于类为什么要这样设计,蕴含的设计模式等等高级知识点咱们在下节再探讨。

先看看最简单的DBUtils是如何工作的。

数据库里有一张表,student,里面就三个属性 姓名,学号,出生日期( xm,xh,birth)其中前两个是vchar,birth是date;

  1. package dbutils;
  2.  
  3. import model.Student;
  4.  
  5. import org.apache.commons.dbutils.QueryRunner;
  6. import org.apache.commons.dbutils.handlers.BeanHandler;
  7. import org.apache.commons.dbutils.ResultSetHandler;
  8. import org.junit.Test;
  9.  
  10. import java.sql.SQLException;
  11.  
  12. public class BeanExample {
  13.  
  14. @Test
  15. public void testBean() {
  16.  
  17. QueryRunner qr = new QueryRunner(new MyDBSource());
  18.  
  19. String sql = "select * from student where xh=?";
  20. Object params[] = { "02" };
  21. Student s=null;
  22. try {
  23. ResultSetHandler<Student> rsh=new BeanHandler<>(Student.class);
  24. s = qr.query(sql,rsh,params);
  25. } catch (SQLException e) {
  26. // TODO Auto-generated catch block
  27. e.printStackTrace();
  28. }
  29. System.out.println(s.getXm());
  30.  
  31. }
  32.  
  33. }

其中,MyDBSource就是个提供数据源的工具而已。

  1. public class MyDBSource implements DataSource {
  2.  
  3. private static String driverClassName = "com.mysql.jdbc.Driver";
  4. private static String url = "jdbc:mysql://localhost:3306/webexample";
  5.  
  6. private static String userName = "root";
  7. private static String passWord = "root";
  8.  
  9. @Override
  10. public Connection getConnection() throws SQLException {
  11. try {
  12. Class.forName(driverClassName);
  13. } catch (ClassNotFoundException e) {
  14. // TODO Auto-generated catch block
  15. e.printStackTrace();
  16. }
  17. return DriverManager.getConnection(url, userName, passWord);
  18.  
  19. }
  20. //....
  21. }

我们再看看BeanHandler的内部。

  1. //BeanHandler.java
  2. public BeanHandler(Class<T> type) {
  3. this(type, ArrayHandler.ROW_PROCESSOR);
  4. }
  5. public BeanHandler(Class<T> type, RowProcessor convert) {
  6. this.type = type;
  7. this.convert = convert;
  8. }
  9.  
  10. //ArrayHandler.java
  11. public class ArrayHandler implements ResultSetHandler<Object[]> {
  12. static final RowProcessor ROW_PROCESSOR = new BasicRowProcessor();
  13. //....
  14. }
  15.  
  16. //BasicRowProcessor.java
  17. public class BasicRowProcessor implements RowProcessor {
  18. private static final BeanProcessor defaultConvert = new BeanProcessor();
  19.  
  20. public BasicRowProcessor() {
  21. this(defaultConvert);
  22. }
  23. //...
  24. }

ok,我们可以看到在BeanHandler中的convert,最终是BeanProcessor类型。



再下来就是我们的重头戏了。

  1. s = qr.query(sql,rsh,params);

我们看时序图

1.1 prepareStatement(conn, sql);

  1. protected PreparedStatement prepareStatement(Connection conn, String sql)
  2. throws SQLException {
  3. return conn.prepareStatement(sql);
  4. }

就是生成prepareStatement

    1.2 fillStatement(stmt, params)

    大家就是看名字也该知道,fillStatement就是填参数

    其核心代码如下:

  1. for (int i = 0; i < params.length; i++) {
  2. if (params[i] != null) {
  3. stmt.setObject(i + 1, params[i]);
  4. }
  5. //...
  6. }

当然fillStatement在核心代码上面还有一块,就是检查sql中的问号数量与params的长度是否相等。

     1.3 handle(rs)

  1. BeanHandler.java
  2. public T handle(ResultSet rs) throws SQLException {
  3. return rs.next() ? this.convert.toBean(rs, this.type) : null;
  4. }

在最开始分析BeanHandler的构造函数时,我们就已经知道了convert是BeanProcessor。

     1.3.1 toBean(rs, this.type)

  1. public <T> T toBean(ResultSet rs, Class<T> type) throws SQLException {
  2. PropertyDescriptor[] props = this.propertyDescriptors(type);
  3. ResultSetMetaData rsmd = rs.getMetaData();
  4. int[] columnToProperty = this.mapColumnsToProperties(rsmd, props);
  5.  
  6. return this.createBean(rs, type, props, columnToProperty);
  7. }

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)

 

  1. //对源码略微有删改
  2. //但绝对不影响核心思想
  3. private <T> T createBean(ResultSet rs, Class<T> type,
  4. PropertyDescriptor[] props, int[] columnToProperty)
  5. throws SQLException {
  6.  
  7. T bean = this.newInstance(type);
  8.  
  9. for (int i = 1; i < columnToProperty.length; i++) {
  10.  
  11. PropertyDescriptor prop = props[columnToProperty[i]];
  12. Class<?> propType = prop.getPropertyType();
  13.  
  14. Object value = null;
  15. if(propType != null)
  16. value = this.processColumn(rs, i, propType);
  17.  
  18. this.callSetter(bean, prop, value);
  19. }
  20.  
  21. return bean;
  22. }

在createBean里面

    this.processColumn(rs, i, propType)

    就是获得value,那么我们已经知道resultset,还有这个value在resultset中的序列号还有value类型,那么该如何获取呢?

    代码我就不贴了,大家自己看源码吧,看上5秒钟就能知道内部逻辑了。



    1.3.1.3.1 callSetter(bean, prop, value)

    这里面的核心就是下面的代码

  1. // Don't call setter if the value object isn't the right type
  2. if (this.isCompatibleType(value, params[0])) {
  3. setter.invoke(target, new Object[]{value});
  4. } else {
  5. throw new SQLException(
  6. "Cannot set " + prop.getName() + ": incompatible types, cannot convert "
  7. + value.getClass().getName() + " to " + params[0].getName());
  8. // value cannot be null here because isCompatibleType allows null
  9. }

如果value是个String,params却是个double那就得抛出异常了。

DBUtils源码分析的更多相关文章

  1. mybatis 源码分析(四)一二级缓存分析

    本篇博客主要讲了 mybatis 一二级缓存的构成,以及一些容易出错地方的示例分析: 一.mybatis 缓存体系 mybatis 的一二级缓存体系大致如下: 首先当一二级缓存同时开启的时候,首先命中 ...

  2. Flask框架 (四)—— 请求上下文源码分析、g对象、第三方插件(flask_session、flask_script、wtforms)、信号

    Flask框架 (四)—— 请求上下文源码分析.g对象.第三方插件(flask_session.flask_script.wtforms).信号 目录 请求上下文源码分析.g对象.第三方插件(flas ...

  3. ABP源码分析一:整体项目结构及目录

    ABP是一套非常优秀的web应用程序架构,适合用来搭建集中式架构的web应用程序. 整个Abp的Infrastructure是以Abp这个package为核心模块(core)+15个模块(module ...

  4. HashMap与TreeMap源码分析

    1. 引言     在红黑树--算法导论(15)中学习了红黑树的原理.本来打算自己来试着实现一下,然而在看了JDK(1.8.0)TreeMap的源码后恍然发现原来它就是利用红黑树实现的(很惭愧学了Ja ...

  5. nginx源码分析之网络初始化

    nginx作为一个高性能的HTTP服务器,网络的处理是其核心,了解网络的初始化有助于加深对nginx网络处理的了解,本文主要通过nginx的源代码来分析其网络初始化. 从配置文件中读取初始化信息 与网 ...

  6. zookeeper源码分析之五服务端(集群leader)处理请求流程

    leader的实现类为LeaderZooKeeperServer,它间接继承自标准ZookeeperServer.它规定了请求到达leader时需要经历的路径: PrepRequestProcesso ...

  7. zookeeper源码分析之四服务端(单机)处理请求流程

    上文: zookeeper源码分析之一服务端启动过程 中,我们介绍了zookeeper服务器的启动过程,其中单机是ZookeeperServer启动,集群使用QuorumPeer启动,那么这次我们分析 ...

  8. zookeeper源码分析之三客户端发送请求流程

    znode 可以被监控,包括这个目录节点中存储的数据的修改,子节点目录的变化等,一旦变化可以通知设置监控的客户端,这个功能是zookeeper对于应用最重要的特性,通过这个特性可以实现的功能包括配置的 ...

  9. java使用websocket,并且获取HttpSession,源码分析

    转载请在页首注明作者与出处 http://www.cnblogs.com/zhuxiaojie/p/6238826.html 一:本文使用范围 此文不仅仅局限于spring boot,普通的sprin ...

随机推荐

  1. ObjectOutputStream 和 ObjectInputStream的使用

    一.看一下API文档 ObjectOutputStream : ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream.可以使用 ObjectInp ...

  2. Swift中的可选协议和方法的历史渊源

    @objc protocol Transaction { func commit() -> Bool optional func isComplete() -> Bool } 以上协议被标 ...

  3. PHP 文件下载 浅析

    无控制类型 avi文件 rar文件 mp4MP3图片等会被直接解析 核心代码 类型 长度 实现函数 优化 原始下载文件的名称 优化后的文件下载名称 总结 文件下载的功能对一个网站而言基本上是必备的了, ...

  4. ROS探索总结(十九)——如何配置机器人的导航功能

    1.概述 ROS的二维导航功能包,简单来说,就是根据输入的里程计等传感器的信息流和机器人的全局位置,通过导航算法,计算得出安全可靠的机器人速度控制指令.但是,如何在特定的机器人上实现导航功能包的功能, ...

  5. [ExtJS5学习笔记]第三十四节 sencha extjs 5 grid表格之java后台导出excel

    继上次使用js前端导出excel之后,还有一个主要大家比较关注的是后台实现导出excel,因为本人开发使用的java所以这里使用apache的开源项目poi进行后台excel的导出. 本文目录 本文目 ...

  6. Spring之MVC模块

    Spring MVC的Controller用于处理用户的请求.Controller相当于Struts 1里的Action,他们的实现机制.运行原理都类似 Controller是个接口,一般直接继承Ab ...

  7. Linux动态频率调节系统CPUFreq之二:核心(core)架构与API

    上一节中,我们大致地讲解了一下CPUFreq在用户空间的sysfs接口和它的几个重要的数据结构,同时也提到,CPUFreq子系统把一些公共的代码逻辑组织在一起,构成了CPUFreq的核心部分,这些公共 ...

  8. FORM开发中Profiles的使用

    用户配置文件相当于系统参数,可以在不同层级(Site层.应用模块层.责任层.用户层)设置不同的值:作用范围小的覆盖范围大的层,系统已经预设了很多user profile; 开发人员也可以定义 在EBS ...

  9. [uwsgi]使用建议(类似最佳实践)

    看了下uwsgi官方的一个使用建议,之前都是直接参考了下django文档中那个比较简单的配置或者就写了个能运行的配置,么有注意很多细节问题,这里学习下,把需要的配置添加到项目配置中. 1 http a ...

  10. char能表示(-128~127)

    char 的取值范围是 -128 ~127 注:数0的补码表示是唯一的: +0的补码=+0的反码=+0的原码=00000000 -0的补码=11111111+1=00000000(mod 2的8次方) ...