MyBatis源码解读(1)——SqlSessionFactory

在面前对MyBatis稍微有点驾驭过后,现在来对MyBatis的源码试着解读一下,并不是分析,暂时定为解读。所有对MyBatis解读均是根据MyBatis-3.4.1,官网中文文档:http://www.mybatis.org/mybatis-3/zh/getting-started.htmlMyBatis-3.4.1.jar

本应在发轫读MyBatis源码时首先应该精晓下MyBatis的SqlSession的四大目的:Executor、StatemenHandler、ParameterHandler、ResultHandler,但自己想把那四大目的放置大家源码中一步一步来解读。

开始。


 对MyBatis的施用我们在最发轫都已经知道可以经过xml配置文件的不二法门,也足以透过Java代码创造Configuration对象的方法。那两者其实是一律,xml配置文件的法子最终也是经过解析xml配置文件创设一个Configuration对象。可能对于许三个人的话MyBatis经常是和Spring合营使用,用了N年MyBatis也不可以把MyBatis说个所以出来。写MyBatis的那些体系,正式希望不要只光会用,还要懂其规律,熟习一个语言、一个框架的特征原理才能在分化场所使用差别的特点。

回到正题,我们说到应用MyBatis第一步就是配置,或者说第二个重点的靶子就是Configuration。但自己想要阅读的首先个源码并不是Configuration类,大家暂且知道它会贯通整个MyBatis的生命周期,它是存放在一些布署所在的地点即可。大家要说的是MyBatis第一个至关首要片段——SqlSession的举行进程。

从官方文档的声明,大家可以清楚SqlSession是由SqlSessionFactory创造,SqlSessionFactoryBuilder创制。

MyBatis 1

咱俩由此代码不难回想一下SQLSession实例的创始进程。

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();

在成立一个SqlSession实例时,首先要求创设一个SqlSessionFactory实例,而又要求经过SqlSessionFactoryBuilder()的build静态方法来创建SqlSessionFactory。(关于那三者的功能域(Scope)及生命周期以前有介绍过,那里不再多讲,参考《SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession功用域(Scope)和生命周期》

为此大家率先来看望SqlSessionFactoryBuilder这么些类。它放置在package
org.apache.ibatis.session包中。

 1 public class SqlSessionFactoryBuilder {
 2 
 3   public SqlSessionFactory build(Reader reader) {
 4     return build(reader, null, null);
 5   }
 6 
 7   public SqlSessionFactory build(Reader reader, String environment) {
 8     return build(reader, environment, null);
 9   }
10 
11   public SqlSessionFactory build(Reader reader, Properties properties) {
12     return build(reader, null, properties);
13   }
14 
15   public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
16     try {
17       XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
18       return build(parser.parse());
19     } catch (Exception e) {
20       throw ExceptionFactory.wrapException("Error building SqlSession.", e);
21     } finally {
22       ErrorContext.instance().reset();
23       try {
24         reader.close();
25       } catch (IOException e) {
26         // Intentionally ignore. Prefer previous error.
27       }
28     }
29   }
30 
31   public SqlSessionFactory build(InputStream inputStream) {
32     return build(inputStream, null, null);
33   }
34 
35   public SqlSessionFactory build(InputStream inputStream, String environment) {
36     return build(inputStream, environment, null);
37   }
38 
39   public SqlSessionFactory build(InputStream inputStream, Properties properties) {
40     return build(inputStream, null, properties);
41   }
42 
43   public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
44     try {
45       XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
46       return build(parser.parse());
47     } catch (Exception e) {
48       throw ExceptionFactory.wrapException("Error building SqlSession.", e);
49     } finally {
50       ErrorContext.instance().reset();
51       try {
52         inputStream.close();
53       } catch (IOException e) {
54         // Intentionally ignore. Prefer previous error.
55       }
56     }
57   }
58     
59   public SqlSessionFactory build(Configuration config) {
60     return new DefaultSqlSessionFactory(config);
61   }
62 
63 }

咱俩得以看看这么些类用很多的构造方法,但紧要分为三大类:1、第3-29行是经过读取字符流(里德r)的措施构件SqlSessionFactory。2、第31-57行是由此字节流(InputStream)的艺术构件SqlSessionFacotry。3、第59行直接通过Configuration对象营造SqlSessionFactory。第1、2种办法是经过安插文件措施,第3种是透过Java代码格局。

让大家再仔细来看到底是怎么创设出SqlSessionFactory的啊?以通过InputStream字节流的模式来看,和它相关的共计有4个构造方法,其中最后一个public
SqlSessionFactory build(InputStream inputStream, String environment,
Properties
properties),第2个和第3个参数并不生疏,这一定于在报告那三个布局项environment、properties是可以通过在打造SqlSessionFactory的时候进行布局的或重新配置(此时优先级最高)。首先通过第45-46行代码XMLConfigBuilder工具类对配备文件进行解析成Configuration对象,再调用public
SqlSessionFactory build(Configuration
config)营造出SqlSessionFactory,所以兜兜转转,不管是计划文件或者Java代码,最终都会经过分析通过Configuration对象发生SqlSessionFactory。

咱俩得以窥见第60行代码重返的是DefaultSqlSessionFactory实例,而不是SqlSessionFactory。那是因为其实SqlSessionFactory是一个接口,而DefaultSqlSessionFactory是它的兑现类。如下图所示。

MyBatis 2

在此地大家暂且不论SqlSessionManager,大家只需领悟SqlSessionFactory有DefaultSqlSessionFactory和SqlSessionManager。在SqlSessionFactory可以算计一下有啥样办法。

回首SqlSession的开创进度,其实大家也能猜测获得SqlSessionFactory一定重倘使制造SqlSession实例的主意。

 1 public interface SqlSessionFactory {
 2 
 3   SqlSession openSession();
 4 
 5   SqlSession openSession(boolean autoCommit);
 6   SqlSession openSession(Connection connection);
 7   SqlSession openSession(TransactionIsolationLevel level);
 8 
 9   SqlSession openSession(ExecutorType execType);
10   SqlSession openSession(ExecutorType execType, boolean autoCommit);
11   SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
12   SqlSession openSession(ExecutorType execType, Connection connection);
13 
14   Configuration getConfiguration();
15 
16 }

这么多的openSession重载方法,都是通过传播分歧的参数构造SqlSession实例,有经过设置工作是不是自动提交”autoCommit”,有设置执行器类型”ExecutorType”来布局的,还有工作的割裂级别等等。最终一个办法就告知我们得以经过SqlSessionFactory来取得Configuration对象。至于DefaultSqlSessionFactory对SqlSessionFactory的切实可行已毕,除了上述办法之外,还包罗了:openSessionFromDataSource、openSessionFromConnection、getTransactionFactoryFromEnvironment、closeTransaction。到那边大家就像依旧只停留在表面,并不曾提到相对相比较底层的代码啊,别急。大家那是刚走了一遍“SqlSession创设进度”的流程。上边大家从SqlSessionFactoryBuilder第60行return
new DefaultSqlSessionFactory(config)发轫。


 

是因为SqlSessionFactory的落实类DefaultSqlSessionFactory,源码过长,大家在中间以截取关键的代码作为解读。

DefaultSqlSessionFactory中的第1行代码实际上就非凡值得咱们考虑:final关键字。

private final Configuration configuration;

缘何会利用final关键字对Configuration对象举行修饰呢?Configuration应该是存在于MyBatis的全方位生命周期那么意味着它应有是有且仅有一个实例的,而final关键字修饰的变量字段就意味着它是不可变对象MyBatis,(《“不可变的目标”与“不可变的靶子引用”》),那也恰恰能解释表达官方User
Guide中所说的SqlSessionFactory应该是单例的。但那是布置性在前?依旧规则在前呢?假若是陈设在前,那怎么这样设计?要是是规则在前,是怎么的平整规定了这么做呢?我以为是设计在前。

第一,MyBatis认为配置文件之所以是布置文件那么就觉着着它唯有一种配备(这一个说法并不是很完善,因为大家曾经观看了那么多的构造方法就注明在一个应用程序中可以透过分裂的气象布局选取不相同的布署,事实也那样),就好比大家将一个新手机买回来过后,设置时间、日期就不再去改变,但我们兴许会出国,那几个时候将要安插选择另一个时区的日子,但是自己仍旧使用的是其一手机的设置,换句话说,你的无绳电话机不容许有八个系统安装吧。所以Configuration对象实际就是大家手机上的种类装置。而SqlSessionFactory是通过Configuration来协会SqlSession的,对Configuration的引用当然是不可变的,如果可变,那一定于你手机里岂不是可以新建一个种类设置?那不就乱套了?索性final,对象不可变。此时也就指出SqlSessionFactory是单例的了,你创设N个SqlSessionFactory,它们也是经过一个Configuration对象来社团的SqlSession实例,那还有要求有N个SqlSessionFactory了啊?鲜明没有须要,所以最好就是将SqlSessionFactory设计为单例。同样可参照《SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession作用域(Scope)和生命周期》

那才对DefaultSqlSessionFactory类第一句话进行问询读,接着就是完结SqlSessionFactory接口的8个构造方法。DefaultSqlSessionFactory并不曾直接落成那8个构造方法而是调用其它八个新的措施,这8个构造方法实际上分为两大类:一个是从数据源中获取SqlSession,一个是从Connection中拿走SqlSession(蕴含Connection参数的那五个构造函数)。

先看从数额源中获取SqlSession。

 1 private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
 2     Transaction tx = null;
 3     try {
 4       final Environment environment = configuration.getEnvironment();
 5       final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
 6       tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
 7       final Executor executor = configuration.newExecutor(tx, execType);
 8       return new DefaultSqlSession(configuration, executor, autoCommit);
 9     } catch (Exception e) {
10       closeTransaction(tx); // may have fetched a connection so lets call close()
11       throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
12     } finally {
13       ErrorContext.instance().reset();
14     }
15   }

设若没有传入ExecutorType execType, TransactionIsolationLevel level,
boolean
autoCommit那多个参数就表示行使大家Configuration对象中的配置(看来Executor、TransactionIsolationLevel、autoCommit是足以灵活配置的)。第8行创设出一个DefaultSqlSession实例,可以算计SqlSession是一个接口而DefaultSqlSession是事实上现类。对于SqlSession的创始进度,大家马上就要走到最终一步SqlSession的营造。而那也是最要害最要害最发杂的一步。

网站地图xml地图