一:首先
对MyBatis的使用我们在最开始都已经知道可以通过xml配置文件
的方式,也可以通过Java
代码创建Configuration
对象的方式。 这两者实际上是一样,xml配置文件的方式最终也是通过解析xml配置文件创建一个Configuration对象
。可能对于很多人(我也是)来说MyBatis通常是和Spring配合使用,用了N年MyBatis也不能把MyBatis说个所以出来
回顾一下mybatis实例的创建过程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = sqlSession.selectOne("getUserById", 10);
System.out.println(user);
sqlSession.close();
|
二:创建 SqlSessionFactory
在创建一个SqlSession
实例时,首先需要创建一个SqlSessionFactory实例
,而又需要通过SqlSessionFactoryBuilder()build()
来创建SqlSessionFactory
先看SqlSessionFactoryBuilder
这个类,放在package org.apache.ibatis.session
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| public class SqlSessionFactoryBuilder {
public SqlSessionFactory build(Reader reader) { return build(reader, null, null); }
public SqlSessionFactory build(Reader reader, String environment) { return build(reader, environment, null); }
public SqlSessionFactory build(Reader reader, Properties properties) { return build(reader, null, properties); }
public SqlSessionFactory build(Reader reader, String environment, Properties properties) { try { XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties); return build(parser.parse()); } catch (Exception e) { throw ExceptionFactory.wrapException("Error building SqlSession.", e); } finally { ErrorContext.instance().reset(); try { reader.close(); } catch (IOException e) { } } } public SqlSessionFactory build(InputStream inputStream) { return build(inputStream, null, null); }
public SqlSessionFactory build(InputStream inputStream, String environment) { return build(inputStream, environment, null); }
public SqlSessionFactory build(InputStream inputStream, Properties properties) { return build(inputStream, null, properties); }
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { try { XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); return build(parser.parse()); } catch (Exception e) { throw ExceptionFactory.wrapException("Error building SqlSession.", e); } finally { ErrorContext.instance().reset(); try { inputStream.close(); } catch (IOException e) { } } }
public SqlSessionFactory build(Configuration config) { return new DefaultSqlSessionFactory(config); } }
|
- 第1,2种方式是通过配置文件方式,第3种是通过Java代码方式。
三.是如何创建SqlSessionFactory的
以通过InputStream字节流
的方式来看,和它相关的一共有4个构造方法,其中第2个和第3个参数并不陌生
1
| public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties)
|
这相当于在告诉这两个配置项environment
,properties
是可以通过在构建SqlSessionFactory
的时候进行配置的或重新配置(此时优先级最高)。XMLConfigBuilder工具类
对配置文件进行解析成Configuration对象,
1 2 3 4 5
| public SqlSessionFactory build(){ XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); return build(parser.parse()); }
|
再调用构建出SqlSessionFactory
,构建出SqlSessionFactory
四 所以
兜兜转转,不管是配置文件还是Java代码,最后都会经过解析通过Configuration
对象产生SqlSessionFactory
,也就是最后一个build(Configuration config)
。
然而看最后一个方法的时候,返回的不是SqlSessionFactory
,而是DefaultSqlSessionFactory实例
,那是因为实际上SqlSessionFactory
是一个接口,而DefaultSqlSessionFactory
是它的实现类.暂且不管SqlSessionManager
,暂时只需知道SqlSessionFactory
有DefaultSqlSessionFactory
和SqlSessionManager
。
回顾SqlSession的创建过程,其实我们也能猜测得到SqlSessionFactory一定主要是创建SqlSession实例的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public interface SqlSessionFactory {
SqlSession openSession();
SqlSession openSession(boolean autoCommit); SqlSession openSession(Connection connection); SqlSession openSession(TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType); SqlSession openSession(ExecutorType execType, boolean autoCommit); SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level); SqlSession openSession(ExecutorType execType, Connection connection); Configuration getConfiguration(); }
|
这么多的openSession重载方法,都是通过传入不同的参数构造SqlSession实例
- 有通过设置事务是否自动提交”autoCommit”.
- 有设置执行器类型”ExecutorType”来构造的
- 还有事务的隔离级别等等
至于DefaultSqlSessionFactory对SqlSessionFactory的具体实现,除了以上方法之外,还包括了:openSessionFromDataSource、openSessionFromConnection、getTransactionFactoryFromEnvironment、closeTransaction。到这里我们似乎还是只停留在表面,并没有涉及相对比较底层的代码啊
我们这是刚走了一遍SqlSession
创建过程的流程。
五: 下面我们从return new DefaultSqlSessionFactory(config)开始。
由于SqlSessionFactory的实现类DefaultSqlSessionFactory,源码过长,我们在其中以截取关键的代码作为解读。
DefaultSqlSessionFactory中的第1行代码实际上就非常值得我们思考:final关键字。
1
| private final Configuration configuration;
|
为什么会使用final关键字对Configuration对象进行修饰呢?
- Configuration应该是存在于MyBatis的整个生命周期那么意味着它应该是有且仅有一个实例的,而final关键字修饰的变量字段就代表它是不可变对象
- 这也恰好能解释说明官方所说的
SqlSessionFactory
应该是单例
的。
首先,MyBatis认为配置文件之所以是配置文件,那么就以为着它只有一种配置,就好比我们将一个新手机买回来过后,设置时间、日期就不再去更改,但我们可能会出国,这个时候就要配置选用另一个时区的时间,不过我还是使用的是这个手机的设置,换句话说,你的手机不可能有两个系统设置吧。
所以Configuration对象实际上就是我们手机上的系统设置。而SqlSessionFactory是通过Configuration来构造SqlSession的,对Configuration的引用当然是不可变的,如果可变,那相当于你手机里岂不是可以新建一个系统设置?那不就乱套了?索性final,对象不可变。
此时也就建议SqlSessionFactory是单例的了,你构建N个SqlSessionFactory,它们也是通过一个Configuration对象来构造的SqlSession实例,那还有必要有N个SqlSessionFactory了吗?显然没有必要,所以最好就是将SqlSessionFactory设计为单例。
这才对DefaultSqlSessionFactory类第一句话进行了解读,接着就是实现SqlSessionFactory接口的8个构造方法。DefaultSqlSessionFactory
并没有直接实现这8个构造方法而是调用另外两个新的方法.
这8个构造方法实际上分为两大类:
- 一个是从数据源中获取SqlSession.
- 一个是从Connection中获取SqlSession(包含Connection参数的那两个构造函数)。
先看从数据源中获取SqlSession。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; try { final Environment environment = configuration.getEnvironment(); final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); final Executor executor = configuration.newExecutor(tx, execType); return new DefaultSqlSession(configuration, executor, autoCommit); } catch (Exception e) { closeTransaction(tx); throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
|
如果没有传入ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit这三个参数就代表使用我们Configuration对象中的配置(看来Executor、TransactionIsolationLevel、autoCommit是可以灵活配置的)。第8行创建出一个DefaultSqlSession实例,可以猜测SqlSession是一个接口而DefaultSqlSession是其实现类。对于SqlSession的创建过程,我们马上就要走到最后一步SqlSession的构建。而这也是最关键最重要最发杂的一步
参考:https://www.cnblogs.com/yulinfeng/p/6063974.html