1 回顧

  上一篇中我解說了數(shù)據(jù)源接口DataSource與數(shù)據(jù)源工廠接口DataSourceFactory,這二者是MyBatis數(shù)據(jù)源模塊的基礎(chǔ),包括本文中的非池型非池型數(shù)據(jù)源(UnpooledDataSource)和之后的池型數(shù)據(jù)源(PooledDataSource)、托管型數(shù)據(jù)源(JndiDataSourceFactory)都是在這兩個接口上產(chǎn)生的。

  本文解讀一下MyBatis中的非池型數(shù)據(jù)源,這是基礎(chǔ)的數(shù)據(jù)源,之后要解讀的池型數(shù)據(jù)源又是以此數(shù)據(jù)源為基礎(chǔ)產(chǎn)生的。

2 非池型數(shù)據(jù)源及其工廠

2.1 非池型數(shù)據(jù)源工廠:UnpooledDataSourceFactory

  該數(shù)據(jù)源工廠實現(xiàn)了DataSourceFactory接口,源碼如下:

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動開發(fā)培訓(xùn)

 1 package org.apache.ibatis.datasource.unpooled;
 2 import java.util.Iterator;
 3 import java.util.Properties;
 4 import java.util.Set;
 5 import javax.sql.DataSource;
 6 import org.apache.ibatis.datasource.DataSourceException;
 7 import org.apache.ibatis.datasource.DataSourceFactory;
 8 import org.apache.ibatis.reflection.MetaObject;
 9 import org.apache.ibatis.reflection.SystemMetaObject;
10 
11 public class UnpooledDataSourceFactory implements DataSourceFactory{
12   private static final String DRIVER_PROPERTY_PREFIX = "driver.";//屬性前綴
13   private static final int DRIVER_PROPERTY_PREFIX_LENGTH = "driver.".length();//屬性前綴的長度
14   protected DataSource dataSource;
15   /*
16    * 在工廠的構(gòu)造器中創(chuàng)建具體數(shù)據(jù)源的實例并賦值,這將用于供getDataSource()方法獲取數(shù)據(jù)源實例
17    */
18   public UnpooledDataSourceFactory()
19   {
20     this.dataSource = new UnpooledDataSource();
21   }
22   /*
23    * 設(shè)置數(shù)據(jù)源驅(qū)動器屬性,有兩種情況
24    */
25   public void setProperties(Properties properties)
26   {
27     Properties driverProperties = new Properties();
28     MetaObject metaDataSource = SystemMetaObject.forObject(this.dataSource);
29     for (Iterator i$ = properties.keySet().iterator(); i$.hasNext(); ) { Object key = i$.next();
30       String propertyName = (String)key;
31       if (propertyName.startsWith("driver.")) {//第一種情況,以driver.開頭的屬性名
32         String value = properties.getProperty(propertyName);
33         driverProperties.setProperty(propertyName.substring(DRIVER_PROPERTY_PREFIX_LENGTH), value);
34       } else if (metaDataSource.hasSetter(propertyName)) {//元對象中擁有針對屬性名的set設(shè)置方法
35         String value = (String)properties.get(propertyName);
36         Object convertedValue = convertValue(metaDataSource, propertyName, value);
37         metaDataSource.setValue(propertyName, convertedValue);
38       } else {
39         throw new DataSourceException("Unknown DataSource property: " + propertyName);
40       }
41     }
42     if (driverProperties.size() > 0)
43       metaDataSource.setValue("driverProperties", driverProperties);
44   }
45 
46   public DataSource getDataSource()
47   {
48     return this.dataSource;
49   }
50   /*
51    * 值類型強(qiáng)轉(zhuǎn)方法
52    */
53   private Object convertValue(MetaObject metaDataSource, String propertyName, String value) {
54     Object convertedValue = value;
55     Class targetType = metaDataSource.getSetterType(propertyName);
56     if ((targetType == Integer.class) || (targetType == Integer.TYPE))
57       convertedValue = Integer.valueOf(value);
58     else if ((targetType == Long.class) || (targetType == Long.TYPE))
59       convertedValue = Long.valueOf(value);
60     else if ((targetType == Boolean.class) || (targetType == Boolean.TYPE)) {
61       convertedValue = Boolean.valueOf(value);
62     }
63     return convertedValue;
64   }

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動開發(fā)培訓(xùn)

  以上源碼中,數(shù)據(jù)源工廠可通過無參構(gòu)造器創(chuàng)建具體的數(shù)據(jù)源實例,然后可通過getDataSource()方法,來獲取之前在構(gòu)造器中創(chuàng)建的數(shù)據(jù)源實例,getDataSource()方法是對外的。

  該工廠類的重點是設(shè)置屬性的方法:setProperties(Properties properties),我們繼續(xù)簡單解析:

  第27行:創(chuàng)建局部變量driverProperties,用于存放參數(shù)屬性列表中經(jīng)過過濾的屬性

  第28行:在我們創(chuàng)建該工廠實例的基礎(chǔ)上(即字段dataSource已被賦值的基礎(chǔ)上),調(diào)用MyBatis提供的元對象MetaObject來生成針對dataSource實例的元實例metaDataSource。

    解析:元實例,可以看成是針對具體實例的一批裝飾器,在包裝器核心是具體實例,外圍是多個裝飾器(包裝器),用于增強(qiáng)功能。

  第29行:遍歷獲取的屬性列表Properties(參數(shù))

  第31-33行:針對參數(shù)列表中屬性名是以driver.為前綴的屬性,獲取其值,保存在driverProperties中以備后用。

  第34-37行:針對參數(shù)列表中屬性名在dataSource實例中存在set方法的屬性,獲取其值,將值經(jīng)過必要的轉(zhuǎn)換后,將其保存到metaDataSource元實例中

    這個其實是最常使用的,我們在配置文件中配置的driver、url、username、password四個參數(shù),全部都是以此種方式保存到dataSource實例中的。

  第42-44行:最后對driverProperties變量進(jìn)行null判斷,即判斷有無通過前綴方式配置的屬性,如果有則將這些配置同樣保存到metaDataSource元實例中

  這樣到最后其實所有的配置信息都保存到了metaDataSource元實例中,這樣說其實不對,其實最后通過MetaObject的setValue()方法,將所有這些經(jīng)過過濾的屬性設(shè)置保存到了元實例的核心:dataSource中了(內(nèi)部原理,榮后稟明),對應(yīng)于UnpolledDataSource中的driver、url、username、password、driverProperties這五個字段(見下文)。

  總結(jié)來看看:數(shù)據(jù)源工廠的目的就是講配置文件中的配置內(nèi)容配置到通過自己的構(gòu)造器創(chuàng)建的具體數(shù)據(jù)源實例中,再使用getDataSource()方法返回給調(diào)用方。

2.2 非池型數(shù)據(jù)源:UnpolledDataSource

  該類實現(xiàn)了DataSource數(shù)據(jù)源接口,實現(xiàn)了其中的所有抽象方法。

  非池型是相對池型而言的,池型數(shù)據(jù)源統(tǒng)籌管理著一個數(shù)據(jù)庫連接池,在這個池中擁有指定數(shù)量的數(shù)據(jù)庫連接實例connection可供使用,其內(nèi)部采用一定的規(guī)則指定數(shù)據(jù)連接對象的使用、創(chuàng)建、銷毀規(guī)則,來為多線程數(shù)據(jù)庫訪問提供及時有效的數(shù)據(jù)庫連接。

  非池型數(shù)據(jù)源,即保持有一個數(shù)據(jù)庫連接實例的數(shù)據(jù)源。下面我們來看看這種數(shù)據(jù)源的實現(xiàn)方式:

  首先我們來看看字段:

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動開發(fā)培訓(xùn)

1   private ClassLoader driverClassLoader;
2   private Properties driverProperties;
3   private static Map<String, Driver> registeredDrivers = new ConcurrentHashMap<String, Driver>();
4   private String driver;
5   private String url;
6   private String username;
7   private String password;
8   private Boolean autoCommit;
9   private Integer defaultTransactionIsolationLevel;

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動開發(fā)培訓(xùn)

  我們一一進(jìn)行解析:

    ClassLoader driverClassLoader:這個是數(shù)據(jù)庫的驅(qū)動類加載器:目的正是用于從磁盤中加載數(shù)據(jù)庫驅(qū)動類到內(nèi)存

    Properties driverProperties:驅(qū)動器屬性,這個用于保存我們手動設(shè)置的數(shù)據(jù)庫驅(qū)動器屬性,如果我們不進(jìn)行設(shè)置,則會使用默認(rèn)值。這個屬性設(shè)置的鍵一般都會是以“driver.”為前綴。

    Map<String,Driver> registeredDrivers = new ConcurrentHashMap<String,Driver>():表示數(shù)據(jù)庫驅(qū)動注冊器,其內(nèi)部保存著所有已注冊的數(shù)據(jù)庫驅(qū)動類實例,這個字段是static修飾的,表示在數(shù)據(jù)源類加載的時候就會創(chuàng)建,這個時候創(chuàng)建的其實是個空集合。再者使用ConcurrentHashMap集合,這是一個線程安全的鍵值對型集合,它幾乎可以與HashTable通用(二者都是線程安全的集合類)。這個靜態(tài)字段其實是和之后要提到的一個靜態(tài)代碼塊配合工作的(容后介紹)。

    String driver:數(shù)據(jù)庫驅(qū)動類名

    String url:數(shù)據(jù)庫服務(wù)器URL地址

    String username:數(shù)據(jù)庫服務(wù)器連接用戶名

    String password:數(shù)據(jù)庫服務(wù)器連接密碼

    Boolean autoCommit:是否自動提交,這是一個邏輯值,真表示使用自動提交,假表示關(guān)閉自動提交。上一文中提到了自動提交,其實那里的自動提交值就是在這里設(shè)置的。

    Integer defaultTransactionIsolationLevel:表示默認(rèn)的事務(wù)級別

靜態(tài)代碼塊:

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動開發(fā)培訓(xùn)

1   static {
2     Enumeration<Driver> drivers = DriverManager.getDrivers();
3     while (drivers.hasMoreElements()) {
4       Driver driver = drivers.nextElement();
5       registeredDrivers.put(driver.getClass().getName(), driver);
6     }
7   }

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動開發(fā)培訓(xùn)

  這個靜態(tài)塊中的內(nèi)容需要結(jié)合之前的靜態(tài)字段registeredDrivers來進(jìn)行解析。

  DriverManager是JDK提供的驅(qū)動器管理類,在該類加載時會執(zhí)行其中的靜態(tài)塊,靜態(tài)塊中調(diào)用了一個loadInitialDrivers()方法,這是一個加載原始驅(qū)動器的方法,他將JDK中的原始配置jdbc.drivers中的驅(qū)動器名表示的驅(qū)動類加載到內(nèi)存,其會調(diào)用本地方法進(jìn)行驅(qū)動類進(jìn)行加載,然后調(diào)用DriverManager中的registerDriver()方法將驅(qū)動類實例一一保存到DriverManager中的registeredDrivers集合中。

  我們這里調(diào)用DriverManager中g(shù)etDrivers()方法,將會獲取DriverManager中在集合registeredDrivers中的保存的驅(qū)動器實例,在獲取的時候會進(jìn)行類加載驗證,驗證的目的是確保使用本類加載器獲取的驅(qū)動器類與在registeredDrivers中保存的對應(yīng)驅(qū)動類實例的類是同一類型(==)。最后獲取到的是驅(qū)動實例的枚舉。

  對這個枚舉進(jìn)行遍歷,將其中所有驅(qū)動器實例保存到我們當(dāng)前的UnpooledDataSource中的靜態(tài)集合registeredDrivers(區(qū)別于DriverManager中的同名集合,二者類型都不同)中。

  這樣保證在該類加載的時候就將默認(rèn)的驅(qū)動器實例加載到靜態(tài)集合中以備用。該靜態(tài)代碼塊的作用就是為registeredDrivers集合賦值。

下面我們來看看實現(xiàn)的DataSource中的構(gòu)造方法:

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動開發(fā)培訓(xùn)

 1   public UnpooledDataSource() {
 2   }
 3   public UnpooledDataSource(String driver, String url, String username, String password) {
 4     this.driver = driver;
 5     this.url = url;
 6     this.username = username;
 7     this.password = password;
 8   }
 9   public UnpooledDataSource(String driver, String url, Properties driverProperties) {
10     this.driver = driver;
11     this.url = url;
12     this.driverProperties = driverProperties;
13   }
14   public UnpooledDataSource(ClassLoader driverClassLoader, String driver, String url, String username, String password) {
15     this.driverClassLoader = driverClassLoader;
16     this.driver = driver;
17     this.url = url;
18     this.username = username;
19     this.password = password;
20   }
21   public UnpooledDataSource(ClassLoader driverClassLoader, String driver, String url, Properties driverProperties) {
22     this.driverClassLoader = driverClassLoader;
23     this.driver = driver;
24     this.url = url;
25     this.driverProperties = driverProperties;
26   }

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動開發(fā)培訓(xùn)

  可見UnpooledDataSource提供了五個構(gòu)造器,第一個為無參構(gòu)造器,其余皆帶有賦值功能:以驅(qū)動器類全限定名driver、數(shù)據(jù)庫服務(wù)器地址url、數(shù)據(jù)庫登錄用戶名及密碼為參數(shù)是最為常用的數(shù)據(jù)源構(gòu)建方式,也可以將用戶名與密碼整合為以驅(qū)動器參數(shù)driverProperties的形式傳參,甚至還可以指定驅(qū)動類的類加載器driverClassLoader。

  這里最常用的還是無參構(gòu)造器,這個構(gòu)造器在數(shù)據(jù)源工廠的無參構(gòu)造器中被調(diào)用,用于創(chuàng)建一個空的UnpolledDataSource實例,然后使用工廠類中的setProperties()方法,為這個空實例中的各個字段進(jìn)行賦值(采用上面提到的第二種方式進(jìn)行讀取配置信息并保存到實例中),從而創(chuàng)建一個飽滿的實例,

  下面看看實現(xiàn)自接口的方法

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動開發(fā)培訓(xùn)

 1   public void setLoginTimeout(int loginTimeout) throws SQLException
 2   {
 3     DriverManager.setLoginTimeout(loginTimeout);
 4   }
 5   public int getLoginTimeout() throws SQLException
 6   {
 7     return DriverManager.getLoginTimeout();
 8   }
 9   public void setLogWriter(PrintWriter logWriter) throws SQLException
10   {
11     DriverManager.setLogWriter(logWriter);
12   }
13   public PrintWriter getLogWriter() throws SQLException
14   {
15     return DriverManager.getLogWriter();
16   }
17   public Logger getParentLogger()
18  {
19     return Logger.getLogger("global");
20   }

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動開發(fā)培訓(xùn)

  這五個方法繼承自CommonDataSource通用數(shù)據(jù)源接口,其中各方法的意義如下:

    setLoginTimeout():表示設(shè)置數(shù)據(jù)源連接數(shù)據(jù)庫的最長等待時間,以秒為單位

    getLoginTimeout():表示獲取數(shù)據(jù)源連接到數(shù)據(jù)庫的最長等待時間

    setLogWriter():設(shè)置數(shù)據(jù)源的日志輸出者(log writer)為給定的一個PrintWriter實例

    getLogWriter():獲取數(shù)據(jù)源的日志輸出者。

    通過上面的源碼可以發(fā)現(xiàn),數(shù)據(jù)源類中的這些方法的實現(xiàn)都是直接調(diào)用的DriverManager的對應(yīng)方法。

     getParentLogger():獲取這個DataSource所使用的所有Logger的父Logger。

  下面是UnpooledDataSource中最重要的方法:

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動開發(fā)培訓(xùn)

1   @Override
2   public Connection getConnection() throws SQLException {
3     return doGetConnection(username, password);
4   }
5   @Override
6   public Connection getConnection(String username, String password) throws SQLException {
7     return doGetConnection(username, password);
8   }

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動開發(fā)培訓(xùn)

  這是兩個獲取數(shù)據(jù)源連接的方法,第一個是無參方法,它內(nèi)部使用默認(rèn)的用戶名與面目進(jìn)行數(shù)據(jù)庫連接,第二個提供了指定的用戶名與密碼,使用指定的用戶名與密碼進(jìn)行數(shù)據(jù)庫連接,并將該連接返回。二者內(nèi)部都調(diào)用了同一個方法doGetConnection(),這是真正執(zhí)行數(shù)據(jù)庫連接并獲取這個連接的方法。

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動開發(fā)培訓(xùn)

 1   private Connection doGetConnection(String username, String password) throws SQLException {
 2     Properties props = new Properties();
 3     if (driverProperties != null) {
 4       props.putAll(driverProperties);
 5     }
 6     if (username != null) {
 7       props.setProperty("user", username);
 8     }
 9     if (password != null) {
10       props.setProperty("password", password);
11     }
12     return doGetConnection(props);
13   }

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動開發(fā)培訓(xùn)

  解析:內(nèi)部定義一個Properties屬性變量用于存儲傳遞的參數(shù),先對driverProperties(里面保存的是以driver.為前綴設(shè)置的配置信息)進(jìn)行判斷,如果有值直接將其內(nèi)部的值全部轉(zhuǎn)移到新的properties中,再判斷傳遞的username與password,并將其保存到properties中,其實這時,properties中保存的是有關(guān)數(shù)據(jù)源連接的基礎(chǔ)信息。

  上面的方法最后調(diào)用了重載的同名方法:

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動開發(fā)培訓(xùn)

1   private Connection doGetConnection(Properties properties) throws SQLException