• Mybatis详解(二) sqlsession的创建过程


    我们处于的位置

    我们要清楚现在的情况.

    现在我们已经调用了SqlSessionFactoryBuilder的build方法生成了SqlSessionFactory 对象.

    但是如标题所说,要想生成sqlsession还要另一步SqlSessionFactory 调用openSession()方法生成sqlsession;

    这就要从上一部分代码讲起

    上文讲到

    img

    我们创建的实际上是一个叫做DefaultSqlSessionFactory的类,实际上他是一个SqlSessionFactory接口(没错,这玩应是接口)的实现类.

    既然sqlsession是由opensession产生的,那我们就先看这个方法.

    img

    说一嘴题外话就是自动提交也是在这个部分设置的,下面是如果你设置了autocommit的情况.

    public SqlSession openSession(boolean autoCommit) {
      //this.configuration.getDefaultExecutorType()值为 ExecutorType.SIMPLE;
        return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, autoCommit);
    }
    

    参数中 configuration 获取了默认的执行器 “SIMPLE”.


    DefaultSqlSessionFactory

    调用了一个同一个类中openSessionFromDataSource方法.

    在这个类中是如下执行流程

    所要知道的一部分知识.

    environments运行环境

    MyBatis 核心配置综述之 Configuration详解

    其实就是数据库连接那个部分.

    private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
      Transaction tx = null;
      try {
        //从configuration对象中得到环境配置的对象
        final Environment environment = configuration.getEnvironment();
        //这个对象被用来创建一个事务工厂->一号分支
        final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      //事务工厂创建一个事务对象->二号分支
        tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
        //而 configurationye 则会根据事务对象和执行器类型创建一个执行器。
        ->三号分支
        final Executor executor = configuration.newExecutor(tx, execType);
        //返回一个默认的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();
      }
    }
    

    现在我们要从一号分支开始


    一号分支

    final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);

    这个代码如下:

    我们发现有两种可能性.

    如果传进来的值没有设置标签那么他会执行ManagedTransactionFactory()而反之则会执行environment.getTransactionFactory()

    这两者产生的对象都实现了 TransactionFactory接口.

    这里ManagedTransactionFactory()是没有标签时生成的对象.其核心就是一句

    private boolean closeConnection = true;的属性.

    我们不必过于关注这个部分.

    private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
      if (environment == null || environment.getTransactionFactory() == null) {
       //如果没有目标标签
        return new ManagedTransactionFactory();
      }
      //如果有目标标签
      return environment.getTransactionFactory();
    }
    

    environment.getTransactionFactory()产生的东西才是重点.

    调用环境对象的getTransactionFactory方法,该方法和我们配置的一样返回了一个 JdbcTransactionFactory,而实际上,TransactionFactory 只有2个实现类,一个是 ManagedTransactionFactory (没有标签时返回的),一个是 JdbcTransactionFactory(有标签时返回的)。

    至此一号分支结束,从此看来,一号分支实际上是将environment对象包装成一个工厂对象.

    请返回一号分支之前部分继续.


    分支二

    tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);

    我们回到openSessionFromDataSource方法,获取了 JdbcTransactionFactory 后,调用 JdbcTransactionFactorynewTransaction方法创建一个事务对象.

    当然因为代码中采用TransactionFactory 接口作为声明对象.所以无论分之一传回来的是哪个工厂对象.在分支二中都可以执行.

    我们先讲 JdbcTransactionFactory的情况.

    分支二中调用的是这个newTransaction方法.(还有一个重载的)

    public Transaction newTransaction(Connection conn) {
      return new JdbcTransaction(conn);
    }
    

    这就到了另一个类中JdbcTransaction中.

    JdbcTransaction

    我删掉其中的实现代码

    public class JdbcTransaction implements Transaction {
    
      private static final Log log = LogFactory.getLog(JdbcTransaction.class);
    
      protected Connection connection;
      protected DataSource dataSource;
      protected TransactionIsolationLevel level;
      protected boolean autoCommmit;
    
      public JdbcTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) {
        dataSource = ds;
        level = desiredLevel;
        autoCommmit = desiredAutoCommit;
      }
    
      public JdbcTransaction(Connection connection) {
        this.connection = connection;
      }
    
      public Connection getConnection() throws SQLException {
      
      }
    
      public void commit() throws SQLException {
       
      }
    
      public void rollback() throws SQLException {
        
      }
    
      public void close() throws SQLException {
        
      }
    
      protected void setDesiredAutoCommit(boolean desiredAutoCommit) {
       
      }
    
      protected void resetAutoCommit() {
        
      }
    
      protected void openConnection() throws SQLException {
       
      }
    
    }
    

    其实只要看了代码你就会发现,这个类中的方法,和我们调用session的方法高度重合.比如commit,rollback等等.而且还能设置事务的隔离级别

    img

    所以我们有理由认为,这个类就是对jdbc连接部分的封装.

    总结

    至此分支二结束,我们对于标签在xml中的存在情况,会返回两种截然不同对象.一种是作为jdbc连接封装的JdbcTransaction对象.另一个则是ManagedTransaction对象(这个没讲....)


    分支三

    第三分支我们将回到Configuration对象.

    Configuration对象

    法此时已经创建好事务对象。接下来将事务对象执行器作为参数执行 configuration 的 newExecutor 方法来获取一个 执行器类。我们看看该方法实现:

    img

    首先第一句将判断是否传入了一个excutorType参数,如果没有就用默认的参数.

    也就是 ExecutorType.SIMPLE(前面出现过),然后根据执行的类型来创建不同的执行器,默认是 SimpleExecutor 执行器.

    Mybatis有三种基本的Executor执行器:

    • SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。

    • ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map<String, Statement>内,供下一次使用。简言之,就是重复使用Statement对象。

    • BatchExecutor:执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理。与JDBC批处理相同。

    作用范围:Executor的这些特点,都严格限制在SqlSession生命周期范围内。

    然后我们看下一句部分

    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);
    }
    

    我们先将 BatchExecutor执行器.

    img

    该类包装了事务对象,延迟加载的队列,本地缓存,永久缓存,配置对象,还包装了自己。

    传入的两个参数分别为存储了配置信息的Configuration对象,以及封装了jdbc中连接数据库部分代码的JdbcTransaction对象.

    回到 newExecutor 方法,判断是否使用缓存,默认是true, 则将刚刚的执行器包装到新的 CachingExecutor 缓存执行器中。最后将执行器添加到所有的拦截器中(如果配置了话),我们这里没有配置。

    img

    到此分支三结束

    总结:

    我们从用从分支二得到的对象,构建了一个执行器.这个执行对象,包括事务对象(即连jdbc连接部分的控制封装.JdbcTransaction),延迟加载的队列,本地缓存,永久缓存,配置对象(Configuration),还包装了自己。


    四号分支

    我们已经有了执行器,此时创建 DefaultSqlSession 对象,携带 configuration, executor, autoCommit 三个参数,该构造器就是简单的赋值过程。我们有必要看看该类的结构:

    img

    该类包含了常用的所有方法,包括事务方法,可以说,该类封装了执行器和事务类。而执行器才是具体的执行工作人员。

    至此,我们已经完成了 SqlSession 的创建过程。

  • 相关阅读:
    新书推荐——《How We Test Software at Microsoft》
    [ZZ]采访与书摘:使用C#进行基于模型的软件测试和分析
    Adding Different Types of Data Sources to a Web Test
    [ZZ]为什么传统的自动化测试工具会扼杀敏捷?
    很久没有这么High了
    留图以纪念这次地震
    white——Automate windows applications
    WatiN、Wax、WatiN Test Recorder开源自动化测试框架
    绝版的T61普屏
    有感于公司搬家
  • 原文地址:https://www.cnblogs.com/yanzezhong/p/13097082.html
Copyright © 2020-2023  润新知