Mybatis 深入淺出 -- 執(zhí)行篇

Mybatis 執(zhí)行流程深入淺出

本文繼上篇mybatis初始化流程后丧蘸,剖析其執(zhí)行流程

上篇文章傳送門

還是這幾個靈魂問題:

  1. Mybatis解決了什么問題? 無非是簡化數(shù)據(jù)庫操作怜珍、實現(xiàn)封裝恼策、讓程序員更關注SQL本身码倦、維護便利
  2. 它是如何解決這些問題的企孩?
  3. Mybatis是運行在什么樣的環(huán)境下的?
  4. 它如何讀取解析用戶定義的配置信息袁稽?即如何初始化的
  5. 它的環(huán)境結(jié)構(gòu)是什么樣的勿璃?
  6. 在這一環(huán)境下如何 實現(xiàn)做增刪查改
  7. 如何執(zhí)行SQL
  8. 如何執(zhí)行動態(tài)SQL
  9. 如何拼接查詢參數(shù)等等

上篇文章解答了 2問的 1、2推汽、3小問补疑,這篇文章就來說說剩下的幾個問題
也是大家使用得最多、最關心的問題


正經(jīng)的分割線


先給出一張圖歹撒,幫助大家能快速了解流程

Mybatis的執(zhí)行流程.png

同樣莲组,這里搬出我們上篇文章的TestCase

注意 閱讀 序號 (1)、(2)暖夭、(3)锹杈、(4)、(5)迈着、(6)...
Test.java

    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(“xxx/mybatis.xml”);
    // 使用一個SqlSession作為此次連接竭望, 主要講這兒,初始化流程
    SqlSession sqlSession = sqlSessionFactory.openSession();
    (1)動態(tài)代理獲取代理的mapper對象
    CustomMapper mapper = sqlSession.getMapper(DemoMapper.class);
    Map<String,Object> map = new HashMap<>();
    map.put("id","1");
    (4) 執(zhí)行代理對象的方法
    System.out.println(mapper.select(map));
    sqlSession.close();

獲取動態(tài)代理的mapper

DefaultSqlSession

  (2)通過默認的sqlsession獲取到上篇文章講到的configuration(存儲著mappers)
  @Override
  public <T> T getMapper(Class<T> type) {
    // 從
    return configuration.getMapper(type, this);
  }

MapperRegistry

  (2)從knownMappers中拿到 代理工廠裕菠,使用工廠生成代理對象
  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

這里貼上關鍵點:本質(zhì)是通過Proxy.newProxyInstance 來生成的代理對象
MapperRegistry

  (3)本質(zhì)是通過MapperProxyFactory
public class MapperProxyFactory<T> {

  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<>();

  public MapperProxyFactory(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
  }

  public Class<T> getMapperInterface() {
    return mapperInterface;
  }

  public Map<Method, MapperMethod> getMethodCache() {
    return methodCache;
  }

  @SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

  public T newInstance(SqlSession sqlSession) {
    // 這里的 MapperProxy<T> implements InvocationHandler 借助了InvocationHandler咬清,實現(xiàn)invoke 方法,進而實現(xiàn)代理對象調(diào)用邏輯控制
    具體調(diào)用請回到上面TestCase中的 (4)
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

}

執(zhí)行代理對象的方法

MapperProxy

  (5)執(zhí)行具體方法
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {
        //如果是object類的默認方法奴潘,比如equals旧烧,hashcode什么的
        return method.invoke(this, args);
      } else if (method.isDefault()) {
        //如果是default方法,java8画髓,9處理不同掘剪。但都是把方法綁定到代理對象再調(diào)用
        if (privateLookupInMethod == null) {
          return invokeDefaultMethodJava8(proxy, method, args);
        } else {
          return invokeDefaultMethodJava9(proxy, method, args);
        }
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    // 重點來了
    (6)獲取mapper執(zhí)行的方法
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    (8)真正執(zhí)行傳進來的mapper操作
    return mapperMethod.execute(sqlSession, args);
  }  
  
  (7)看是否有緩存,如果沒有就創(chuàng)建新的 mapper的執(zhí)行方法
  private MapperMethod cachedMapperMethod(Method method) {
    return methodCache.computeIfAbsent(method,
        k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
  }

MapperMethod

public class MapperMethod {

  private final SqlCommand command;
  private final MethodSignature method;

  public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
    // SQL的信息奈虾,select|insert|xxx杖小,初始化的MappedStatement等信息
    this.command = new SqlCommand(config, mapperInterface, method);
    // 當前執(zhí)行方法簽名肆汹,包含返回類型、resultHanlder予权、行范圍、查詢參數(shù)映射解析等信息
    this.method = new MethodSignature(config, mapperInterface, method);
  }

 (8)執(zhí)行sql
  public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    switch (command.getType()) {
      case INSERT: {
        // 如果是CUD操作浪册,將傳入的參數(shù)扫腺,映射到具體某個位置的SQL參數(shù)上
        // CUD操作其實都是調(diào)用的 update方法, 詳見(9)
        // 下面先講這個流程村象,select在之后講
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.insert(command.getName(), param));
        break;
      }
      case UPDATE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.update(command.getName(), param));
        break;
      }
      case DELETE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.delete(command.getName(), param));
        break;
      }
      case SELECT:
        if (method.returnsVoid() && method.hasResultHandler()) {
          executeWithResultHandler(sqlSession, args);
          result = null;
        } else if (method.returnsMany()) {
          result = executeForMany(sqlSession, args);
        } else if (method.returnsMap()) {
          result = executeForMap(sqlSession, args);
        } else if (method.returnsCursor()) {
          result = executeForCursor(sqlSession, args);
        } else {
          Object param = method.convertArgsToSqlCommandParam(args);
          result = sqlSession.selectOne(command.getName(), param);
          if (method.returnsOptional()
              && (result == null || !method.getReturnType().equals(result.getClass()))) {
            result = Optional.ofNullable(result);
          }
        }
        break;
      case FLUSH:
        result = sqlSession.flushStatements();
        break;
      default:
        throw new BindingException("Unknown execution method for: " + command.getName());
    }
    if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
      throw new BindingException("Mapper method '" + command.getName()
          + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
    }
    return result;
  }

  ······
}

增刪改 的流程

DefaultSqlSession

······
  @Override
  public int update(String statement, Object parameter) {
    try {
      (9)獲取MappedStatement笆环,通過執(zhí)行器執(zhí)行
      dirty = true;
      MappedStatement ms = configuration.getMappedStatement(statement);
      return executor.update(ms, wrapCollection(parameter));
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
······

SimpleExecutor

  @Override
  public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      // 創(chuàng)建一個statement的處理器
      (10)
      StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
      (11)
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.update(stmt);
    } finally {
      closeStatement(stmt);
    }
  }

  (11)這里就是jdbc熟悉的,獲取連接厚者,準備Statement躁劣,設置參數(shù)到Statement
  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    Connection connection = getConnection(statementLog);
    stmt = handler.prepare(connection, transaction.getTimeout());
    // parameterHandler設置參數(shù)
    handler.parameterize(stmt);
    return stmt;
  }

Configuration

(10)
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
   // 將StatementHandler加入調(diào)用鏈里,返回代理的statementHandler库菲, 供插件用账忘, 分頁插件可通過這兒來攔截執(zhí)行
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
  }

DefaultParameterHandler

public class DefaultParameterHandler implements ParameterHandler {

  private final TypeHandlerRegistry typeHandlerRegistry;

  private final MappedStatement mappedStatement;
  private final Object parameterObject;
  private final BoundSql boundSql;
  private final Configuration configuration;

  public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
    this.mappedStatement = mappedStatement;
    this.configuration = mappedStatement.getConfiguration();
    this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();
    this.parameterObject = parameterObject;
    this.boundSql = boundSql;
  }

  @Override
  public Object getParameterObject() {
    return parameterObject;
  }

  (12)設置PreparedStatement 參數(shù)的邏輯
   @Override
   public void setParameters(PreparedStatement ps) {
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
    // 獲取到xml中配置的 參數(shù)
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings != null) {
      for (int i = 0; i < parameterMappings.size(); i++) {
        ParameterMapping parameterMapping = parameterMappings.get(i);
        if (parameterMapping.getMode() != ParameterMode.OUT) {
          Object value;
          // 拿到名稱
          String propertyName = parameterMapping.getProperty();
          // 首先看是否給參數(shù)設置了“額外”的名稱,如果是直接賦值
          if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
            value = boundSql.getAdditionalParameter(propertyName);
          } else if (parameterObject == null) {
            value = null;
            // 使用mybatis內(nèi)置的java常用類型的type映射
          } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
            value = parameterObject;
          } else {
            // 否則嘗試從元數(shù)據(jù)中嘗試獲取 值
            MetaObject metaObject = configuration.newMetaObject(parameterObject);
            value = metaObject.getValue(propertyName);
          }
          TypeHandler typeHandler = parameterMapping.getTypeHandler();
          JdbcType jdbcType = parameterMapping.getJdbcType();
          if (value == null && jdbcType == null) {
            // 都為空則各拷,通過null獲取type
            jdbcType = configuration.getJdbcTypeForNull();
          }
          try {
            // 設置對應參數(shù) -- 底層PreparedStatement.setxxx(i + 1, value)
            typeHandler.setParameter(ps, i + 1, value, jdbcType);
          } catch (TypeException | SQLException e) {
            throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
          }
        }
      }
    }
  }

}

PreparedStatementHandler

  (13)給statement設置好參數(shù)后岖免,到通過PreparedStatementHandler真正執(zhí)行update的時候了
  @Override
  public int update(Statement statement) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    // 獲取affectRows數(shù)量
    int rows = ps.getUpdateCount();
    Object parameterObject = boundSql.getParameterObject();
    // 主鍵返回后的執(zhí)行策略台腥,比如處理數(shù)據(jù)庫自增主鍵
    KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
    keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
    return rows;
  }

到此增刪改操作流程全部結(jié)束

下面開始講Select執(zhí)行流程

和上述相同部分會一筆帶過,主要差異在結(jié)果集的映射上
這里 閱讀 序號重排 從mapper.select(map) 上的(4)開始蒋荚,(5)、(6)...
MapperMethod

  (5)接著從MapperMethod開始
      case SELECT:
        if (method.returnsVoid() && method.hasResultHandler()) {
        // 如果無返回值馆蠕,有結(jié)果集處理器
          executeWithResultHandler(sqlSession, args);
          result = null;
        } else if (method.returnsMany()) {
        // 如果返回list
          result = executeForMany(sqlSession, args);
        } else if (method.returnsMap()) {
        // 如果返回map
          result = executeForMap(sqlSession, args);
        } else if (method.returnsCursor()) {
        // 查詢下標
          result = executeForCursor(sqlSession, args);
        } else {
        // 如果單個結(jié)果
          Object param = method.convertArgsToSqlCommandParam(args);
          result = sqlSession.selectOne(command.getName(), param);
          if (method.returnsOptional()
              && (result == null || !method.getReturnType().equals(result.getClass()))) {
            result = Optional.ofNullable(result);
          }
        }
        break;
  
  private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
    List<E> result;
    (6)如果是多個則轉(zhuǎn)換成參數(shù)為map映射期升,單個則直接是當前參數(shù),沒有則為null
    Object param = method.convertArgsToSqlCommandParam(args);
    // 默認分頁互躬,rowBounds 基于內(nèi)存實現(xiàn)分頁播赁,不建議使用
    if (method.hasRowBounds()) {
      RowBounds rowBounds = method.extractRowBounds(args);
      result = sqlSession.selectList(command.getName(), param, rowBounds);
    } else {
      // 執(zhí)行查詢 詳見(9)
      result = sqlSession.selectList(command.getName(), param);
    }
    // issue #510 Collections & arrays support
    if (!method.getReturnType().isAssignableFrom(result.getClass())) {
      if (method.getReturnType().isArray()) {
        return convertToArray(result);
      } else {
        return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
      }
    }
    return result;
  }
  
  (7)通過解析器解析參數(shù),構(gòu)造參數(shù)映射
  public Object convertArgsToSqlCommandParam(Object[] args) {
    return paramNameResolver.getNamedParams(args);
  }

ParamNameResolver

  (8)解析names映射的地方吨铸,在mapper方法上添加了 @Param 注解行拢,會解析到names里
  public ParamNameResolver(Configuration config, Method method) {
    final Class<?>[] paramTypes = method.getParameterTypes();
    final Annotation[][] paramAnnotations = method.getParameterAnnotations();
    final SortedMap<Integer, String> map = new TreeMap<>();
    int paramCount = paramAnnotations.length;
    // get names from @Param annotations
    for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
      if (isSpecialParameter(paramTypes[paramIndex])) {
        // skip special parameters
        continue;
      }
      String name = null;
      for (Annotation annotation : paramAnnotations[paramIndex]) {
        if (annotation instanceof Param) {
          hasParamAnnotation = true;
          name = ((Param) annotation).value();
          break;
        }
      }
      if (name == null) {
        // @Param was not specified.
        //Spring MVC 底層調(diào)用的不是JDK的API  Spring MVC底層是去解析字節(jié)碼
        //在jdk8以前 調(diào)用這個getName 會有問題 arg0
        //jdk8
        if (config.isUseActualParamName()) {
          name = getActualParamName(method, paramIndex);
        }
        if (name == null) {
          // use the parameter index as the name ("0", "1", ...)
          // gcode issue #71
          name = String.valueOf(map.size());
        }
      }
      map.put(paramIndex, name);
    }
    names = Collections.unmodifiableSortedMap(map);
  }
  public Object getNamedParams(Object[] args) {
    final int paramCount = names.size();
    if (args == null || paramCount == 0) {
      return null;
    } else if (!hasParamAnnotation && paramCount == 1) {
      return args[names.firstKey()];
    } else {
      final Map<String, Object> param = new ParamMap<>();
      int i = 0;
      for (Map.Entry<Integer, String> entry : names.entrySet()) {
        //names : arg0,arg1
        param.put(entry.getValue(), args[entry.getKey()]);
        // add generic param names (param1, param2, ...)
        final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
        // ensure not to overwrite parameter named with @Param
        if (!names.containsValue(genericParamName)) {
          param.put(genericParamName, args[entry.getKey()]);
        }
        i++;
      }
      return param;
    }
  }

DefaultSqlSession

  (9)
  @Override
  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      MappedStatement ms = configuration.getMappedStatement(statement);
      // 通過執(zhí)行器執(zhí)行查詢
      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();
    }
  }

BaseExecutor

  (10)
  @Override
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    BoundSql boundSql = ms.getBoundSql(parameter);
    (11)獲取緩存的key
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    (12)
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
  }


  @Override
  public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    CacheKey cacheKey = new CacheKey();
    // 根據(jù)SQL的id(com.DemoMapper.select),開啟分頁的查詢范圍(起始位置诞吱,查詢條數(shù))舟奠,SQL語句,從方法上傳過來的參數(shù)房维,返回的結(jié)果value值
    // 五個條件來判斷沼瘫,是否是相同的查詢,做緩存key
    cacheKey.update(ms.getId());
    cacheKey.update(rowBounds.getOffset());
    cacheKey.update(rowBounds.getLimit());
    cacheKey.update(boundSql.getSql());
    // 獲取到xml中配置的 參數(shù)
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
    // mimic DefaultParameterHandler logic
  // 這里與上面做CUD操作時的 (12)DefaultParameterHandler設置PreparedStatement參數(shù)的邏輯 相同咙俩,不再贅述
    for (ParameterMapping parameterMapping : parameterMappings) {
      if (parameterMapping.getMode() != ParameterMode.OUT) {
        Object value;
        String propertyName = parameterMapping.getProperty();
        if (boundSql.hasAdditionalParameter(propertyName)) {
          value = boundSql.getAdditionalParameter(propertyName);
        } else if (parameterObject == null) {
          value = null;
        } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
          value = parameterObject;
        } else {
          MetaObject metaObject = configuration.newMetaObject(parameterObject);
          value = metaObject.getValue(propertyName);
        }
        // 通過獲取到的value更新 緩存key
        cacheKey.update(value);
      }
    }
    if (configuration.getEnvironment() != null) {
      // issue #176
      cacheKey.update(configuration.getEnvironment().getId());
    }
    return cacheKey;
  }

  // CacheKey類 中代碼計算hashcode
  public void update(Object object) {
    // 如果為空則為1耿戚,不為空判斷是否是數(shù)組湿故,不是直接返回對象hashcode,是則判斷具體是哪一種類型數(shù)組膜蛔,進行hash運算
    int baseHashCode = object == null ? 1 : ArrayUtil.hashCode(object);
    //每update一次累加一次
    count++;
    checksum += baseHashCode;
    baseHashCode *= count;
    hashcode = multiplier * hashcode + baseHashCode;
    updateList.add(object);
  }

  (12)執(zhí)行查詢
  @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.");
    }
    // 如果當前沒有經(jīng)歷過查詢坛猪,并且需要更新緩存
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
      clearLocalCache();
    }
    List<E> list;
    try {
      queryStack++;
      list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
      if (list != null) {
        //對于數(shù)據(jù)庫的存儲過程進行輸出資源處理
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
      } else {
        (13)查詢
        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 {
      (13)
      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

  (13)和上面CUD 相同的邏輯
  @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());
      (14)
      return handler.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

PreparedStatementHandler

  (14)
  @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    (15)處理結(jié)果集
    return resultSetHandler.handleResultSets(ps);
  }

DefaultResultSetHandler

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

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

    int resultSetCount = 0;
    // 有多個結(jié)果集取第一個,將ps取得的結(jié)果集 - ResultSet 包裝在 wrapper中皂股,其中還包含configuration信息
    ResultSetWrapper rsw = getFirstResultSet(stmt);

    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    validateResultMapsCount(rsw, resultMapCount);
    // 遍歷所有結(jié)果集
    while (rsw != null && resultMapCount > resultSetCount) {
      ResultMap resultMap = resultMaps.get(resultSetCount);
      (16)處理單個結(jié)果集
      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);
  }

  (16)
  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 {
          (17)處理具體一行的結(jié)果值
          handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
        }
      }
    } finally {
      // issue #228 (close resultsets)
      closeResultSet(rsw.getResultSet());
    }
  }  

  (17)
  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);
    }
  }

  (18)
  private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
      throws SQLException {
    DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
    ResultSet resultSet = rsw.getResultSet();
    // 跳過分頁行
    skipRows(resultSet, rowBounds);
    // 遍歷resultSet
    while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
      // 解析鑒別器墅茉,<discriminator>標簽 - 用于 當某個字段滿足一定條件時,使用指定的resultMap呜呐。比如
      //     <discriminator column="enabled" javaType="int">
      //        <case value="1" resultMap="testMap1"/>
      //        <case value="0" resultMap="testMap2"/>
      //    </discriminator>
      ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
      (19)取值
      Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
      // 將取出來的值就斤,放入context中,再將context的值存入resultHandler
      storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
    }
  }

  (19)
  private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
    // 當需要進行循環(huán)結(jié)果集映射時蘑辑,使用到的懶加載 LoadPair
    final ResultLoaderMap lazyLoader = new ResultLoaderMap();
    // 創(chuàng)建空map存儲值
    Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
    // 有結(jié)果集的處理器
    if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
      // 借助于初始化生成MetaObject信息 利用反射信息洋机,操作返回值
      final MetaObject metaObject = configuration.newMetaObject(rowValue);
      boolean foundValues = this.useConstructorMappings;
      // 是否自動映射 通過resultMap 的 automapping屬性設置, 默認啟用
      if (shouldApplyAutomaticMappings(resultMap, false)) {
        foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
      }
      // 自定義的映射 比如 通過resultMap 的 子標簽<id>洋魂、<result>設置
      foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
      foundValues = lazyLoader.size() > 0 || foundValues;
      rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
    }
    return rowValue;
  }

  // 到這里查詢就結(jié)束了绷旗,后面就是上面的流程,關閉ps等資源忧设,然后將結(jié)果放入緩存等刁标。

看到這兒的都是狼人,現(xiàn)在來看看圖址晕,自我總結(jié)一下

Mybatis的執(zhí)行流程.png

下篇文章將會剖析 可能是大家開發(fā)中用到最多的開源插件源碼 PageHelper

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末膀懈,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子谨垃,更是在濱河造成了極大的恐慌启搂,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件刘陶,死亡現(xiàn)場離奇詭異胳赌,居然都是意外死亡,警方通過查閱死者的電腦和手機匙隔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門疑苫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人纷责,你說我怎么就攤上這事捍掺。” “怎么了再膳?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵挺勿,是天一觀的道長。 經(jīng)常有香客問我喂柒,道長不瓶,這世上最難降的妖魔是什么禾嫉? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮蚊丐,結(jié)果婚禮上熙参,老公的妹妹穿的比我還像新娘。我一直安慰自己麦备,他們只是感情好尊惰,可當我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著泥兰,像睡著了一般。 火紅的嫁衣襯著肌膚如雪题禀。 梳的紋絲不亂的頭發(fā)上鞋诗,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天,我揣著相機與錄音迈嘹,去河邊找鬼削彬。 笑死,一個胖子當著我的面吹牛秀仲,可吹牛的內(nèi)容都是我干的融痛。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼神僵,長吁一口氣:“原來是場噩夢啊……” “哼雁刷!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起保礼,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤沛励,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后炮障,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體目派,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年胁赢,在試婚紗的時候發(fā)現(xiàn)自己被綠了企蹭。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡智末,死狀恐怖谅摄,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情吹害,我是刑警寧澤螟凭,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站它呀,受9級特大地震影響螺男,放射性物質(zhì)發(fā)生泄漏棒厘。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一下隧、第九天 我趴在偏房一處隱蔽的房頂上張望奢人。 院中可真熱鬧,春花似錦淆院、人聲如沸何乎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽支救。三九已至,卻和暖如春拷淘,著一層夾襖步出監(jiān)牢的瞬間各墨,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工启涯, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留贬堵,地道東北人。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓结洼,卻偏偏與公主長得像黎做,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子松忍,可洞房花燭夜當晚...
    茶點故事閱讀 44,592評論 2 353