Mybatis 3.5.5的版本
本系列的前三篇主要讲述了xml文件解析的过程和其中主要的类:
在我们使用Mybatis的过程中,这只是完成了开始的准备工作,接下来就是根据需要执行我们定义的SQL。那么在这一过程中,必须要做的就是拿到SqlSession,那么我们这篇文章就来梳理SqlSession的基本情况。
一、SqlSession接口
public interface SqlSession extends Closeable {
<T> T selectOne(String statement);
<E> List<E> selectList(String statement);
<K, V> Map<K, V> selectMap(String statement, String mapKey);
<T> Cursor<T> selectCursor(String statement);
void select(String statement, Object parameter, ResultHandler handler);
int insert(String statement, Object parameter);
int update(String statement, Object parameter);
int delete(String statement, Object parameter);
void commit(boolean force);
void rollback(boolean force);
List<BatchResult> flushStatements();
void clearCache();
<T> T getMapper(Class<T> type);
Connection getConnection();
Configuration getConfiguration();
}
在这里摘录的代码只是其中的一部分,在这个类中对于同一中方法定义了很多不同参数的重载方法,在这里我们只展示其中一个。我把这些方法主要分为了六类,以空行分割。第一类最多,是与我们对数据库的操作息息相关的,即select | insert | update | delete操作的方法。第二类是对数据库事务的抽象,体现出来就是commit和rollback方法。第三类是刷新批处理语句。第四类是缓存相关。第五类是与Mybatis提供的mapper代理使用方式相关的方法。最后一类是get类方法,获取某对象或属性。
上图为SqlSession接口的实现关系,我们看到有两个实现类,分别是Deafault和Manager。我们调用的openSession方法之后会返回给我们一个DefaultSqlSession的对象,下文中会详细分析。
二、openSession的具体过程
1.我们通过在SqlSessionFactory中调用openSession方法来获取一个SqlSession。我们知道SqlSessionFactory只是一个接口,那么具体我们用的是哪一个实现类呢?答案是DefaultSqlSessionFactory,我们通过这段代码可以确认:
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);
}
这段代码来自于SqlSessionFactoryBuilder类,我们可以看到,最终是创建了DefaultSqlSessionFactory并返回。
2.那么我们再看一下在DefaultSqlSessionFactory类中,openSession方法是如何运作的呢:
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
//获取Environment对象
final Environment environment = configuration.getEnvironment();
//得到TransactionFactory
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
//生产一个Transaction
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//生成一个Executor
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();
}
}
那么直接看到最后面返回的是一个DefaultSqlSession对象,它的构造方法需要Configuration对象(已有)、Executor对象和boolean autoCommit。默认的自动提交为false,从openSession方法中可以看出。最后剩下的就是要构建一个Excutor了,我们看一下openSessionFromDataSource的前面几句,分别是:获取Environment对象,根据Environment得到TransactionFactory,使用工厂生产一个Transaction,最后就是new一个Executor交给DefaultSqlSession的构造方法。
看这些方法名就大概知道是干什么用的了,但是具体是什么过程我们还需要从源码中阅读,接下来我们分别看一下其中用到的各个方法。
三、TransactionFactory+Transaction类
第一个获取Environment对象就不解释了,它是在xml解析过程中得到的。
首先我们会不清楚的一个地方是这个TransactionFactory+Transaction是怎么得到的,有什么属性,我们现在来看看。
首先看看TransactionFactory工厂的获得,见代码:
//DefualtSqlSessionFactory类中的方法
private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
if (environment == null || environment.getTransactionFactory() == null) {
return new ManagedTransactionFactory();
}
return environment.getTransactionFactory();
}
也就是说我们传递进去的Environment为空,或者其中的属性TransactionFactory为空,会创建一个ManagedTransactionFactory。如果不是这样的话,会将Environment中的TransactionFactory取出来返回,那这回是什么类型的呢,我们需要回到Environment创建的时候进行探究一下。
//这段代码存在于XmlConfigBuilder类中
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
if (environment == null) {
environment = context.getStringAttribute("default");
}
for (XNode child : context.getChildren()) {
String id = child.getStringAttribute("id");
//if语句判断的是environments标签的default属性是否与environment的id一致,一致的话才会进行解析
if (isSpecifiedEnvironment(id)) {
//通过这个语句我们就可以得到一个TransactionFactory,最后放到configuration中
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
DataSource dataSource = dsFactory.getDataSource();
Environment.Builder environmentBuilder = new Environment.Builder(id)
.transactionFactory(txFactory)
.dataSource(dataSource);
configuration.setEnvironment(environmentBuilder.build());
}
}
}
}
那么我们可以看到,其中决定TransactionFactory类型的就是transactionManagerElement这个方法,接着看代码:
private TransactionFactory transactionManagerElement(XNode context) throws Exception {
if (context != null) {
String type = context.getStringAttribute("type");
Properties props = context.getChildrenAsProperties();
TransactionFactory factory = (TransactionFactory) resolveClass(type).getDeclaredConstructor().newInstance();
factory.setProperties(props);
return factory;
}
throw new BuilderException("Environment declaration requires a TransactionFactory.");
}
我们看到其实就是根据我们配置的“transactionManager”这个标签通过反射得到TransactionFactory的,我们配置为“JDBC”(大小写无所谓)时,会生成一个叫做JdbcTransactionFactory的对象,那么它的功能就是创建JdbcTransaction对象,我们看一下它的方法:
public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
return new JdbcTransaction(ds, level, autoCommit);
}
相应的,如果我们最终获得了ManagedTransactionFactory工厂对象,那么生产出来的就是ManagedTransaction对象。
四、Executor的创建过程
//该方法来自于Configuration类
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor 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);
}
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
我们通过创建Executor的代码可以看出,创建一个什么样的Executor依据是type,那么这里面的defaultExecutorType其实也是被初始化为Simple类型的;另外一个依据就是cacheEnabled即是否使用缓存,说明了缓存与Executor息息相关。最后一句代码是在所有的拦截器中都插入Executor对象,之后我们详解Executor会进行分析。