Mybatis源码学习(23)-SqlSession对象的创建过程

一、前言

  在前面的内容里,我们已经分析了Mybatis中的基础支撑层的各个模块,后续也分析了在Mybatis开始工作的时候,如何加载并解析相关配置文件的内容。详细可以参考《Mybatis整体架构概述》《Mybatis启动时的初始化过程》等内容。本节内容,我们开始来分析创建SqlSession对象的过程。

二、SqlSession对象简介

  SqlSession类是Mybatis接口层最核心的接口,该接口主要用来执行命令,获取映射器和进行管理事务管理等。SqlSession实例是由 SqlSessionFactory 实例创建的。SqlSessionFactory 对象包含创建 SqlSession 实例的所有方法。而 SqlSessionFactory 本身是由 SqlSessionFactoryBuilder 创建的,它可以从 XML、注解或手动配置 Java 代码来创建 SqlSessionFactory。在SqlSession类中,包含了众多的方法,大概可以分为几类:

  • 执行语句方法
    这些方法被用来执行定义在 SQL 映射的 XML 文件中的 SELECT、INSERT、UPDATE 和 DELETE 语句。它们都会自行解释,每一句都使用语句的 ID 属性和参数对象,参数可以是原生类型(自动装箱或包装类)、JavaBean、POJO 或 Map。

    <T> T selectOne(String statement, Object parameter)
    <E> List<E> selectList(String statement, Object parameter)
    <T> Cursor<T> selectCursor(String statement, Object parameter)
    <K,V> Map<K,V> selectMap(String statement, Object parameter, String mapKey)
    int insert(String statement, Object parameter)
    int update(String statement, Object parameter)
    int delete(String statement, Object parameter)
    
  • 批量立即更新方法
    有一个方法可以刷新(执行)存储在 JDBC 驱动类中的批量更新语句。当你将 ExecutorType.BATCH 作为 ExecutorType 使用时可以采用此方法。

    List<BatchResult> flushStatements()
    
  • 事务控制方法
    控制事务作用域有四个方法。当然,如果你已经设置了自动提交或你正在使用外部事务管理器,这就没有任何效果了。然而,如果你正在使用 JDBC 事务管理器,由Connection 实例来控制,那么这四个方法就会派上用场:

    void commit()
    void commit(boolean force)
    void rollback()
    void rollback(boolean force)
    
  • 本地缓存
    Mybatis 使用到了两种缓存:本地缓存(local cache)和二级缓存(second level cache)。每当一个新 session 被创建,MyBatis 就会创建一个与之相关联的本地缓存。任何在 session 执行过的查询语句本身都会被保存在本地缓存中,那么,相同的查询语句和相同的参数所产生的更改就不会二度影响数据库了。本地缓存会被增删改、提交事务、关闭事务以及关闭 session 所清空。默认情况下,本地缓存数据可在整个 session 的周期内使用,这一缓存需要被用来解决循环引用错误和加快重复嵌套查询的速度,所以它可以不被禁用掉,但是你可以设置 localCacheScope=STATEMENT 表示缓存仅在语句执行时有效。

    void clearCache()
    
  • SqlSession 关闭
    用来确保SqlSession对象被关闭。

    void close()
    
  • 其他方法
    下面三个方法分别是用来:获取Configuration配置实例;获取映射器对象;获取当前的数据库连接对象。

    Configuration getConfiguration()
    
    <T> T getMapper(Class<T> type)
    
    Connection getConnection();
    
三、创建SqlSession对象的方法

  创建SqlSession对象的方法,是由SqlSessionFactory接口的openSession()方法或其重载方法实现。

  SqlSessionFactory接口的相关方法如下:

//SqlSessionFactory.java
 SqlSession openSession(boolean autoCommit);
  SqlSession openSession(Connection connection);
  SqlSession openSession(TransactionIsolationLevel level);

  SqlSession openSession(ExecutorType execType);
  SqlSession openSession(ExecutorType execType, boolean autoCommit);
  SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
  SqlSession openSession(ExecutorType execType, Connection connection);

  在Mybatis中,实现SqlSessionFactory接口的类有两个,分别是:DefaultSqlSessionFactory、SqlSessionManager。这里主要分析DefaultSqlSessionFactory类提供的创建SqlSession对象的实现方法。在openSession()方法中,底层是通过openSessionFromDataSource()方法实现,核心逻辑如下所示:

//DefaultSqlSessionFactory.java
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      final Environment environment = configuration.getEnvironment();
      //根据mybatis中配置的环境,获取事务工厂,如果没有配置环境,就得到代理模式事务工厂ManagedTransactionFactory
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      //根据事务工厂创建事务实例对象
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      //根据事务实例对象,还有执行器类型创建执行器
      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();
    }
  }

上述方法的逻辑如下:

  1. 获取环境变量Environment,已经在解析配置文件的时候,存储到了全局唯一变量configuration中,所以直接通过get方法获取。
  2. 根据环境变量,创建事务。首先是创建事务工厂类TransactionFactory,然后在使用工厂类的newTransaction()方法创建对应的事务实例。
  3. 创建执行器Executor对象,该逻辑在configuration的newExecutor()方法中完成。
  4. 最后,创建SqlSession实例对象,默认使用DefaultSqlSession实现类。
四、getTransactionFactoryFromEnvironment()方法

  根据环境Environment实例创建事务工厂实例TransactionFactory。如果环境Environment实例为空或者环境实例不包含事务工厂实例,就创建托管模式的事务工厂实例ManagedTransactionFactory,否则,返回环境中的实例。

//DefaultSqlSessionFactory.java
 private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
    if (environment == null  || environment.getTransactionFactory() == null) {
      return new ManagedTransactionFactory();
    }
    return environment.getTransactionFactory();
  }

  根据TransactionFactory工厂实例的newTransaction()方法,创建对应的Transaction实例,该过程其实就是根据参数创建一个相应类型的Transaction实例实例,下面以JdbcTransactionFactory工厂类为例,newTransaction()方法源码如下:

//JdbcTransactionFactory.java
  @Override
  public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
    return new JdbcTransaction(ds, level, autoCommit);
  }
五、newExecutor()方法,创建执行器Executor

  newExecutor()方法在Configuration类中,用来创建SQL操作的执行器。然后根据ExecutorType类型创建对应的执行器,可能是BatchExecutor、ReuseExecutor或SimpleExecutor中的一个,然后在判断是否开启缓存机制,如果开启缓存,则再包装一层CachingExecutor,最后,通过InterceptorChain类的pluginAll()方法,动态把定义的所有插件,添加到了执行器executor上。关于插件相关内容请参考《Mybatis拦截器实现分页插件》

//Configuration.java
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    //个人认为:该句代码有点儿多余,因为defaultExecutorType为空时,下面的判断语句还是默认会执行创建SimpleExecutor执行的代码
    //可能是考虑代码的可阅读性和代码逻辑的完整性(猜测)
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    if (ExecutorType.BATCH == executorType) {//根据执行器类型创建BatchExecutor
      executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {//根据执行器类型创建ReuseExecutor
      executor = new ReuseExecutor(this, transaction);
    } else {//根据执行器类型创建SimpleExecutor
      executor = new SimpleExecutor(this, transaction);
    }
    if (cacheEnabled) {//如果开启全局缓存配置,则使用CachingExecutor包装executor
      executor = new CachingExecutor(executor);
    }
    //通过动态代理,把所有插件添加到执行器executor上
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }
发布了48 篇原创文章 · 获赞 3 · 访问量 3110

猜你喜欢

转载自blog.csdn.net/hou_ge/article/details/103734682
今日推荐