Mybatis源码-SqlSession获取
前言
已知在Mybatis
的使用中,使用Mybatis
时会先读取配置文件mybatis-config.xml
为字符流或者字节流,然后通过SqlSessionFactoryBuilder
基于配置文件的字符流或字节流来构建SqlSessionFactory
,然后再通过SqlSessionFactory
的openSession()
方法获取SqlSession
,示例代码如下所示。
public static void main(String[] args) throws Exception {
String resource = "mybatis-config.xml";
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
.build(Resources.getResourceAsStream(resource));
SqlSession sqlSession = sqlSessionFactory.openSession();
}
上述示例代码中的
SqlSessionFactory
实际为DefaultSqlSessionFactory
,本篇文章将对DefaultSqlSessionFactory
获取SqlSession
的流程进行学习。正文
DefaultSqlSessionFactory
的openSession()
方法如下所示。public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
【Mybatis源码-SqlSession获取】继续看
openSessionFromDataSource()
方法的实现,如下所示。private SqlSession openSessionFromDataSource(ExecutorType execType,
TransactionIsolationLevel level,
boolean autoCommit) {
Transaction tx = null;
try {
//Environment中包含有事务工厂和数据源
final Environment environment = configuration.getEnvironment();
//获取事务工厂
//如果配置了JDBC事务,则获取JDBC事务工厂,否则获取MANAGED事务工厂
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
//创建事务
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//创建执行器
final Executor executor = configuration.newExecutor(tx, execType);
//构建DefaultSqlSession并返回
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx);
throw ExceptionFactory.wrapException("Error opening session.Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
openSessionFromDataSource()
方法会先从Configuration
中将Environment
获取出来,Environment
中包含有事务工厂和数据源,Environment
是在Mybatis
配置文件中进行配置并会在加载Mybatis
配置文件时被添加到Configuration
中。Mybatis
的配置文件中可以配置两种事务工厂,为JDBC
事务工厂和MANAGED
事务工厂,它们分别可以创建JDBC
事务和MANAGED
事务,类图如下所示。文章图片
获取事务工厂的
getTransactionFactoryFromEnvironment()
方法如下所示。private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
if (environment == null || environment.getTransactionFactory() == null) {
//配置了事务工厂则使用配置的事务工厂
//Mybatis可以配置JDBC和MANAGED这两种事务工厂
return new ManagedTransactionFactory();
}
//没做配置的情况下使用MANAGED事务工厂
return environment.getTransactionFactory();
}
Mybatis
中如果使用JDBC
事务,则事务的管理由java.sql.Connection
完成,如果使用MANAGED
事务,则Mybatis
会将事务的管理交由WEB
容器(Tomcat
,JBoss
等)来完成。以commit()
和rollback()
方法为例,看下JdbcTransaction
和ManagedTransaction
对这两个方法的实现,如下所示。public class JdbcTransaction implements Transaction {......@Override
public void commit() throws SQLException {
if (connection != null && !connection.getAutoCommit()) {
if (log.isDebugEnabled()) {
log.debug("Committing JDBC Connection [" + connection + "]");
}
connection.commit();
}
}@Override
public void rollback() throws SQLException {
if (connection != null && !connection.getAutoCommit()) {
if (log.isDebugEnabled()) {
log.debug("Rolling back JDBC Connection [" + connection + "]");
}
connection.rollback();
}
}......}public class ManagedTransaction implements Transaction {......@Override
public void commit() throws SQLException {
//Does nothing
}@Override
public void rollback() throws SQLException {
//Does nothing
}......}
回到
openSessionFromDataSource()
方法,创建好事务后,会构建执行器Executor
,看一下newExecutor(Transaction transaction, ExecutorType executorType)
方法的实现,如下所示。public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
//根据ExecutorType的枚举值创建对应类型的Executor
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
//如果Mybatis配置文件中开启了二级缓存
if (cacheEnabled) {
//创建CachingExecutor作为Executor的装饰器,为Executor增加二级缓存功能
executor = new CachingExecutor(executor);
}
//将插件逻辑添加到Executor中
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
在上面构建执行器
Executor
的方法中,主要是做了三件事情,解释如下。第一件事情,根据
ExecutorType
的枚举值创建对应类型的Executor
,ExecutorType
枚举值如下所示。public enum ExecutorType {
SIMPLE, REUSE, BATCH
}
Mybatis
提供了三种类型的Executor
,分别为BatchExecutor
,ReuseExecutor
和SimpleExecutor
,类图如下所示。文章图片
在
BaseExecutor
中定义了四个抽象方法,如下所示。protected abstract int doUpdate(MappedStatement ms, Object parameter) throws SQLException;
protected abstract List doFlushStatements(boolean isRollback) throws SQLException;
protected abstract List doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
throws SQLException;
protected abstract Cursor doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql)
throws SQLException;
BaseExecutor
提供的方法中会调用这四个抽象方法,同时这四个抽象方法需要由BatchExecutor
,ReuseExecutor
和SimpleExecutor
根据各自的功能进行实现,所以Executor
的设计使用了模板设计模式。第二件事情,如果在
Mybatis
配置文件中开启了二级缓存,则为Executor
创建装饰器CachingExecutor
,以增加对二级缓存功能的支持。CachingExecutor
与Executor
的关系如下所示。文章图片
CachingExecutor
实现了Executor
接口,同时CachingExecutor
持有Executor
的引用,这是装饰器模式的应用。关于Mybatis
中的缓存,会在后续的文章中进行介绍。第三件事情,如果配置了插件,则将插件的逻辑添加到
Executor
中。关于Mybatis
中的插件,会在后续的文章中进行介绍。回到
openSessionFromDataSource()
方法,创建好Executor
后,就会调用DefaultSqlSession
的构造方法创建一个DefaultSqlSession
并返回,在DefaultSqlSession
的构造方法中只是进行了简单的赋值,如下所示。public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
this.configuration = configuration;
this.executor = executor;
this.dirty = false;
this.autoCommit = autoCommit;
}
至此,
SqlSession
就创建出来了。总结
SqlSession
的获取是通过SqlSessionFactory
的openSession()
方法,SqlSession
的创建过程主要先是创建事务管理器,然后基于事务管理器创建执行器Executor
,最后基于Executor
创建SqlSession
。通常,SqlSessionFactory
为DefaultSqlSessionFactory
,创建出来的SqlSession
为DefaultSqlSession
。推荐阅读
- 关于QueryWrapper|关于QueryWrapper,实现MybatisPlus多表关联查询方式
- mybatisplus如何在xml的连表查询中使用queryWrapper
- mybatisplus|mybatisplus where QueryWrapper加括号嵌套查询方式
- MybatisPlus|MybatisPlus LambdaQueryWrapper使用int默认值的坑及解决
- MybatisPlus使用queryWrapper如何实现复杂查询
- Android事件传递源码分析
- Quartz|Quartz 源码解析(四) —— QuartzScheduler和Listener事件监听
- [源码解析]|[源码解析] NVIDIA HugeCTR,GPU版本参数服务器---(3)
- ffmpeg源码分析01(结构体)
- Java程序员阅读源码的小技巧,原来大牛都是这样读的,赶紧看看!