DBUtils源碼分析,dbutils源碼

來源:互聯網
上載者:User

DBUtils源碼分析,dbutils源碼

其實,在這篇文章裡,我只是分析了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那就得拋出異常了。

相關文章

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.