继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

myBatis源码流程分析记录

慕村9548890
关注TA
已关注
手记 1102
粉丝 227
获赞 987

webp

mybatis核心组件关系图

mybatis主要组件

  • SqlSession:是mybatis提供的面向开发者的框架底层API,开发者通过获取一个是sqlSession实例来进行具体的数据库增删改查操作

  • Executor:mybatis的执行器,用于执行sql,生成结果并进行结果缓存等操作,类似于controller

  • StatementHandler:对JDBC的Statement进行封装

  • ParameterHandler:参数转换,将用户传递的参数转换成JDBC需要的参数

  • ResultSetHandler:将JDBC返回的结果集转换成java对象,并返回给调用者

  • TypeHandler:java数据类型和JDBC数据类型之间的映射

  • MappedStatement:一个MappedStatement维护一条<select|update|delete|insert>节点的封装

  • SqlSource:根据用户传递的参数动态生成sql语句,信息封装搭配BoundSql中,BoundSql表示动态生成的sql语句及参数信息

  • Configuration:持有mybatis所有的配置信息

mybatis主干流程(以query为例)

1: SqlSession

  /**
   * Retrieve a single row mapped from the statement key
   * @param <T> the returned object type
   * @param statement
   * @return Mapped object
   */
  <T> T selectOne(String statement);

2:DefaultSessionSql

  @Override
  public <T> T selectOne(String statement) {    return this.<T>selectOne(statement, null);
  }  @Override
  public <T> T selectOne(String statement, Object parameter) {    // Popular vote was to return null on 0 results and throw exception on too many.
    List<T> list = this.<T>selectList(statement, parameter);    if (list.size() == 1) {      return list.get(0);
    } else if (list.size() > 1) {      throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
    } else {      return 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);      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();
    }
  }

3:BaseExecutor

  @Override
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    BoundSql boundSql = ms.getBoundSql(parameter);
    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.");
    }    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;
  }  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;
  }

4:SimpleExecutor

  @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 handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      stmt = prepareStatement(handler, ms.getStatementLog());      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

5:SimpleStatementHandler

  @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    String sql = boundSql.getSql();
    statement.execute(sql);    return resultSetHandler.<E>handleResultSets(statement);
  }

6:DefaultResultSetHandler

 //
  // HANDLE RESULT SETS
  //
  @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;
    ResultSetWrapper rsw = getFirstResultSet(stmt);

    List<ResultMap> resultMaps = mappedStatement.getResultMaps();    int resultMapCount = resultMaps.size();
    validateResultMapsCount(rsw, resultMapCount);    while (rsw != null && resultMapCount > resultSetCount) {
      ResultMap resultMap = resultMaps.get(resultSetCount);
      handleResultSet(rsw, resultMap, multipleResults, null);
      rsw = getNextResultSet(stmt);
      cleanUpAfterHandlingResultSet();
      resultSetCount++;
    }

    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);
  }  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 {    if (resultMap.hasNestedResultMaps()) {
      ensureNoRowBounds();
      checkResultHandler();
      handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    } else {
      handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    }
  } private void handleRowValuesForNestedResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {    final DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>();
    skipRows(rsw.getResultSet(), rowBounds);
    Object rowValue = previousRowValue;    while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {      final ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);      final CacheKey rowKey = createRowKey(discriminatedResultMap, rsw, null);
      Object partialObject = nestedResultObjects.get(rowKey);      // issue #577 && #542
      if (mappedStatement.isResultOrdered()) {        if (partialObject == null && rowValue != null) {
          nestedResultObjects.clear();
          storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
        }
        rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, null, partialObject);
      } else {
        rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, null, partialObject);        if (partialObject == null) {
          storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
        }
      }
    }    if (rowValue != null && mappedStatement.isResultOrdered() && shouldProcessMoreRows(resultContext, rowBounds)) {
      storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
      previousRowValue = null;
    } else if (rowValue != null) {
      previousRowValue = rowValue;
    }
  } private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, CacheKey combinedKey, String columnPrefix, Object partialObject) throws SQLException {    final String resultMapId = resultMap.getId();
    Object rowValue = partialObject;    if (rowValue != null) {      final MetaObject metaObject = configuration.newMetaObject(rowValue);
      putAncestor(rowValue, resultMapId, columnPrefix);
      applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, false);
      ancestorObjects.remove(resultMapId);
    } else {      final ResultLoaderMap lazyLoader = new ResultLoaderMap();
      rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);      if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {        final MetaObject metaObject = configuration.newMetaObject(rowValue);        boolean foundValues = this.useConstructorMappings;        if (shouldApplyAutomaticMappings(resultMap, true)) {
          foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
        }
        foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
        putAncestor(rowValue, resultMapId, columnPrefix);
        foundValues = applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, true) || foundValues;
        ancestorObjects.remove(resultMapId);
        foundValues = lazyLoader.size() > 0 || foundValues;
        rowValue = (foundValues || configuration.isReturnInstanceForEmptyRow()) ? rowValue : null;
      }      if (combinedKey != CacheKey.NULL_CACHE_KEY) {
        nestedResultObjects.put(combinedKey, rowValue);
      }
    }    return rowValue;
  }  private boolean applyNestedResultMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String parentPrefix, CacheKey parentRowKey, boolean newObject) {    boolean foundValues = false;    for (ResultMapping resultMapping : resultMap.getPropertyResultMappings()) {      final String nestedResultMapId = resultMapping.getNestedResultMapId();      if (nestedResultMapId != null && resultMapping.getResultSet() == null) {        try {          final String columnPrefix = getColumnPrefix(parentPrefix, resultMapping);          final ResultMap nestedResultMap = getNestedResultMap(rsw.getResultSet(), nestedResultMapId, columnPrefix);          if (resultMapping.getColumnPrefix() == null) {            // try to fill circular reference only when columnPrefix
            // is not specified for the nested result map (issue #215)
            Object ancestorObject = ancestorObjects.get(nestedResultMapId);            if (ancestorObject != null) {              if (newObject) {
                linkObjects(metaObject, resultMapping, ancestorObject); // issue #385
              }              continue;
            }
          } 
          final CacheKey rowKey = createRowKey(nestedResultMap, rsw, columnPrefix);          final CacheKey combinedKey = combineKeys(rowKey, parentRowKey);
          Object rowValue = nestedResultObjects.get(combinedKey);          boolean knownValue = (rowValue != null);
          instantiateCollectionPropertyIfAppropriate(resultMapping, metaObject); // mandatory
          if (anyNotNullColumnHasValue(resultMapping, columnPrefix, rsw)) {
            rowValue = getRowValue(rsw, nestedResultMap, combinedKey, columnPrefix, rowValue);            if (rowValue != null && !knownValue) {
              linkObjects(metaObject, resultMapping, rowValue);
              foundValues = true;
            }
          }
        } catch (SQLException e) {          throw new ExecutorException("Error getting nested result map values for '" + resultMapping.getProperty() + "'.  Cause: " + e, e);
        }
      }
    }    return foundValues;
  }  private ResultMap getNestedResultMap(ResultSet rs, String nestedResultMapId, String columnPrefix) throws SQLException {
    ResultMap nestedResultMap = configuration.getResultMap(nestedResultMapId);    return resolveDiscriminatedResultMap(rs, nestedResultMap, columnPrefix);
  } private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, CacheKey combinedKey, String columnPrefix, Object partialObject) throws SQLException {    final String resultMapId = resultMap.getId();
    Object rowValue = partialObject;    if (rowValue != null) {      final MetaObject metaObject = configuration.newMetaObject(rowValue);
      putAncestor(rowValue, resultMapId, columnPrefix);
      applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, false);
      ancestorObjects.remove(resultMapId);
    } else {      final ResultLoaderMap lazyLoader = new ResultLoaderMap();
      rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);      if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {        final MetaObject metaObject = configuration.newMetaObject(rowValue);        boolean foundValues = this.useConstructorMappings;        if (shouldApplyAutomaticMappings(resultMap, true)) {
          foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
        }
        foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
        putAncestor(rowValue, resultMapId, columnPrefix);
        foundValues = applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, true) || foundValues;
        ancestorObjects.remove(resultMapId);
        foundValues = lazyLoader.size() > 0 || foundValues;
        rowValue = (foundValues || configuration.isReturnInstanceForEmptyRow()) ? rowValue : null;
      }      if (combinedKey != CacheKey.NULL_CACHE_KEY) {
        nestedResultObjects.put(combinedKey, rowValue);
      }
    }    return rowValue;
  }  private void linkObjects(MetaObject metaObject, ResultMapping resultMapping, Object rowValue) {    final Object collectionProperty = instantiateCollectionPropertyIfAppropriate(resultMapping, metaObject);    if (collectionProperty != null) {      final MetaObject targetMetaObject = configuration.newMetaObject(collectionProperty);
      targetMetaObject.add(rowValue);
    } else {
      metaObject.setValue(resultMapping.getProperty(), rowValue);
    }
  }

其实流程到达JDBC获取到结果集这里都不算复杂,最为复杂的地方(对于query)就是ResultSetHandler这一部分,当然也分两种情况,如果没有关联查询,那就不难理解了,直接使用ResultType的相关规则生成结果对象即可,具体就是通过resultType先create一个新的结果对象,然后根据字段映射规则将DB中查询出来的结果集映射到java对象中来即可。麻烦的是对于关联查询,就不能简单的这么处理了,可能还存在一对多的情况,先看下面两个方法:

 private void linkObjects(MetaObject metaObject, ResultMapping resultMapping, Object rowValue) {    final Object collectionProperty = instantiateCollectionPropertyIfAppropriate(resultMapping, metaObject);    if (collectionProperty != null) {      final MetaObject targetMetaObject = configuration.newMetaObject(collectionProperty);
      targetMetaObject.add(rowValue);
    } else {
      metaObject.setValue(resultMapping.getProperty(), rowValue);
    }
  }  private Object instantiateCollectionPropertyIfAppropriate(ResultMapping resultMapping, MetaObject metaObject) {    final String propertyName = resultMapping.getProperty();
    Object propertyValue = metaObject.getValue(propertyName);    if (propertyValue == null) {
      Class<?> type = resultMapping.getJavaType();      if (type == null) {
        type = metaObject.getSetterType(propertyName);
      }      try {        if (objectFactory.isCollection(type)) {
          propertyValue = objectFactory.create(type);
          metaObject.setValue(propertyName, propertyValue);          return propertyValue;
        }
      } catch (Exception e) {        throw new ExecutorException("Error instantiating collection property for result '" + resultMapping.getProperty() + "'.  Cause: " + e, e);
      }
    } else if (objectFactory.isCollection(propertyValue.getClass())) {      return propertyValue;
    }    return null;
  }

instantiateCollectionPropertyIfAppropriate方法会在需要的时候创建一个集合(处理一对多的情况),在上面的流程中提到过linkObjects方法,该方法会将一个查询好的对象(数据库中的一行查询结果)关联到结果对象中去,如果关联查询到的是一个集合,则添加该行数据到集合中去,否则设置一个结果属性。

最后再说一件事情,getNestedResultMap 方法会根据resultMap id取到正确的resultMap,然后就可以获取到resultClass,createResultObject会生成一个新的用于接收查询结果集的映射对象,getRowValue会生根据设置的映射规则填充这个生成的对象,然后会通过createRowKey方法创建一个cacheKey,put到nestedResultObjects中去,然后接着就可以通过linkObjects将这个对象设置到映射结果对象中去了。



作者:一字马胡
链接:https://www.jianshu.com/p/119d6cf6b8fb


打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP