美文网首页
2.8、mybatis源码分析之创建SqlSession流程

2.8、mybatis源码分析之创建SqlSession流程

作者: 小manong | 来源:发表于2018-09-19 22:46 被阅读0次

    一、mybatis接口层

    • 在讲创建SqlSession之前,先来介绍下myabtis中的接口层API


      SqlSession相关类结构

    1、SqlSession是mybatis的核心接口之一,是myabtis接口层的主要组成部分,对外提供了mybatis常用的api。myabtis提供了两个SqlSesion接口的实现,常用的实现类是DefaultSqlSession。
    2、SqlSessionFactory负责创建SqlSession,其中包含多个openSession方法的重载,具体创建在DefaultSqlSessionFactory实现类中。

    1、DefaultSqlSession

    • SqlSession接口的主要实现类,在DefaultSqlSession中使用了策略模式,把数据库相关的操作全部封装到了Executor接口实现中,并通过executor字段选择不同的Executor实现类,最终操作由Executor接口实现类完成。
    public class DefaultSqlSession implements SqlSession {
    //configuration配置对象
      private final Configuration configuration;
    //数据库操作相关的executor接口(策略模式)
      private final Executor executor;
    //是否自动提交
      private final boolean autoCommit;
    //当前缓存中是否有脏数据
      private boolean dirty;
    //为防止用户忘记关闭打开的游标对象,会通过cursorList字段记录由该sqlsession对象生成的游标对象
      private List<Cursor<?>> cursorList;
    

    2、DefaultSqlSessionFactory

    • 是SqlSessionFactory接口的具体实现类,该类主要提供了两种方式创建相应的DefaultSqlSession对象。一种是通过数据源获取数据库连接,并创建Executor对象以及DefaultSqlSession对象
     private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
        Transaction tx = null;
        try {
    //获取配置的environment对象
          final Environment environment = configuration.getEnvironment();
          final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
          tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
    //根据配置创建Executor 对象
          final Executor executor = configuration.newExecutor(tx, execType);
          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();
        }
      }
    
    • 另一种是用户提供数据库连接对象,DefaultSqlSessionFactory会使用该数据库连接对象创建相应的Executor对象以及DefaultSqlSession对象
    private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {
        try {
          boolean autoCommit;
          try {
            autoCommit = connection.getAutoCommit();
          } catch (SQLException e) {
            // Failover to true, as most poor drivers
            // or databases won't support transactions
            autoCommit = true;
          }      
          final Environment environment = configuration.getEnvironment();
          final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
          final Transaction tx = transactionFactory.newTransaction(connection);
          final Executor executor = configuration.newExecutor(tx, execType);
          return new DefaultSqlSession(configuration, executor, autoCommit);
        } catch (Exception e) {
          throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
        } finally {
          ErrorContext.instance().reset();
        }
      }
    

    二、Executor接口

    • mybatis的核心接口之一,其中定义了数据库操作的基本方法。实际应用中经常涉及的sqlSession接口的功能,都是基于Executor接口实现的。
    • mybatis中提供了Executor的接口实现,这些接口实现中涉及两种涉及模式,分别是模板方法模式和装饰器模式。其中CachingEecutor扮演了装饰器的角色,为Executor添加了二级缓存的功能。


      Executor接口实现类结构

    1、BaseExecutor

    • Executor接口的抽象实现类,实现了Executor接口的发部分方法,其中就是使用了模板方法模式。在BaseExecutor中主要提供了缓存管理(比如一级固定模式不变的一级缓存)和事物管理的基本功能,继承BaseExecutor的子类只要实现四个基本方法来完成数据库的相关操作既可以,四个方法是doUpdate、doQuery、doQueryCursor、doFlushStatement方法。
    public abstract class BaseExecutor implements Executor {
      private static final Log log = LogFactory.getLog(BaseExecutor.class);
    //实现事务的提交和回滚和关闭操作
      protected Transaction transaction;
    //封装executor对象
      protected Executor wrapper;
    //延迟队列
      protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;
    //一级缓存用于该Executor对象查询结果集映射得到的结果对象
      protected PerpetualCache localCache;
    //一级缓存用于缓存输出类型的参数
      protected PerpetualCache localOutputParameterCache;
      protected Configuration configuration;
      protected int queryStack;
      private boolean closed;
    其他方法
    

    2、SimpleExecutor

    • 继承自BaseExecutor抽象类,主要专注的实现上面介绍的四个类。例如doQuery查询方法:
      @Override
      public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        Statement stmt = null;
        try {
    //获取配置文件
          Configuration configuration = ms.getConfiguration();
    //根据相应参数创建具体额StatementHandler
          StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
          stmt = prepareStatement(handler, ms.getStatementLog());
    //执行StatementHandler.query方法执行sql语句并通过ResultSetHandler完成结果集映射。
          return handler.<E>query(stmt, resultHandler);
        } finally {
          closeStatement(stmt);
        }
      }
      private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
        Statement stmt;
        Connection connection = getConnection(statementLog);
    //创建Statement对象
        stmt = handler.prepare(connection, transaction.getTimeout());
    //处理占位符号
        handler.parameterize(stmt);
        return stmt;
      }
    

    2、ReuseExecutor

    • ReuseExecutor提供了Statement重用的功能,ReuseExecutor中通过statementMap字段缓存使用过的Statement对象,key是sql语句,value是对应的Statement对象。
    • 和SimpleExecutor的区别是在prepareStatement方法,会尝试重用StatementMap中的缓存的Statement对象。
     private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
        Statement stmt;
        BoundSql boundSql = handler.getBoundSql();
    //获取sql语句
        String sql = boundSql.getSql();
        if (hasStatementFor(sql)) {
          stmt = getStatement(sql);
    //修改超时时间
          applyTransactionTimeout(stmt);
        } else {
    //获取数据库连接并创建Statement并缓存发哦StatementMap中
          Connection connection = getConnection(statementLog);
          stmt = handler.prepare(connection, transaction.getTimeout());
          putStatement(sql, stmt);
        }
        handler.parameterize(stmt);
        return stmt;
      }
      private Statement getStatement(String s) {
        return statementMap.get(s);
      }
     private boolean hasStatementFor(String sql) {
        try {
          return statementMap.keySet().contains(sql) && !statementMap.get(sql).getConnection().isClosed();
        } catch (SQLException e) {
          return false;
        }
      }
      private void putStatement(String sql, Statement stmt) {
        statementMap.put(sql, stmt);
      }
    

    4、BatchExecutor

    • BatchExexutor提供了批量操作sql语句的功能,也就是在可以在客户端缓存多条sql语句,并在何时的时机将多条sql语句打包发送给数据库执行,从而减少网络方面的开销。
    public class BatchExecutor extends BaseExecutor {
        //缓存多个statement对象,其中每一个statement对象中都缓存可许多条sql语句
      private final List<Statement> statementList = new ArrayList<Statement>();
      //记录批处理的结果
      private final List<BatchResult> batchResultList = new ArrayList<BatchResult>();
      //当前的sql语句
      private String currentSql;
      //当前的MappedStatement对象
      private MappedStatement currentStatement;
    
      public BatchExecutor(Configuration configuration, Transaction transaction) {
        super(configuration, transaction);
      }
    
      @Override
      public int doUpdate(MappedStatement ms, Object parameterObject) throws SQLException {
        final Configuration configuration = ms.getConfiguration();
        final StatementHandler handler = configuration.newStatementHandler(this, ms, parameterObject, RowBounds.DEFAULT, null, null);
        final BoundSql boundSql = handler.getBoundSql();
        final String sql = boundSql.getSql();
        final Statement stmt;
        //如果当前执行的sql模式与上次执行的sql模式相同且对应的MappedStatement对象相同
        if (sql.equals(currentSql) && ms.equals(currentStatement)) {
          int last = statementList.size() - 1;
          stmt = statementList.get(last);
          //参数绑定处理?占位符号
          applyTransactionTimeout(stmt);
         handler.parameterize(stmt);//fix Issues 322
          BatchResult batchResult = batchResultList.get(last);
          batchResult.addParameterObject(parameterObject);
        } else {
          Connection connection = getConnection(ms.getStatementLog());
          stmt = handler.prepare(connection, transaction.getTimeout());
          handler.parameterize(stmt);    //fix Issues 322
          currentSql = sql;
          currentStatement = ms;
          statementList.add(stmt);
          batchResultList.add(new BatchResult(ms, sql, parameterObject));
        }
      // 底层使用statement.addBatch方法添加sql语句
        handler.batch(stmt);
        return BATCH_UPDATE_RETURN_VALUE;
      }
    

    5、CacheExecutor

    • 是Executor接口的装饰器,为Executor对象增加二级缓存的相关功能。
    • 具体实现讲缓存部分在说

    三、创建SqlSession流程

    • 前面介绍了关于myabtis接口层的东西,下面我们步入正题,研究创建SqlSession的流程。
    • 入口:SqlSession sqlSession = sqlSessionFactory.openSession();

    1、DefaultSqlSessionFactory的openSession()方法

    • 最终调用openSessionFromDataSource方法,前面已经讲过
    public SqlSession openSession() {
      /*
      configuration.getDefaultExecutorType()是获取ExecutorType类型,在Config配置文件中可以配置
       有三种形式SIMPLE(普通的执行器;), REUSE(执行器会重用预处理语句), BATCH(执行器将重用语句并执行批量更新),
    默认的是SIMPLE
      */
        return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
      }
    

    2、Configuration的newExecutor方法创建Executor对象

    public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
        executorType = executorType == null ? defaultExecutorType : executorType;
        executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
        Executor executor;
        //根据executorType来创建相应的执行器
        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(默认就是有缓存),装饰者模式,所以默认都是返回CachingExecutor
        /**
          * 二级缓存开关配置示例
         * <settings>
         *   <setting name="cacheEnabled" value="true"/>
         * </settings>
         */
        if (cacheEnabled) {
          executor = new CachingExecutor(executor);
        }
        //此处调用插件,通过插件可以改变Executor行为
        executor = (Executor) interceptorChain.pluginAll(executor);
        return executor;
      }
    

    3、new DefaultSqlSession创建SqlSession对象

      public DefaultSqlSession(Configuration configuration, Executor 
    executor, boolean autoCommit) {
        this.configuration = configuration;
        this.executor = executor;
        this.dirty = false;
        this.autoCommit = autoCommit;
      }
    

    四、创建SqlSession流程图

    创建SqlSession流程图

    相关文章

      网友评论

          本文标题:2.8、mybatis源码分析之创建SqlSession流程

          本文链接:https://www.haomeiwen.com/subject/jgcngftx.html