01-dbutils源码之AbstractQueryRunner

今天中午吃饭时,听了公司架构师的一些话,意思是这样的:每天花一点时间看开源项目的源码,一开始看不懂没关系,继续看,即使看懂了3行代码,也是一种收获。来北京之后,自己也有一段时间看过一些项目的源码,但是断断续续的,一个项目看一点,那个项目看一点,没有形成整体的思维。现在重新开始看和整理,能看懂多少,就记录多少吧。
 
        第一次写阅读源码的笔记,先选了个较简单的dbutils。 

        先来介绍下dbutils,去了官网弄了些文字过来。红色的那句看懂了,就大概知道dbutils是干什么的了。搞IT,英文很重要的,所以我不翻译了。看不懂的,还是去学习下英文吧。All right, let us get started !
 ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- 
         Commons DbUtils: JDBC Utility Component
         The Commons DbUtils library is a small set of classes designed to make working with JDBC easier.  JDBC resource cleanup code is mundane, error prone work so these classes abstract out all of the cleanup tasks from your code leaving you with what you really wanted to do with JDBC in the first place: query and update data.

Some of the advantages of using DbUtils are:
        (1)No possibility for resource leaks. Correct JDBC coding isn't difficult but it is time-consuming and tedious. This often leads to connection leaks that may be difficult to track down.
        (2)Cleaner, clearer persistence code. The amount of code needed to persist data in a database is drastically reduced. The remaining code clearly expresses your intention without being cluttered with resource cleanup.
        (3)Automatically populate JavaBean properties from ResultSets. You don't need to manually copy column values into bean instances by calling setter methods. Each row of the ResultSet can be represented by one fully populated bean instance.

      Scope of the Package
        DbUtils is designed to be:
         Small  - you should be able to understand the whole package in a short amount of time.
         Transparent  - DbUtils doesn't do any magic behind the scenes. You give it a query, it executes it and cleans up for you.
         Fast  - You don't need to create a million temporary objects to work with DbUtils.

        DbUtils is not:
        (1)An Object/Relational bridge - there are plenty of good O/R tools already. DbUtils is for developers looking to use JDBC without all the mundane pieces.
        (2)A Data Access Object (DAO) framework - DbUtils can be used to build a DAO framework though.
        (3)An object oriented abstraction of general database objects like a Table, Column, or PrimaryKey.
        (4)A heavyweight framework of any kind - the goal here is to be a straightforward and easy to use JDBC helper library.

         Dependencies
        DbUtils is intentionally a single jar distribution and relies only on a standard Java 1.6 or later JRE.  
 -----------------------------------------------------------------------------------------------------------------------------------------------------------------------
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package  org. apache. commons. dbutils;
import  java. beans. IntrospectionException;
import  java. beans. Introspector;
import  java. beans. PropertyDescriptor;
import  java. lang. reflect. InvocationTargetException;
import  java. lang. reflect. Method;
import  java. sql. Connection;
import  java. sql. ParameterMetaData;
import  java. sql. PreparedStatement;
import  java. sql. ResultSet;
import  java. sql. SQLException;
import  java. sql. Statement;
import  java. sql. Types;
import  java. util. Arrays;
import  javax. sql. DataSource;
/**
 * The base class for QueryRunner & AsyncQueryRunner. This class is thread safe.
 * QueryRunner & AsyncQueryRunnerPreparedStatementConnection
 * PreparedStatement“?”
 * ConnectionStatementResultSet
 *  @since  1.4 (mostly extracted from QueryRunner)
 */
public  abstract  class  AbstractQueryRunner {
     /**
      * Is { @link  ParameterMetaData#getParameterType(int)} broken (have we tried
      * it yet)?
      * getParameterType(int)
      */
     private  volatile  boolean  pmdKnownBroken  =  false;
     /**
      * The DataSource to retrieve connections from.
      * 使dbutils使dbcpc3p0
      *  @deprecated  Access to this field should be through { @link  #getDataSource()}.
      * 访getDataSource()
      */
    @ Deprecated
     protected  final  DataSource  ds;
     /**
      * Default constructor, sets pmdKnownBroken to false and ds to null.
      * null
      */
     public  AbstractQueryRunner() {
         ds  =  null;
    }
     /**
      * Constructor to control the use of <code>ParameterMetaData</code>.
      * ParameterMetaData使使使
      *  @param  pmdKnownBroken
      *            Some drivers don't support
      *            { @link  ParameterMetaData#getParameterType(int) }; if
      *            <code>pmdKnownBroken</code> is set to true, we won't even try
      *            it; if false, we'll try it, and if it breaks, we'll remember
      *            not to use it again.
      *            getParameterType(int)pmdKnownBrokentrue
      *            false
      */
     public  AbstractQueryRunner( boolean  pmdKnownBroken) {
         this. pmdKnownBroken  =  pmdKnownBroken;
         //null
         ds  =  null;
    }
     /**
      * Constructor to provide a <code>DataSource</code>. Methods that do not
      * take a <code>Connection</code> parameter will retrieve connections from
      * this <code>DataSource</code>.
      * 使Connection
      * 使dbutils使
      *
      *  @param  ds
      *            The <code>DataSource</code> to retrieve connections from.
      */
     public  AbstractQueryRunner( DataSource  ds) {
         this. ds  =  ds;
    }
     /**
      * Constructor to provide a <code>DataSource</code> and control the use of
      * <code>ParameterMetaData</code>. Methods that do not take a
      * <code>Connection</code> parameter will retrieve connections from this
      * <code>DataSource</code>.
      * 
      *  @param  ds
      *            The <code>DataSource</code> to retrieve connections from.
      *  @param  pmdKnownBroken
      *            Some drivers don't support
      *            { @link  ParameterMetaData#getParameterType(int) }; if
      *            <code>pmdKnownBroken</code> is set to true, we won't even try
      *            it; if false, we'll try it, and if it breaks, we'll remember
      *            not to use it again.
      */
     public  AbstractQueryRunner( DataSource  ds,  boolean  pmdKnownBroken) {
         this. pmdKnownBroken  =  pmdKnownBroken;
         this. ds  =  ds;
    }
     /**
      * Returns the <code>DataSource</code> this runner is using.
      * <code>QueryRunner</code> methods always call this method to get the
      * <code>DataSource</code> so subclasses can provide specialized behavior.
      * getter访访
      *  @return  DataSource the runner is using
      */
     public  DataSource  getDataSource() {
         return  this. ds;
    }
     /**
      * Some drivers don't support
      * { @link  ParameterMetaData#getParameterType(int) }; if
      * <code>pmdKnownBroken</code> is set to true, we won't even try it; if
      * false, we'll try it, and if it breaks, we'll remember not to use it
      * again.
      * pmdKnownBrokengetterboolean使isPmdKnownBroken
      *  @return  the flag to skip (or not)
      *         { @link  ParameterMetaData#getParameterType(int) }
      *  @since  1.4
      */
     public  boolean  isPmdKnownBroken() {
         return  pmdKnownBroken;
    }
     /**
      * Factory method that creates and initializes a
      * <code>PreparedStatement</code> object for the given SQL.
      * <code>QueryRunner</code> methods always call this method to prepare
      * statements for them. Subclasses can override this method to provide
      * special PreparedStatement configuration if needed. This implementation
      * simply calls <code>conn.prepareStatement(sql)</code>.
      * SQLPreparedStatementQueryRunnerAbstractQueryRunner
      * statementsPreparedStatement
      * conn.prepareStatement(sql)JDBC
      *  @param  conn
      *            The <code>Connection</code> used to create the
      *            <code>PreparedStatement</code>
      *  @param  sql
      *            The SQL statement to prepare.
      *  @return  An initialized <code>PreparedStatement</code>.
      *  @throws  SQLException
      *             if a database access error occurs
      */
     protected  PreparedStatement  prepareStatement( Connection  conn,  String  sql)
     throws  SQLException {
         return  conn. prepareStatement( sql);
    }
     /**
      * Factory method that creates and initializes a <code>Connection</code>
      * object. <code>QueryRunner</code> methods always call this method to
      * retrieve connections from its DataSource. Subclasses can override this
      * method to provide special <code>Connection</code> configuration if
      * needed. This implementation simply calls <code>ds.getConnection()</code>.
      * 
      * ConnectionQueryRunner
      * getConnection()
      *  @return  An initialized <code>Connection</code>.
      *  @throws  SQLException
      *             if a database access error occurs
      *  @since  DbUtils 1.1
      */
     protected  Connection  prepareConnection()  throws  SQLException {
         if ( this. getDataSource()  ==  null) {
              throw  new  SQLException(
                        "QueryRunner requires a DataSource to be "
                        +  "invoked in this way, or a Connection should be passed in");
        }
         return  this. getDataSource(). getConnection();
    }
     /**
      * Fill the <code>PreparedStatement</code> replacement parameters with the
      * given objects.
      * 使PreparedStatement
      *  @param  stmt
      *            PreparedStatement to fill
      *  @param  params
      *            Query replacement parameters; <code>null</code> is a valid
      *            value to pass in.
      *  @throws  SQLException
      *             if a database access error occurs
      */
     public  void  fillStatement( PreparedStatement  stmt,  Object...  params)
     throws  SQLException {
         // check the parameter count, if we can
         ParameterMetaData  pmd  =  null;
         if ( ! pmdKnownBroken) {  //pmdKnownBrokenfalse
              pmd  =  stmt. getParameterMetaData(); //
              int  stmtCount  =  pmd. getParameterCount(); //
              int  paramsCount  =  params  ==  null ?  0 :  params. length; //
              if ( stmtCount  !=  paramsCount) { //
                   throw  new  SQLException( "Wrong number of parameters: expected "
                             +  stmtCount  +  ", was given "  +  paramsCount);
             }
        }
         // nothing to do here
         if ( params  ==  null) {  //null
              return;
        }
         // TODO else
         for ( int  i  =  0;  i  <  params. length;  i ++) {
              if ( params[ i]  !=  null) {
                   stmt. setObject( i  +  1,  params[ i]);
             }  else {
                   // VARCHAR works with many drivers regardless
                   // of the actual column type. Oddly, NULL and
                   // OTHER don't work with Oracle's drivers.
                   int  sqlType  =  Types. VARCHAR;
                   if ( ! pmdKnownBroken) {
                        try {
                             /*
                               * It's not possible for pmdKnownBroken to change from
                               * true to false, (once true, always true) so pmd cannot
                               * be null here.
                               */
                             sqlType  =  pmd. getParameterType( i  +  1);
                       }  catch ( SQLException  e) {
                             pmdKnownBroken  =  true;
                       }
                  }
                   stmt. setNull( i  +  1,  sqlType);
             }
        }
    }
     /**
      * Fill the <code>PreparedStatement</code> replacement parameters with the
      * given object's bean property values.
      *
      *  @param  stmt
      *            PreparedStatement to fill
      *  @param  bean
      *            a JavaBean object
      *  @param  properties
      *            an ordered array of properties; this gives the order to insert
      *            values in the statement
      *  @throws  SQLException
      *             if a database access error occurs
      */
     public  void  fillStatementWithBean( PreparedStatement  stmt,  Object  bean,
              PropertyDescriptor[]  properties)  throws  SQLException {
         //ObjectSQL
         Object[]  params  =  new  Object[ properties. length];
         //SQL
         for ( int  i  =  0;  i  <  properties. length;  i ++) {
              PropertyDescriptor  property  =  properties[ i]; //i
              Object  value  =  null;
              //igetter
              Method  method  =  property. getReadMethod();
              //getter
              if ( method  ==  null) {
                   throw  new  RuntimeException( "No read method for bean property "
                             +  bean. getClass()  +  " "  +  property. getName());
             }
              try {
                  //getterget
                   value  =  method. invoke( bean,  new  Object[ 0]);
             }  catch ( InvocationTargetException  e) {
                   throw  new  RuntimeException( "Couldn't invoke method: "  +  method,
                             e);
             }  catch ( IllegalArgumentException  e) {
                   throw  new  RuntimeException(
                             "Couldn't invoke method with 0 arguments: "  +  method,  e);
             }  catch ( IllegalAccessException  e) {
                   throw  new  RuntimeException( "Couldn't invoke method: "  +  method,
                             e);
             }
              //Object
              params[ i]  =  value;
        }
         //fillStatementSQL
         fillStatement( stmt,  params);
    }
     /**
      * Fill the <code>PreparedStatement</code> replacement parameters with the
      * given object's bean property values.
      * fillStatementWithBean(PreparedStatement stmt, Object bean,
      *       PropertyDescriptor[] properties)
      * 使
      * 
      *
      *  @param  stmt
      *            PreparedStatement to fill
      *  @param  bean
      *            A JavaBean object
      *  @param  propertyNames
      *            An ordered array of property names (these should match the
      *            getters/setters); this gives the order to insert values in the
      *            statement
      *  @throws  SQLException
      *             If a database access error occurs
      */
     public  void  fillStatementWithBean( PreparedStatement  stmt,  Object  bean,
              String...  propertyNames)  throws  SQLException {
         PropertyDescriptor[]  descriptors;
         try {
              descriptors  =  Introspector. getBeanInfo( bean. getClass())
             . getPropertyDescriptors();
        }  catch ( IntrospectionException  e) {
              throw  new  RuntimeException( "Couldn't introspect bean "
                        +  bean. getClass(). toString(),  e);
        }
         PropertyDescriptor[]  sorted  =  new  PropertyDescriptor[ propertyNames. length];
         for ( int  i  =  0;  i  <  propertyNames. length;  i ++) {
              String  propertyName  =  propertyNames[ i];
              if ( propertyName  ==  null) {
                   throw  new  NullPointerException( "propertyName can't be null: "
                             +  i);
             }
              boolean  found  =  false;
              for ( int  j  =  0;  j  <  descriptors. length;  j ++) {
                   PropertyDescriptor  descriptor  =  descriptors[ j];
                   if ( propertyName. equals( descriptor. getName())) {
                        sorted[ i]  =  descriptor;
                        found  =  true;
                        break;
                  }
             }
              if ( ! found) {
                   throw  new  RuntimeException( "Couldn't find bean property: "
                             +  bean. getClass()  +  " "  +  propertyName);
             }
        }
         fillStatementWithBean( stmt,  bean,  sorted);
    }
     /**
      * Throws a new exception with a more informative error message.
      *
      *  @param  cause
      *            The original exception that will be chained to the new
      *            exception when it's rethrown.
      *
      *  @param  sql
      *            The query that was executing when the exception happened.
      *
      *  @param  params
      *            The query replacement parameters; <code>null</code> is a valid
      *            value to pass in.
      *
      *  @throws  SQLException
      *             if a database access error occurs
      */
     protected  void  rethrow( SQLException  cause,  String  sql,  Object...  params)
     throws  SQLException {
         String  causeMessage  =  cause. getMessage();
         if ( causeMessage  ==  null) {
              causeMessage  =  "";
        }
         StringBuffer  msg  =  new  StringBuffer( causeMessage);
         msg. append( " Query: ");
         msg. append( sql);
         msg. append( " Parameters: ");
         if ( params  ==  null) {
              msg. append( "[]");
        }  else {
              msg. append( Arrays. deepToString( params));
        }
         SQLException  e  =  new  SQLException( msg. toString(),  cause. getSQLState(),
                   cause. getErrorCode());
         e. setNextException( cause);
         throw  e;
    }
     /**
      * 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;
    }
     /**
      * Close a <code>Connection</code>. This implementation avoids closing if
      * null and does <strong>not</strong> suppress any exceptions. Subclasses
      * can override to provide special handling like logging.
      * Connection
      *  @param  conn
      *            Connection to close
      *  @throws  SQLException
      *             if a database access error occurs
      *  @since  DbUtils 1.1
      */
     protected  void  close( Connection  conn)  throws  SQLException {
         DbUtils. close( conn);
    }
     /**
      * Close a <code>Statement</code>. This implementation avoids closing if
      * null and does <strong>not</strong> suppress any exceptions. Subclasses
      * can override to provide special handling like logging.
      * Statement
      *  @param  stmt
      *            Statement to close
      *  @throws  SQLException
      *             if a database access error occurs
      *  @since  DbUtils 1.1
      */
     protected  void  close( Statement  stmt)  throws  SQLException {
         DbUtils. close( stmt);
    }
     /**
      * Close a <code>ResultSet</code>. This implementation avoids closing if
      * null and does <strong>not</strong> suppress any exceptions. Subclasses
      * can override to provide special handling like logging.
      * ResultSet
      *  @param  rs
      *            ResultSet to close
      *  @throws  SQLException
      *             if a database access error occurs
      *  @since  DbUtils 1.1
      */
     protected  void  close( ResultSet  rs)  throws  SQLException {
         DbUtils. close( rs);
    }
}

从这里,我们可以只是,相对于JDBC编程,AbstractQueryRunner已经帮我们封装了一下几步:
1、获得数据库连接
2、获得PreparedStatement
3、为PreparedStatement中的参数设置值
4、关闭ResultSet、关闭PreparedStatement、关闭Connection

这里缺少了最重要的一步,dbutils对于ResultSet怎样处理?这就要下次再说了。 

PS:在这里讲一下
DbUtils中的close方法。在 AbstractQueryRunner的3个close方法中(关闭RS,关闭PS,关闭Connection)中就是调用了DbUtils中相对应的close方法。
 
 
     /**
      * Close a <code>Connection</code>, avoid closing if null.
      * ConnectionDataSourceConnectionclose
      * ConnectionConnection pool
      *  @param  conn Connection to close.
      *  @throws  SQLException if a database access error occurs
      */
     public  static  void  close( Connection  conn)  throws  SQLException {
         //null
         if ( conn  !=  null) {
              conn. close();
        }
    }
     /**
      * Close a <code>ResultSet</code>, avoid closing if null.
      * RS
      *  @param  rs ResultSet to close.
      *  @throws  SQLException if a database access error occurs
      */
     public  static  void  close( ResultSet  rs)  throws  SQLException {
         if ( rs  !=  null) {
              rs. close();
        }
    }
     /**
      * Close a <code>Statement</code>, avoid closing if null.
      * Statement
      *  @param  stmt Statement to close.
      *  @throws  SQLException if a database access error occurs
      */
     public  static  void  close( Statement  stmt)  throws  SQLException {
         if ( stmt  !=  null) {
              stmt. close();
        }
    }
 

转载于:https://my.oschina.net/u/198574/blog/160902

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值