其实,在这篇文章里,我只是分析了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源码分析的更多相关文章

  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. Xcode 8新功能介绍

    概述 Xcode 8正式版在9月13日已经推送给开发者下载,下载之后就出现了很多编译错误,之前的插件也不能用了,但是发现Xcode8把好多不错的插件功能整合到自身了,感觉这点也挺不错. 结合自己遇到的 ...

  2. Selenium Webdriver元素定位的八种常用方法

    如果你只是想快速实现控件抓取,而不急于了解其原理,可直接看: http://blog.csdn.net/kaka1121/article/details/51878346 如果你想学习web端自动化, ...

  3. Vc2015 utf8 格式出错

    Vc2015 utf8 格式出错(金庆的专栏)用Vc2015新建一个项目,如下添加一行中文注释.然后将这个文件改为utf8无BOM格式,再转换行结束符为Unix格式.#include "st ...

  4. svn(subversion)代码版本管理在linux下的一些常见使用命令

    以下的操作都是默认你的服务器安装有svn的大前提下进行的. 一.创建版本库 我的版本库存放路径为: /var/svn : 下面我们来创建一个名为 svntet 的版本库    注释: svnadmin ...

  5. Nginx的负载均衡 - 加权轮询 (Weighted Round Robin) 上篇

    Nginx版本:1.9.1 我的博客:http://blog.csdn.net/zhangskd 算法介绍 来看一个简单的Nginx负载均衡配置. http { upstream cluster { ...

  6. 没有文件扩展“.js”的脚本引擎问题解决

    安装MinGW的时候提示没有文件扩展".js"的脚本引擎. 原因:系统安装Dreamwear.UltraEdit.EditPlus后修改了.js文件的默认打开方式.当想直接执行js ...

  7. Oracle 执行计划(Explain Plan) 说明

    如果要分析某条SQL的性能问题,通常我们要先看SQL的执行计划,看看SQL的每一步执行是否存在问题. 如果一条SQL平时执行的好好的,却有一天突然性能很差,如果排除了系统资源和阻塞的原因,那么基本可以 ...

  8. iOS开发之*.a静态库注意事项

    以*.a静态库的形式引入工程的(比如:libUploadLib.a),*.a里面的class有category形式实现时,除了在工程Target的 Build Phases里面的 Link Binar ...

  9. 利用cocos2d-x实现CandyCrushSaga消除功能

    猴子原创,欢迎转载.转载请注明: 转载自Cocos2D开发网–Cocos2Dev.com,谢谢! 原文地址: http://www.cocos2dev.com/?p=455 昨天没事写了个三消玩玩.已 ...

  10. Qzone React Native改造

    Android Qzone 6.1版本在情侣空间涉水React Native,以动态插件方式将情侣空间进行React Natived的改造.在情侣空间基础上,Android Qzone 6.2版本以融 ...