mybatis 执行流程深入分析

目录

mybatis 执行流程深入分析

按照2个主要步骤来分析执行查询的流程
1. jdbc查询的流程
2. 处理返回结果

分析的时候涉及到的概念性名词在附注之后说明

阅读本文时,假定以下条件
1. 理解反射相关知识
2. 理解代理模式

本文忽略的部分:
1. 查询时的参数赋值
2. 延迟加载
3. 元数据对象
4. 结果集处理器

笔者认为,忽略的部分都可以单独拿出来详细说明.本文对于忽略的部分进行了简单的概念介绍.不影响阅读

例子

mybatis 查询所有

    @Test
    public void testRead() throws IOException {


        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        SqlSession sqlSession = sqlSessionFactory.openSession();
        try {
selectAll
            List<Blog> blog = sqlSession.selectList("com.aya.mapper.BlogMapper.selectAll");
            System.out.println(blog);
        } finally {
            sqlSession.close();
        }
    }

selectAll 的sql

   <select id="selectAll" resultType="com.aya.mapper.Blog" >
        select * from blog
    </select>

分析-执行查询流程

准备查询

 @Override
  public <E> List<E> selectList(String statement) {
    return this.selectList(statement, null);
  }

  @Override
  public <E> List<E> selectList(String statement, Object parameter) {
    return this.selectList(statement, parameter, RowBounds.DEFAULT);
  }

  @Override
  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
        // 获取映射声明
      MappedStatement ms = configuration.getMappedStatement(statement);
      //wrapCollection 包装集合对象
      //executor.query 执行查询
      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();
    }
  }
  1. com.aya.mapper.BlogMapper.selectAll 获取对象 MappedStatement
  2. wrapCollection 包装集合
  3. executor.query 执行器执行查询

执行查询-绑定key与缓存

@Override
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
  // 获取绑定的sql
    BoundSql boundSql = ms.getBoundSql(parameter);
    // 获取缓存key
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    // 执行查询
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
 }

执行查询-缓存与延迟加载

@SuppressWarnings("unchecked")
  @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.");
    }
    //queryStack 处理延迟加载
    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;
  }

这里做的相当于一个统筹的工作

  1. ErrorContext是用来记录当前步骤了,发生异常的时候回打印,详情:错误上下文
  2. 使用queryStack 处理延迟加载.当 ResultMap 嵌套association|collection 查询时queryStack>0.
    queryStack == 0 时,处理延迟加载的操作
  3. localCache 从本地缓存中获取数据,获取到之后处理输出数据
  4. 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;
  }

这里任然是做的一个统筹的工作
1. 本地缓存存入占位符
2. 本地缓存存入真实结果

存入占位符的目的是什么? 当ResultMap嵌套的属性是延迟加载的时候,判断是不是占位符.

执行数据库查询-执行

 @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());
      // 执行查询,并返回处理返回结果
      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }
  1. StatementHandler 是mybatis定义的一个,对于 Statement,PrepareStatement,CallableStatement 三种JDBC 查询的封装
  2. prepareStatement 执行对应的 createStatement,prepareStatement,prepareCall
  3. 执行查询获得ResultSet,返回处理结果
  4. 关闭Statement (stat.close())

到这里JDBC的查询就已经执行完成了。

查询流程总结

整体来看做了以下处理
1. 缓存处理
2. 延迟加载处理
3. 异常信息记录

分析-处理返回结果

执行查询-准备处理返回结果

RoutingStatementHandler

 @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    return delegate.<E>query(statement, resultHandler);
  }

PreparedStatementHandler

   @Override
    public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
      PreparedStatement ps = (PreparedStatement) statement;
      ps.execute();
      return resultSetHandler.<E> handleResultSets(ps);
    }
  1. RoutingStatementHandler实际上是一个代理模式,内部执行是用PreparedStatementHandler|StatementHandler|CallableHandler
  2. PreparedStatementHandler 执行查询
  3. DefaultResultSetHandler 执行处理结果

处理返回结果-统筹结果集

 @Override
  public List<Object> handleResultSets(Statement stmt) throws SQLException {
  // 记录信息
    ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

    final List<Object> multipleResults = new ArrayList<Object>();

    int resultSetCount = 0;
    // 查询可以返回多个结果集,ResultSet 可以有多个, 这里获得第一个
    ResultSetWrapper rsw = getFirstResultSet(stmt);
// 获得 Xml 对应的 ResultMap 映射对象,记录了类实例字段和数据库表字段对应关系的列表
// 但是这里笔者只使用过 在select标签定义一个resultMap,并不会定义多个。 
//  不理解这里为什么要定义为一个集合
    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    // 验证是不是有结果集返回,如果没有结果集返回,抛出异常
    validateResultMapsCount(rsw, resultMapCount);
    while (rsw != null && resultMapCount > resultSetCount) {
        // 获得select标签定义的 ResultMap 映射对象
      ResultMap resultMap = resultMaps.get(resultSetCount);
      // 处理结果集将结果存入multipleResults
      handleResultSet(rsw, resultMap, multipleResults, null);
      // 遍历结果集
      rsw = getNextResultSet(stmt);
      cleanUpAfterHandlingResultSet();
      resultSetCount++;
    }
    // 处理select 标签的resultSet 定义的结果集信息
    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++;
      }
    }
    // 展开并返回List<List<T>> 的第一个结果集
    return collapseSingleResultList(multipleResults);
  }
  1. 对多个查询进行处理
  2. 验证是否有 结果集返回
  3. handleResultSet 处理 结果集
  4. collapseSingleResultList 展开 结果集并返回

处理返回结果-统筹结果集处理器

  private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
    try {
    // 父映射不为空
      if (parentMapping != null) {

        handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
      } else {
        if (resultHandler == null) {
        // 默认结果集处理器
          DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
          handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
          multipleResults.add(defaultResultHandler.getResultList());
        } else {
        // 自定义结果集处理器
          handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
        }
      }
    } finally {
      // issue #228 (close resultsets)
      closeResultSet(rsw.getResultSet());
    }
  }

这里对多种情况进行判断,接下来进行常用的默认结果集处理器分析

处理返回结果-统筹嵌套映射

  public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
  // 是否有嵌套的 ResultMap
    if (resultMap.hasNestedResultMaps()) {
      ensureNoRowBounds();
      checkResultHandler();
      handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    } else {
    // 顶层ResultMap 处理
      handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    }
  }

处理返回结果-遍历与存储

    private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
        throws SQLException {
      DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>();
      // 内存忽略读取n行
      skipRows(rsw.getResultSet(), rowBounds);
      遍历ResultSet 查询到的结果,依次添加到resultHandler
      while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {
      // 根据ResultMap 的 discriminated 标签进行选择具体的ResultMap 对象
        ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
        //创建对象, 将第 n 行的数据给对象
        Object rowValue = getRowValue(rsw, discriminatedResultMap);
        // 将对象用 resultHandler 进行处理。
        storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
      }
    }
  1. 内存忽略读取N行
  2. 获得返回结果
  3. 存储到 resultHandler 的 list

处理返回结果-对象的创建于赋值

private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
    final ResultLoaderMap lazyLoader = new ResultLoaderMap();
    // 创建对象
    Object rowValue = createResultObject(rsw, resultMap, lazyLoader, null);
    if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
    // 创建对象的元数据对象
      final MetaObject metaObject = configuration.newMetaObject(rowValue);
      boolean foundValues = this.useConstructorMappings;
      if (shouldApplyAutomaticMappings(resultMap, false)) {
      // 应用自动映射, 类字段和表字段一致时赋值
        foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
      }
      // 应用手动映射, result 定义的 column 与 property
      foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
      foundValues = lazyLoader.size() > 0 || foundValues;
      // 如果找到了值或者 mybatis.xml  configuration>settings>returnInstanceForEmptyRow=true 时,返回对象(有可能并未填充值)
      rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
    }
    return rowValue;
  }
  1. 根据resultMap 的定义,创建对象
  2. 创建元数据对象, (赋值时使用元数据赋值)
  3. 自动映射赋值
  4. 手动映射赋值
  5. 实际找到的结果和 returnInstanceForEmptyRow 综合, 返回空对象或者null

结论

对于mybatis结果为空时:

返回一定不为null 的只有一种顶层集合

返回null 的情况有三种
1. 嵌套对象
2. 嵌套集合
3. 顶层对象

但是返回为null也可以通过xml配置 configuration>settings>returnInstanceForEmptyRow=true , 设置结果集一定不为 null

附注

映射声明

MappedStatement 是mybatis的一个映射声明

在加载xml的时候添加到Configuration实例的字段Map<String, MappedStatement> mappedStatements

错误上下文

ErrorContext 是用来记录当前操作线程正在执行的信息

准备查询时,捕获所有异常,并打印ErrorContext记录的的信息

ExceptionFactory的wrapException

   public static RuntimeException wrapException(String message, Exception e) {
     return new PersistenceException(ErrorContext.instance().message(message).cause(e).toString(), e);
   }

ErrorContext的toString

@Override
  public String toString() {
    StringBuilder description = new StringBuilder();

    // message
    if (this.message != null) {
      description.append(LINE_SEPARATOR);
      description.append("### ");
      description.append(this.message);
    }
    // 版面原因,省略其他打印的信息

    return description.toString();
  }

jdbc查询

选自 https://www.cnblogs.com/wuyuegb2312/p/3872607.html 的一部分

private static Connection getConn() {
    String driver = "com.mysql.jdbc.Driver";
    String url = "jdbc:mysql://localhost:3306/samp_db";
    String username = "root";
    String password = "";
    Connection conn = null;
    try {
        Class.forName(driver); //classLoader,加载对应驱动
        conn = (Connection) DriverManager.getConnection(url, username, password);
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (SQLException e) {
        e.printStackTrace();
    }
    return conn;
}

private static Integer getAll() {
// 创建jdbc连接
    Connection conn = getConn();
    String sql = "select * from students";
    PreparedStatement pstmt;
    try {
    // 创建Statement
        pstmt = (PreparedStatement)conn.prepareStatement(sql);
        // 执行查询
        ResultSet rs = pstmt.executeQuery();

        int col = rs.getMetaData().getColumnCount();
        System.out.println("============================");
        // 处理返回结果
        while (rs.next()) {
            for (int i = 1; i <= col; i++) {
                System.out.print(rs.getString(i) + "\t");
                if ((i == 2) && (rs.getString(i).length() < 8)) {
                    System.out.print("\t");
                }
             }
            System.out.println("");
        }
            System.out.println("============================");
    } catch (SQLException e) {
        e.printStackTrace();
    }
    return null;
}

结果集

结果集可以在sqlSession.selectList自定义,

不传入结果集时使用默认结果集处理器 DefaultResultHandler

元数据对象

MetaObject 内部有真实的对象, 内部对 Bean,Collection,Map 的赋值有自己的处理

主要可以通过 setValue 方法,进行反射赋值

猜你喜欢

转载自blog.csdn.net/mz4138/article/details/81663150