阅读源码之路(四)

抽丝剥茧查看源码, 来自org.apache.commons.dbutils.QueryRunner.class:

        第一个核心方法是  batch(Connection conn, boolean closeConn, String sql, Object[][] params) , 该方法使用一条sql语句和二维数组批量执行数据库的update操作, 返回受影响的行的id值.  

        QueryRunner的Connection需要从Dao中传入,  并调用继承自AbstractQueryRunner封装的prepareStatement方法获得ps对象.  所有有关Connection,  PreparedStatement 可能重复使用的代码都被抽取到AbstractQueryRunner类中.   因为是abstract修饰的,  因此不能创建实例对象,  即使没有一个抽象方法,  所有功能必须使用QueryRunner实现.

      调用父类方法用给定的对象替换预编译sql语句的参数,  调用addBatch()把编译好的sql语句存入ps对象, 最后执行executeUpdate() 方法.

        另一个相似的方法是传入一个不定项参数Object...params 代替 Object[][] params,  这是执行单条update语句的方法.

        第二个核心方法是 query语句, 执行查询操作, 返回一个泛型对象 .  这里传入不定项参数 . 在对rSet进行处理的时候, 调用了通过接口ResultSetHandler传入的实例对象的方法  handle(rs),  把rSet中的值转化成对象返回.

        ResultSetHandler接口有多种实现类,  包括Bean,  BeanList,  BeanMap,  Map,  MapList,  ArrayList,  List等.  

        BeanHandler中有一个私有的行处理器 convert,    handle(rs)正是调用convert对象的toBeans()方法获得返回值.

        接口RowProcessor指向的convert对象来自于Beanhandler的构造方法, 通过ArrayHandler的ROW_PROCESSOR属性,  该属性又来自new BasicRowProcessor();  而BasicRowProcessor()实现了RowProcessor(), 因此传入的实际是一个RowProcessor对象;  有意思的是在BasicRowProcessor 中convert实际是BeanProcessor() 对象.

        按照源码上的解释,  BeanProcessor 主要是匹配列名和方法名, 并把结果集中的列转化成Bean类的属性.  子类必须重写方法链以实现自定义的行为.  但是它既不是抽象类,  又没有任何子类,  实际BeanProcessor是通过BasicRowProcessor的属性获得调用,   再由BeanHandler或MapHandler类调用实现的.  BeanProcessor基本实现了Bean, BeanList方法,  只有Map等类型是在BasicRowProcessor中新加入的.

        toBean方法通过newInstance()方法把Class<T>  type转化成了目标 T  bean对象 , 调用封装的populateBean(rs, bean)方法,  使用了反射和BeanInfo类,  将列名与bean属性名匹配。属性基于多个因素与列匹配: 

        1. 类有一个属性与列报名相等(忽略大小写) 

        2. 通过ResultSet.get方法返回的列的类型可以被转化为属性类型,  返回的值为null时, 属性为默认值. 

        关键方法在于BeanProcesser的callSetter方法,  populateBean方法返回列名,  再通过callSetter方法把每列的值转化为对象属性值,  最后返回Bean对象.

            Class<?> firstParam = setter.getParameterTypes()[0];    //该方法返回参数类型
            for (PropertyHandler handler : propertyHandlers) {       
                if (handler.match(firstParam, value)) {             //比较方法名与列名 
                    value = handler.apply(firstParam, value);       //获得需要的方法.
                    break;    
                }
            }

最后就是执行invoke方法给属性赋值: 

           setter.invoke(target, new Object[]{value});

        第三个核心方法是List<T> execute(), 执行查询操作, 返回对象的List集合 .   通过继承自父类的prepareCall(conn, sql)方法返回一个(CallableStatement)stmt对象,  fillStatement(stmt, params)  用给定的params对象填充sql的参数,  

        这里通过getResultSet() 返回resultSet结果集,  再通过接口的handler()方法返回对象 T.  while循环返回results.

            boolean moreResultSets = stmt.execute();
            // Handle multiple result sets by passing them through the handler
            // retaining the final result
            ResultSet rs = null;
            while (moreResultSets) {
                try {
                    rs = this.wrap(stmt.getResultSet());
                    results.add(rsh.handle(rs));
                    moreResultSets = stmt.getMoreResults();

        如果需要返回一个BeanList,  就传入一个new BeanListHandler(Class<T>  clazz);  最终调用了BeanProcesser类的toBeanList() 和 populateBean() 方法.  

           如果需要返回一个Map集合, 就传入一个new MapHandler(Class<T> clazz);   因为BeanProwcesser没有返回Map的方法,  调用的是BasicRowProcessor 的toMap()方法.

总结:  

        1. 首先创建了一个抽象类, 封装所有要用到的conn到preparedStatement的方法, QueryRunner类需要继承此抽象类

        2. 为了能够返回不同的数据类型, 要把接口作为参数, 传入所需的该接口的实例对象

        3. 这些实例对象要完成ResultSet到Bean的转化, 要把共有的方法抽取出来封装成一个或多个类,  并调用对应的方法实现. 

        4. QueryRunner通过调用其他类的方法完成所有工作

哪些方法需要封装:

        1.重复使用的代码

        2.需要多步完成一项功能的代码块抽取成方法.

        3.某些需要抛出异常的代码,  尤其是可能抛出多种异常的情况,  可以单独封装成方法,  便于维护和阅读

        

     * Wrap the <code>ResultSet</code> in a decorator before processing it. This
     * implementation returns the <code>ResultSet</code> it is given without any
     * decoration.
     *
     * <p>
     * Often, the implementation of this method can be done in an anonymous
     * inner class like this:
     * </p>
     * <pre>
     * QueryRunner run = new QueryRunner() {
     *     protected ResultSet wrap(ResultSet rs) {            //有什么意义?
     *         return StringTrimmedResultSet.wrap(rs);
     *     }
     * };
     * </pre>
     *
     * @param rs
     *            The <code>ResultSet</code> to decorate; never
     *            <code>null</code>.
     * @return The <code>ResultSet</code> wrapped in some decorator.
     */
    protected ResultSet wrap(ResultSet rs) {
        return rs;
    }

猜你喜欢

转载自blog.csdn.net/secret_breathe/article/details/80314952