MyBatisMyBatis源码解析(五)——DataSource数据源模块的非池型数据源

原创作品,可以转载,但是要标注出处地址:http://www.cnblogs.com/V1haoge/p/6675633.html 

1 回顾

  达到一致首中本身说明了数码源接口DataSource与数据源工厂接口DataSourceFactory,这两者是MyBatis数据源模块的底蕴,包括本文中之非池型非池型数据源(UnpooledDataSource)和之后的池型数据源(PooledDataSource)、托管型数据源(JndiDataSourceFactory)都是在及时有限单接口及发生的。

  本文解读一下MyBatis中的非池型数据源,这是基础之数据源,之后如果解读的池型数据源又是盖这个数据源为底蕴来的。

2 非池型数据源及其工厂

2.1 非池型数据源工厂:UnpooledDataSourceFactory

  该数据源工厂实现了DataSourceFactory接口,源码如下:

 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    * 在工厂的构造器中创建具体数据源的实例并赋值,这将用于供getDataSource()方法获取数据源实例
17    */
18   public UnpooledDataSourceFactory()
19   {
20     this.dataSource = new UnpooledDataSource();
21   }
22   /*
23    * 设置数据源驱动器属性,有两种情况
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设置方法
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    * 值类型强转方法
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   }

  以上源码中,数据源工厂可经任参构造器创建具体的数据源实例,然后可由此getDataSource()方法,来取得之前以构造器中创造的数据源实例,getDataSource()方法是对外的。

  该工厂类的最主要是装属性之措施:setProperties(Properties
properties),我们延续简单解析:

  第27尽:创建有变量driverProperties,用于存放参数属性列表中经过过滤的习性

  第28执行:在我们创建该工厂实例的根基及(即许段dataSource就被赋值的功底及),调用MyBatis提供的初次对象MetaObject来变化针对dataSource实例的元实例metaDataSource。

    解析:元实例,可以看作是对准现实实例的平批判装饰器,在包装器核心是有血有肉实例,外围是大半单装饰器(包装器),用于提高力量。

  第29尽:遍历获取之习性列表Properties(参数)

  第31-33推行:针对参数列表中属性名是坐driver.为前缀的性质,获取其价值,保存于driverProperties中因备后用。

  第34-37尽:针对参数列表中属性名在dataSource实例中存在set方法的习性,获取其价值,将价值经过必要之变换后,将其保存至metaDataSource元实例中

    这个实在是最常使用的,我们当布置文件中安排的driver、url、username、password四单参数,全部都是以之种办法保留到dataSource实例中的。

  第42-44行:最后对driverProperties变量进行null判断,即判断有无通过前缀方式安排的性,如果发生则以这些配置同样保留至metaDataSource元实例中

  这样交最终实在有着的布置信息都封存及了metaDataSource元实例中,这样说实在不对,其实最终经过MetaObject的setValue()方法,将兼具这些通过过滤的性质设置保存至了长实例的为主:dataSource中了(内部原理,荣后禀明),对应于UnpolledDataSource中的driver、url、username、password、driverProperties这五只字段(见下文)。

  总结来探视:数据源工厂的目的就是是叙配置文件中之配备内容安排到通过投机的构造器创建的现实多少源实例中,再运getDataSource()方法返回给调用方。

2.2 非池型数据源:UnpolledDataSource

  该类实现了DataSource数据源接口,实现了中的享有抽象方法。

  非池型是对立池型而言之,池型数据源统筹管理在一个数据库连接池,在这个池子中装有指定数量的数据库连接实例connection可供使用,其中间使用自然的平整指定数量连接对象的应用、创建、销毁规则,来吗多线程数据库访问提供这得力的数据库连接。

  非池型数据源,即维持有一个数据库连接实例的数据源。下面我们来看望这种数据源的兑现方式:

  首先我们来瞧字段:

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;

  我们挨个进行分析:

    ClassLoader driverClassLoader:这个是数据库的教类加载器:目的正是用于打磁盘中加载数据库让类及内存

    Properties driverProperties:驱动器属性,这个用于保存我们手动设置的数据库让器属性,如果我们不进行安装,则会利用默认值。这个特性设置的键一般还见面是坐“driver.”为前缀。

    Map<String,Driver> registeredDrivers =
new ConcurrentHashMap<String,Driver>():表示数据库让注册器,其中间保存在有曾经注册之数据库让类实例,这个字段是static修饰的,表示在数额源类加载的当儿即便会见创,这个时段创建的其实是独缺损集合。再者使用ConcurrentHashMap集合,这是一个线程安全之键值对型集合,它几乎可以与HashTable通用(二者都是线程安全的集合类)。这个静态字段其实是跟今后要提到的一个静态代码块配合工作之(容后介绍)。

    String driver:数据库让类名

    String url:数据库服务器URL地址

    String username:数据库服务器连接用户称

    String password:数据库服务器连接密码

    Boolean autoCommit:是否自动提交,这是一个逻辑值,真表示以机动提交,假表示关闭自动提交。上一样中和被干了活动提交,其实那里的自行提交值就是于这里安装的。

    Integer defaultTransactionIsolationLevel:表示默认的作业级别

静态代码块:

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   }

  这个静态块被之始末要整合之前的静态字段registeredDrivers来进展分析。

  DriverManager是JDK提供的驱动器管理类,在此类加载时见面实行中的静态块,静态块被调用了一个loadInitialDrivers()方法,这是一个加载原始驱动器的法子,他将JDK中之本来面目配置jdbc.drivers中的教器名表示的驱动类加载到内存,其会调用本地方法开展驱动类进行加载,然后调用DriverManager中的registerDriver()方法将让类实例一一保存到DriverManager中之registeredDrivers集合中。

  我们这里调用DriverManager中getDrivers()方法,将会晤取得DriverManager中在集合registeredDrivers中之保留之驱动器实例,在获得的时刻会展开类似加载验证,验证的目的是管以本类加载器获取之使器类与当registeredDrivers中保存的相应驱动类实例的类是同一品种(==)。最后收获到之是叫实例的枚举。

  对斯枚举进行遍历,将中间拥有驱动器实例保存及我们脚下的UnpooledDataSource中之静态集合registeredDrivers(区别为DriverManager中的同名集合,二者类型且不同)中。

  这样保证在此类加载的时节就是用默认的驱动器实例加载到静态集合中盖备用。该静态代码块的意就是吗registeredDrivers集合赋值。

脚我们来瞧实现之DataSource中的构造方法:

 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   }

  可见UnpooledDataSource提供了五单构造器,第一只呢无参构造器,其余均带有赋值功能:以让器类全限定名driver、数据库服务器地址url、数据库登录用户名及密码为参数是无比常用的数据源构建方式,也得将用户称和密码组合呢因驱动器参数driverProperties的形式传参,甚至还得指定驱动类的近乎加载器driverClassLoader。

  这里太常用之尚是无论参构造器,这个构造器在数据源工厂的凭参构造器中给调用,用于创造一个空的UnpolledDataSource实例,然后使用工厂类吃之setProperties()方法,为这个拖欠实例中的相继字段进行赋值(采用地方提到的第二栽方法进行读取配置信息并保存至实例中),从而创造一个精神的实例,

  下面看看实现自接口的法子

 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   }

  这五个点子继承自CommonDataSource通用数据源接口,其中各级艺术的义如下:

    setLoginTimeout():表示设置数据源连接数据库的最长等时,以秒为单位

    getLoginTimeout():表示获取数据源连接到数据库的最长等时

    setLogWriter():设置数据源的日志输出者(log
writer)为加的一个PrintWriter实例

    getLogWriter():获取数据源之日记输出者。

    通过地方的源码可以窥见,数据源类中的这些艺术的兑现都是一直调用的DriverManager的附和措施。

     getParentLogger():获取这DataSource所使用的持有Logger的父Logger。

  下面是UnpooledDataSource中极其根本的不二法门:

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   }

  这是少单获取数据源连接的点子,第一独凡是无参方法,它里面以默认的用户称和实质进行数据库连接,第二只供了点名的用户称以及密码,使用指定的用户称和密码进行数据库连接,并将拖欠连返回。二者间还调用了跟一个道doGetConnection(),这是实在实行数据库连接并取之连续的法子。

 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   }

  解析:内部定义一个Properties属性变量用于存储传递的参数,先对driverProperties(里面保存之是坐driver.为前缀设置的配置信息)进行判定,如果发生价直接以那个中间的价值周变换到新的properties中,再判断传递的username与password,并以那个保存至properties中,其实此时,properties中保存的凡有关数据源连接的基本功信息。

  上面的法门最后调用了重载的同名方法:

1   private Connection doGetConnection(Properties properties) throws SQLException {
2     initializeDriver();
3     //属性的前缀是以“driver.”开 头的,它 是 通 过 DriverManager.getConnection(url,driverProperties)方法传递给数据库驱动
4     Connection connection = DriverManager.getConnection(url, properties);
5     configureConnection(connection); 
6     return connection;
7   }

  以斯重载方法中首先调用initializeDriver()方法开展驱动器初始化:

 1   private synchronized void initializeDriver() throws SQLException {
 2       //这里便是大家熟悉的初学JDBC时的那几句话了 Class.forName newInstance()
 3     if (!registeredDrivers.containsKey(driver)) {
 4       Class<?> driverType;
 5       try {
 6         if (driverClassLoader != null) {
 7           driverType = Class.forName(driver, true, driverClassLoader);
 8         } else {
 9           driverType = Resources.classForName(driver);
10         }
11         // DriverManager requires the driver to be loaded via the system ClassLoader.
12         // http://www.kfu.com/~nsayer/Java/dyn-jdbc.html
13         Driver driverInstance = (Driver)driverType.newInstance();
14         DriverManager.registerDriver(new DriverProxy(driverInstance));
15         registeredDrivers.put(driver, driverInstance);
16       } catch (Exception e) {
17         throw new SQLException("Error setting driver on UnpooledDataSource. Cause: " + e);
18       }
19     }
20   }

  这是驱动器初始化的不二法门,该方式以synchronized进行修饰,表示马上是一个一头方法,同一时刻只能给同一介乎调用,这个点子的打算是加载驱动器类,并拿该实例注册到DriverManager中,同时以那保存到比如实例的registeredDrivers中。

  这里发出只问题就是是DriverProxy,这是一个令代理,这个仿佛是盖静态代理类的方法定义的,其落实了Driver接口,实现了Driver接口中的富有抽象方法,是一个着实的代理类,代理Driver真正的贯彻类似,即确实打作用的让类实例,代理类将驱动类的有着方漫天掩护起来,这里真的的目的尚伸手大家共商讨论。

  然后返回上亦然步道被,通过调研DriverManager的getConnection方法来获取数据库连接connection,最后调用configureConnection()方法进行数据库连接的尾声安排,配置的内容如下:

1   private void configureConnection(Connection conn) throws SQLException {
2     if (autoCommit != null && autoCommit != conn.getAutoCommit()) {
3       conn.setAutoCommit(autoCommit);
4     }
5     if (defaultTransactionIsolationLevel != null) {
6       conn.setTransactionIsolation(defaultTransactionIsolationLevel);
7     }
8   }

  配置内容吗:自动提交(boolean值)与事务级别

  最后用获得到之数据库连接返回。

  为什么我会说失误啊?因为该是存活数据源,再闹数据库连接,再来业务操作,所以照应有先介绍DataSource,再介绍Transaction的,但是此地我说反而了……

  (在斯做个记录)

网站地图xml地图