美文网首页Java技术
myBatis源码流程分析记录

myBatis源码流程分析记录

作者: 一字马胡 | 来源:发表于2018-10-22 23:05 被阅读93次

    作者: 一字马胡

    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将这个对象设置到映射结果对象中去了。

    相关文章

      网友评论

        本文标题:myBatis源码流程分析记录

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