美文网首页orm
MyBatis源码阅读【执行】(五)SqlSession执行主流

MyBatis源码阅读【执行】(五)SqlSession执行主流

作者: 云芈山人 | 来源:发表于2021-05-26 13:30 被阅读0次

    一、相关类与接口

    • DefaultSqlSession

      SqlSession接口的默认实现类

    • Executor接口

      Executor接口.png
    • CachingExecutor

      CachingExecutor 是一个 Executor 接口的装饰器,它为 Executor 对象增加了二级缓存的相关功能。

    • BaseExecutor

      专门处理一级缓存

    • SimpleExecutor

      执行简单的JDBC操作

    • StatementHandler接口

    StatementHandler接口.png
    • RoutingStatementHandler

      路由。Mybatis实际使用的类,拦截的StatementHandler实际就是它。它会根据Exector类型创建对应的StatementHandler,保存到属性delegate中
    • PreparedStatementHandler

      预编译Statement
    • ResultSetHandler接口

      处理Statement执行后产生的结果集,生成结果列表;处理存储过程执行后的输出参数
    • DefaultResultSetHandler

      ResultSetHandler的 默认实现类

    二、流程图

    SqlSession执行主流程.png

    三、流程分析

    • 入口:DefaultSqlSession#selectList

       @Override
        public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
            try {
                // 根据传入的statementId,获取MappedStatement对象
                MappedStatement ms = configuration.getMappedStatement(statement);
                // 调用执行器的查询方法
                // RowBounds是用来逻辑分页(按照条件将数据从数据库查询到内存中,在内存中进行分页)
                // wrapCollection(parameter)是用来装饰集合或者数组参数
                return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
            } catch (Exception e) {
                throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
            } finally {
                ErrorContext.instance().reset();
            }
        }
    
    • CachingExecutor#query

      如果开启了二级缓存,则先在二级缓存中查询,调用缓存执行器的查询方法
      //第一步
      @Override
      public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
        // 获取绑定的SQL语句,比如“SELECT * FROM user WHERE id = ? ” 
        BoundSql boundSql = ms.getBoundSql(parameterObject);
        // 生成缓存Key
        CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
        
        return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
      }
    
      //第二步
      @Override
      public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
          throws SQLException {
        // 获取二级缓存
        Cache cache = ms.getCache();
        if (cache != null) {
          // 当为select语句时,flushCache默认为false,表示任何时候语句被调用,都不会去清空本地缓存和二级缓存
          // 当为insert、update、delete语句时,useCache默认为true,表示会将本条语句的结果进行二级缓存
          // 刷新二级缓存 (存在缓存且flushCache为true时)
          flushCacheIfRequired(ms);
          if (ms.isUseCache() && resultHandler == null) {
            ensureNoOutParams(ms, boundSql);
            // 从二级缓存中查询数据
            @SuppressWarnings("unchecked")
            List<E> list = (List<E>) tcm.getObject(cache, key);
            // 如果二级缓存中没有查询到数据,则查询数据库
            if (list == null) {
              // 委托给BaseExecutor执行
              list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
              tcm.putObject(cache, key, list); // issue #578 and #116
            }
            return list;
          }
        }
        // 委托给BaseExecutor执行
        return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
      }
    
    • BaseExecutor#query

      二级缓存设置开启且缓存中没有或者未开启二级缓存,则从一级缓存中查找结果集
    @Override
      public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
        if (closed) {
          throw new ExecutorException("Executor was closed.");
        }
        if (queryStack == 0 && ms.isFlushCacheRequired()) {
          clearLocalCache();
        }
        List<E> list;
        try {
          queryStack++;
          // 从一级缓存中获取数据
          list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
          if (list != null) {
            handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
          } else {
            // 如果一级缓存没有数据,则从数据库查询数据
            list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
          }
        } finally {
          queryStack--;
        }
        if (queryStack == 0) {
          for (DeferredLoad deferredLoad : deferredLoads) {
            deferredLoad.load();
          }
          // issue #601
          deferredLoads.clear();
          if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
            // issue #482
            clearLocalCache();
          }
        }
        return list;
      }
    
    • BaseExecutor#queryFromDatabase

      如果一级缓存没有数据,则从数据库查询数据
    private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        List<E> list;
        localCache.putObject(key, EXECUTION_PLACEHOLDER);
        try {
          // 执行查询
          list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
        } finally {
          //移除一级缓存中原有值
          localCache.removeObject(key);
        }
        //往一级缓存中存值
        localCache.putObject(key, list);
        if (ms.getStatementType() == StatementType.CALLABLE) {
          localOutputParameterCache.putObject(key, parameter);
        }
        return list;
      }
    
    • SimpleExecutor#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 configuration = ms.getConfiguration();
                // 创建RoutingStatementHandler,用来处理Statement
                // RoutingStatementHandler类中初始化delegate类(SimpleStatementHandler、PreparedStatementHandler)
                StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds,
                        resultHandler, boundSql);
                // 子流程1:设置参数
                stmt = prepareStatement(handler, ms.getStatementLog());
                // 子流程2:执行SQL语句(已经设置过参数),并且映射结果集
                return handler.query(stmt, resultHandler);
            } finally {
                closeStatement(stmt);
            }
        }
    
    • 1.Configuration#newStatementHandler

      创建StatementHandler,用来执行MappedStatement对象
    public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement,
                Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
            // 创建路由功能的StatementHandler,根据MappedStatement中的StatementType
            StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject,
                    rowBounds, resultHandler, boundSql);
            statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
            return statementHandler;
        }
    
    • 1.1 RoutingStatementHandler#构造函数

      创建路由功能的StatementHandler,根据MappedStatement中的StatementType
    public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    
        switch (ms.getStatementType()) {
          case STATEMENT:
            delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
          case PREPARED:
            delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
          case CALLABLE:
            delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
          default:
            throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
        }
    
      }
    
    • 2. SimpleExecutor#prepareStatement

      设置参数
        private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
            Statement stmt;
            // 获取连接
            Connection connection = getConnection(statementLog);
            // 创建Statement(PreparedStatement、Statement、CallableStatement)
            stmt = handler.prepare(connection, transaction.getTimeout());
            // SQL参数设置
            handler.parameterize(stmt);
            return stmt;
        }
    
    • 2.1 BaseExecutor#getConnection

      获取数据库连接
    protected Connection getConnection(Log statementLog) throws SQLException {
        Connection connection = transaction.getConnection();
        if (statementLog.isDebugEnabled()) {
          return ConnectionLogger.newInstance(connection, statementLog, queryStack);
        } else {
          return connection;
        }
      }
    
    • 2.2 BaseStatementHandler#prepare

      创建Statement(PreparedStatement、Statement、CallableStatement)
     @Override
      public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
        ErrorContext.instance().sql(boundSql.getSql());
        Statement statement = null;
        try {
          // 实例化Statement,比如PreparedStatement
          statement = instantiateStatement(connection);
          // 设置查询超时时间
          setStatementTimeout(statement, transactionTimeout);
          setFetchSize(statement);
          return statement;
        } catch (SQLException e) {
          closeStatement(statement);
          throw e;
        } catch (Exception e) {
          closeStatement(statement);
          throw new ExecutorException("Error preparing statement.  Cause: " + e, e);
        }
      }
    
    • 2.2.1 PreparedStatementHandler#instantiateStatement

      实例化PreparedStatement
    @Override
      protected Statement instantiateStatement(Connection connection) throws SQLException {
        // 获取带有占位符的SQL语句
        String sql = boundSql.getSql();
        // 处理带有主键返回的SQL
        if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
          String[] keyColumnNames = mappedStatement.getKeyColumns();
          if (keyColumnNames == null) {
            return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
          } else {
            return connection.prepareStatement(sql, keyColumnNames);
          }
        } else if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
          return connection.prepareStatement(sql);
        } else {
          return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
        }
      }
    
    • 2.3 PreparedStatementHandler#parameterize

      SQL参数设置,参数映射流程会详细分解
    @Override
      public void parameterize(Statement statement) throws SQLException {
        // 通过ParameterHandler处理参数
        parameterHandler.setParameters((PreparedStatement) statement);
      }
    
    • 3. PreparedStatementHandler#query

      执行SQL语句(已经设置过参数),并且映射结果集
    @Override
      public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        PreparedStatement ps = (PreparedStatement) statement;
        // 执行PreparedStatement,也就是执行SQL语句
        ps.execute();
        // 处理结果集
        return resultSetHandler.handleResultSets(ps);
      }
    
    • 3.1 PreparedStatement#execute

      调用JDBC的api执行Statement

    • 3.2 DefaultResultSetHandler#handleResultSets

      处理结果集 ,结果映射流程会详细分解

    @Override
        public List<Object> handleResultSets(Statement stmt) throws SQLException {
            ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
    
            // <select>标签的resultMap属性,可以指定多个值,多个值之间用逗号(,)分割
            final List<Object> multipleResults = new ArrayList<>();
    
            int resultSetCount = 0;
            // 这里是获取第一个结果集,将传统JDBC的ResultSet包装成一个包含结果列元信息的ResultSetWrapper对象
            ResultSetWrapper rsw = getFirstResultSet(stmt);
    
            // 这里是获取所有要映射的ResultMap(按照逗号分割出来的)
            List<ResultMap> resultMaps = mappedStatement.getResultMaps();
            // 要映射的ResultMap的数量
            int resultMapCount = resultMaps.size();
            validateResultMapsCount(rsw, resultMapCount);
            // 循环处理每个ResultMap,从第一个开始处理
            while (rsw != null && resultMapCount > resultSetCount) {
                // 得到结果映射信息
                ResultMap resultMap = resultMaps.get(resultSetCount);
                // 处理结果集
                // 从rsw结果集参数中获取查询结果,再根据resultMap映射信息,将查询结果映射到multipleResults中
                handleResultSet(rsw, resultMap, multipleResults, null);
    
                rsw = getNextResultSet(stmt);
                cleanUpAfterHandlingResultSet();
                resultSetCount++;
            }
    
            // 对应<select>标签的resultSets属性,一般不使用该属性
            String[] resultSets = mappedStatement.getResultSets();
            if (resultSets != null) {
                while (rsw != null && resultSetCount < resultSets.length) {
                    ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
                    if (parentMapping != null) {
                        String nestedResultMapId = parentMapping.getNestedResultMapId();
                        ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
                        handleResultSet(rsw, resultMap, null, parentMapping);
                    }
                    rsw = getNextResultSet(stmt);
                    cleanUpAfterHandlingResultSet();
                    resultSetCount++;
                }
            }
    
            // 如果只有一个结果集合,则直接从多结果集中取出第一个
            return collapseSingleResultList(multipleResults);
        }
    

    四、总结

    执行sqlsession:参数有两个(statementId和参数对象)
    1. 根据statementId,去Configuration中的MappedStatement集合中查找
      对应的MappedStatement对象;
    2. 取出MappedStatement中的SQL信息;
    3. 取出MappedStatement中的statementType,用来创建Statement对象;
      • 取出MappedStatement中的Configuration对象,通过Configuration对象,获取DataSource对象,通过DataSource对象,创建Connection,通过Connection创建Statement对象。
      • 设置参数
      • 执行Statement
      • 处理结果集

    相关文章

      网友评论

        本文标题:MyBatis源码阅读【执行】(五)SqlSession执行主流

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