对MyBatis的使用我们在最开始都已经知道可以通过xml配置文件
的方式,也可以通过Java
代码创建Configuration
对象的方式。 这两者实际上是一样,xml配置文件的方式最终也是通过解析xml配置文件创建一个Configuration对象
。可能对于很多人(我也是)来说MyBatis通常是和Spring配合使用,用了N年MyBatis也不能把MyBatis说个所以出来
回顾一下mybatis实例的创建过程
// 第一步,创建SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
// 第二步,加载配置文件
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
// 第三步,创建SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
// 第四步,创建SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
// 第五步,使用SqlSession对象执行查询,得到User对象
// 第一个参数:执行查询的StatementId
User user = sqlSession.selectOne("getUserById", 10);
// 第六步,打印结果
System.out.println(user);
// 第七步,释放资源,每一个sqlSession就是一个连接
sqlSession.close();
在创建一个SqlSession
实例时,首先需要创建一个SqlSessionFactory实例
,而又需要通过SqlSessionFactoryBuilder()build()
来创建SqlSessionFactory
先看SqlSessionFactoryBuilder
这个类,放在package org.apache.ibatis.session
public class SqlSessionFactoryBuilder {
//--------------------------------通过读取字符流(Reader)的方式构件SqlSessionFactory-----------------
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) {
// Intentionally ignore. Prefer previous error.
}
}
}
//-----------------------通过读取字符流(Reader)的方式构件SqlSessionFactory---------------
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) {
// Intentionally ignore. Prefer previous error.
}
}
}
//--------------------------------创建SqlSessionFactory--------------------------
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
}
以通过InputStream字节流
的方式来看,和它相关的一共有4个构造方法,其中第2个和第3个参数并不陌生
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties)
这相当于在告诉这两个配置项environment
,properties
是可以通过在构建SqlSessionFactory
的时候进行配置的或重新配置(此时优先级最高)。XMLConfigBuilder工具类
对配置文件进行解析成Configuration对象,
//参考上面的代码
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实例的方法。
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
创建过程的流程。
由于SqlSessionFactory的实现类DefaultSqlSessionFactory,源码过长,我们在其中以截取关键的代码作为解读。
DefaultSqlSessionFactory中的第1行代码实际上就非常值得我们思考:final关键字。
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。
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); // may have fetched a connection so lets call close()
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