书接前文,我们这一节来看看 SqlSession。
// SqlSession 是 mybatis 主要的接口之一,通过这个接口,
// 我们可以实现执行命令,获取 mappers,以及事务的管理
public interface SqlSession extends Closeable {
......
}
该接口的方法大致如下,有了这些方法,我们就可以进行各种操作了
上一节中,我们通过 SqlSessionFactoryBean 的 buildSqlSessionFactory 方法获得到了 DefaultSqlSessionFactory。下面我们来看下是如何通过 SqlSessionFactory 获得 SqlSession 的,我们取其中的一个例子。
@Override
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
private SqlSession openSessionFromDataSource(ExecutorType execType,
TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
// 获取Environment,Environment对象中有TransactionFactory和DataSource对象,
// 使用经典的建造者模式,通过链式调用返回创建。
final Environment environment = configuration.getEnvironment();
// 通过environment 获取transactionFactory,来创建transaction,
// 这里的autoCommit为false,即默认不自动提交。
final TransactionFactory transactionFactory =
getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// 根据不同的执行类型获取Executor执行器
final Executor executor = configuration.newExecutor(tx, execType);
// 这一步,创建了SqlSession的一个实现类DefaultSqlSession,
// 这样我们就获取到了SqlSession
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
// 异常了关闭Transaction
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
// 这个关闭很简单,就是异常了如果Transaction对象不为空,关闭,做垃圾清理
private void closeTransaction(Transaction tx) {
if (tx != null) {
try {
tx.close();
} catch (SQLException ignore) {
// Intentionally ignore. Prefer previous error.
}
}
}
好了,我么来看下 SqlSession 的实现
public class DefaultSqlSession implements SqlSession {
// 获取配置
private final Configuration configuration;
// 这个是核心,后面的执行都是他来做的。
private final Executor executor;
private final boolean autoCommit;
// 脏数据标记
private boolean dirty;
private List<Cursor<?>> cursorList;
public DefaultSqlSession(Configuration configuration,
Executor executor, boolean autoCommit) {
this.configuration = configuration;
this.executor = executor;
this.dirty = false;
this.autoCommit = autoCommit;
}
......
}
题外话,有时候我们需要最一些特殊的操作,比如我们根据业务要求要进行大量数据的批量提交,并包含一定的逻辑,这时候我们想获得 Connection,怎么做呢?
这时候我们可以通过 DefaultSqlSession 的 getConnection 方法获取,但是需要注意的是,这个时候我们要控制好事务,这一点尤为重要。
@Override
public Connection getConnection() {
try {
return executor.getTransaction().getConnection();
} catch (SQLException e) {
throw ExceptionFactory.wrapException("Error getting a new connection. Cause: " + e, e);
}
}
接下来,我们以其中一个方法为突破口,来分析一下,不失一般性,我们以 insert 为例
@Override
public int insert(String statement, Object parameter) {
return update(statement, parameter);
}
@Override
public int update(String statement, Object parameter) {
try {
// 脏数据标记
dirty = true;
// 在SqlSessionFactoty中,我们加载MapperLocation的mapper,
// 通过parse方法,解析到Configuration对象中。
// 这里再通过statement这个key从mappedStatements这个map中把它取出来
MappedStatement ms = configuration.getMappedStatement(statement);
// 通过DefaultSqlSession的executor执行相应的方法,这里的executor也是一个接口,
// 在前面openSession中我们介绍了,它根据不同的执行类型创建对应的实现
return executor.update(ms, wrapCollection(parameter));
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error updating database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
总结:
总体来说,session 的构建还是很简单的,需要注意的是,采用面向接口编程,需要根据不同的参数实例化选择不同实现对象,比较绕。通过 SqlSession 获取 session,session 里包含执行代码需要的其他对象,这些对象根据实际需要构建,最后统一进入到 executor 中去执行。下一节,我们将分析一下 Executor