Mybatis源码笔记(二)、SqlSession

书接前文,我们这一节来看看 SqlSession。


// SqlSession 是 mybatis 主要的接口之一,通过这个接口, // 我们可以实现执行命令,获取 mappers,以及事务的管理 public interface SqlSession extends Closeable { ...... }

该接口的方法大致如下,有了这些方法,我们就可以进行各种操作了
sqlSession
上一节中,我们通过 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

发表评论

邮箱地址不会被公开。 必填项已用*标注