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  dsboolean  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  connString  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  stmtObject...  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  =  0i  <  params. lengthi ++) {
              if ( params[ i!=  null) {
                   stmt. setObject( i  +  1params[ 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  +  1sqlType);
             }
        }
    }
     /**
      * 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  stmtObject  bean,
              PropertyDescriptor[]  propertiesthrows  SQLException {
         //ObjectSQL
         Object[]  params  =  new  Object[ properties. length];
         //SQL
         for ( int  i  =  0i  <  properties. lengthi ++) {
              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( beannew  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: "  +  methode);
             }  catch ( IllegalAccessException  e) {
                   throw  new  RuntimeException( "Couldn't invoke method: "  +  method,
                             e);
             }
              //Object
              params[ i=  value;
        }
         //調fillStatementSQL
         fillStatement( stmtparams);
    }
     /**
      * 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  stmtObject  bean,
              String...  propertyNamesthrows  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  =  0i  <  propertyNames. lengthi ++) {
              String  propertyName  =  propertyNames[ i];
              if ( propertyName  ==  null) {
                   throw  new  NullPointerException( "propertyName can't be null: "
                             +  i);
             }
              boolean  found  =  false;
              for ( int  j  =  0j  <  descriptors. lengthj ++) {
                   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( stmtbeansorted);
    }
     /**
      * 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  causeString  sqlObject...  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  connthrows  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  stmtthrows  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  rsthrows  SQLException {
         DbUtils. close( rs);
    }
}

從這裏,咱們能夠只是,相對於JDBC編程,AbstractQueryRunner已經幫咱們封裝了一下幾步:
一、得到數據庫鏈接
二、得到PreparedStatement
三、爲PreparedStatement中的參數設置值
四、關閉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  connthrows  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  rsthrows  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  stmtthrows  SQLException {
         if ( stmt  !=  null) {
              stmt. close();
        }
    }
相關文章
相關標籤/搜索