mybatis源码分析3 - sqlSession的创建

1 引言和主要类

初始化mybatis,也就是创建完单例SqlSessionFactory后,就进入到了mybatis的运行阶段。mybatis每次的运行都是通过SqlSession对象来进行,它是运行时的核心。不同于SqlSessionFactory,SqlSession不是线程安全的,故一般建议放在局部作用域中定义, 且使用完后close掉。我们使用mybatis创建SqlSession十分简单,代码如下

// 读入xml配置文件
String resource = "main/resources/SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// 创建SqlSessionFactory,初始化mybatis的关键所在,上一节分析过了
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 利用SqlSessionFactory来创建SqlSession,这一节重点分析
SqlSession session = sessionFactory.openSession();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

创建SqlSession十分简单,调用SqlSessionFactory的openSession()方法即可。但这个方法背后其实相当复杂,mybatis为我们做了很多幕后工作。我们先介绍下openSession()过程中要用到的主要类。

  1. DefaultSqlSessionFactory:SqlSessionFactory的默认实现类,上一节我们讲到过。
  2. Transaction:数据库事务,有JdbcTransaction和ManagedTransaction两个实现
  3. Executor:调度器,sqlSession的select update等方法的真正实现,mybatis运行的核心。
  4. DefaultSqlSession:sqlSession的默认实现类,其方法几乎均由Executor代理实现。

2 流程分析

我们从DefaultSqlSessionFactory这个默认工厂类的openSession()方法说起。

// SqlSessionFactory单例创建并开启一个sqlSession实例。sqlSession线程不安全,一般存放在局部作用域中,用完close即可。
public SqlSession openSession() {
  return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}

// 开启sqlSession
  private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      // 获取configuration的environment,它代表了运行的数据库环境,
      // 由配置文件中的environments节点的environment子节点生成,创建SqlSessionFactory时指定其id,默认为default
      final Environment environment = configuration.getEnvironment();

      // environment实例中取出transactionFactory成员变量,然后实例化它。
      // JdbcTransactionFactory创建JdbcTransaction,使用JDBC代理管理commit等事务
      // ManagedTransactionFactory创建ManagedTransaction,自身不对事务进行处理,完全交给容器,如Spring
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);

      // 由事务transaction创建调度器Executor,SqlSession的几乎所有方法都是通过代理模式由Executor真正实现
      // Executor代表调度器,由他来调度StatementHandler ParameterHandler ResultSetHandler。四者合称SqlSession四大组件
      // ExecutorType在XML配置文件的settings节点中设置(defaultExecutorType), 可以取SIMPLE REUSE BATCH,默认为SIMPLE
      // SIMPLE表示简易执行器,REUSE为一种执行器重用预处理语句,BATCH则为批量专用的执行器。
      final Executor executor = configuration.newExecutor(tx, execType);

      // 构造SqlSession实例,mybatis默认的实现类为DefaultSqlSession
      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();
    }
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

mybatis先由初始化阶段创建在configuration中的environment变量来创建数据库事务,然后根据XML配置文件的settings节点中设置的defaultExecutorType来创建调度器Executor,最后就可以创建得到DefaultSqlSession实例了。下面我们来分析事务的创建

2.1 数据库事务Transaction的创建

Transaction有两个实现类,即JdbcTransaction和ManagedTransaction,他们分别由JdbcTransactionFactory和ManagedTransactionFactory工厂类来创建实例。JdbcTransaction本地事务由数据库本身来管理事务,其getConnection()从DataSource中得到, commit() rollback() close() 则由java.sql.Connection实例代理实现。

ManagedTransaction由容器来管理事务,其getConnection()从DataSource中得到,commit() rollback()均为空实现,不做任何事情,完全托管给容器。

下面以JDBCTransaction为例来看Transaction事务的创建

  // 工厂类来创建事务实例
  // @Param DataSource: 数据库源
  // @Param level: 事务隔离级别,定义并发事务的处理方式。如A读时,B在写。
  // @param autoCommit: 是否自动提交
  public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
    return new JdbcTransaction(ds, level, autoCommit);
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

2.2 调度器Executor的创建

Executor是SqlSession的核心,其select update commit rollback close等方法均由Executor代理实现。Executor代表调度器,由他来调度StatementHandler ParameterHandler ResultSetHandler。四者合称mybatis运行时四大组件。四大组件均可由用户通过插件注入,也都有默认实现。下面看创建Executor的源码

// 创建sqlsession执行器Executor
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
  // executorType通过settings节点中的defaultExecutorType来设置,没有设置则默认为SIMPLE
  executorType = executorType == null ? defaultExecutorType : executorType;
  executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
  Executor executor;

  // 根据executorType分别创建BatchExecutor ReuseExecutor SimpleExecutor
  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);
  }

  // 如果打开了缓存,使用CachingExecutor包装下之前创建的executor,简单理解就是为executor添加了cache功能
  if (cacheEnabled) {
    executor = new CachingExecutor(executor);
  }

  // 将执行器executor设置到plugins节点中设置的所有插件中,作为插件的目标执行器。
  executor = (Executor) interceptorChain.pluginAll(executor);
  return executor;
}

public Object pluginAll(Object target) {
    // 遍历所有插件,将传入的target,作为插件的目标执行器。插件通过配置XML文件的plugins节点设置。
    for (Interceptor interceptor : interceptors) {
      target = interceptor.plugin(target);
    }
    return target;
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

Executor根据executorType分别创建BatchExecutor ReuseExecutor SimpleExecutor三个实现类,他们的基类为BaseExecutor。下一节我们分析selectList方法时,以SimpleExecutor这个默认的调度器为例分析。

2.3 SqlSession实例的创建

SqlSession的默认实现为DefaultSqlSession,其创建十分简单,构造方法如下

public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
  this.configuration = configuration;
  this.executor = executor;
  this.dirty = false;
  this.autoCommit = autoCommit;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

其中executor成员最为关键,DefaultSqlSession的大部分方法均是通过它来代理实现。比如select update方法。而delete和insert方法均调用update方法来实现。

// select方法通过executor实现,典型的代理模式
// selectOne selectList均调用它实现
public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
  try {
    MappedStatement ms = configuration.getMappedStatement(statement);
    // 利用调度器Executor代理实现
    executor.query(ms, wrapCollection(parameter), rowBounds, handler);
  } catch (Exception e) {
    throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
  } finally {
    ErrorContext.instance().reset();
  }
}

// update方法通过executor实现,典型的代理模式
// insert update等DML操作均调用它实现 
public int update(String statement, Object parameter) {
    try {
      dirty = true;
      MappedStatement ms = configuration.getMappedStatement(statement);
      // 利用调度器Executor代理实现
      return executor.update(ms, wrapCollection(parameter));
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

public int insert(String statement, Object parameter) {
    return update(statement, parameter);
}

public int delete(String statement, Object parameter) {
    return update(statement, parameter);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

3 总结

构建SqlSession的核心是创建调度器Executor,它是mybatis运行的核心。通过调度StatementHandler ParameterHandler ResultSetHandler来完成各种操作。四者合称SqlSession四大组件。后面一节我们以sqlSession.selectOne()方法为例来分析mybatis的CRUD操作的背后原理。

阅读更多
个人分类: Mybatis
上一篇mybatis源码分析2 - SqlSessionFactory的创建
下一篇mybatis源码分析4 - sqlSession读写数据库完全解析
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭