2.10、mybatis源碼分析之sql執(zhí)行過程以select為例

  • 在研究select執(zhí)行過程之前先來介紹一個重要的類

一、SqlNode和SqlSource

  • 在myabtis初始化過程中可以知道,映射配置文件中的sql節(jié)點會被解析為MappedStatement對象,其中sql語句解析成SqlSource對象,sql語句中定義的Sql節(jié)點、文本節(jié)點等,則由SqlNode接口的響應實現(xiàn)。

1、SqlSource接口

SqlSource接口結構
public interface SqlSource {
//通過解析得到BoundSql對象,其中封裝了包含?占位符號的sql語句以及綁定的實參
  BoundSql getBoundSql(Object parameterObject);
}

2、SqlNode

public interface SqlNode {
//根據(jù)用戶傳入的實際參數(shù),解析該sqlNode所記錄的動態(tài)的sql節(jié)點,當sql節(jié)點下的所有sqlnode
//完成解析后,我們就可以從dynamicContext中獲取一條動態(tài)生成的完整的sql語句。
  boolean apply(DynamicContext context);
}
  • sqlNode有很多的實現(xiàn)類,比如ChooseSqlNode、ForEachSqlNode等

二、StatementHandler

  • 核心接口,完成mybatis中最核心的工作,接口功能很多,例如創(chuàng)建statement對象,為sql語句綁定實際參數(shù),執(zhí)行select、insert等多種類型的sql語句,并將結果集映射成結果對象。


    StatementHandler類結構圖

1、RoutingStatementHandler

  • 很根據(jù)MapperdStatement中指定的statementType字段,創(chuàng)建相應的StatementHandler接口實現(xiàn)。

2、BaseStatementHandler

  • 實現(xiàn)了Statementhandler接口的抽象類,只提供了參數(shù)綁定的相關方法,并沒有具體操作數(shù)據(jù)庫的方法。依賴于ParameterHandler和ResultSetHandler組件
  • 實現(xiàn)了 StatementHandler接口中的prepare方法,該方法中調用instantiateStatement抽象方法初始化statement對象,然后配置相關的參數(shù)

3、PreparedStatementHandler等

  • 是BaseStatementHandler的實現(xiàn)類,是具體操作sql的地方

三、ResultHandler

  • StatementHandler接口在執(zhí)行完指定的select語句之后,會將查詢得到的結果集交給ResultSetHandler完成映射處理,ResultSetHandler除了負責映射select語句得到的結果集還會處理存儲過程執(zhí)行后的輸出參數(shù)
public interface ResultSetHandler {
//處理結果集生成相應的結果對象集合
  <E> List<E> handleResultSets(Statement stmt) throws SQLException;
//處理結果集,返回相應的游標對象
  <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;
//處理存儲過程的輸出對象
  void handleOutputParameters(CallableStatement cs) throws SQLException;

}

1、DefaultResultSetHandler實現(xiàn)類

  • 重要的屬性:
 private final Executor executor;
  private final Configuration configuration;
  private final MappedStatement mappedStatement;
  private final RowBounds rowBounds;
  private final ParameterHandler parameterHandler;
//用戶指定用于處理結果的ResultHandler對象
  private final ResultHandler<?> resultHandler;
  private final BoundSql boundSql;
  private final TypeHandlerRegistry typeHandlerRegistry;
  private final ObjectFactory objectFactory;
  private final ReflectorFactory reflectorFactory;

四、select的執(zhí)行流程

1、方法入口,執(zhí)行代理接口的invoke方法

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  //判斷方法的父類是否是Object如果是就直接調用
    if (Object.class.equals(method.getDeclaringClass())) {
      return method.invoke(this, args);
    }
    //獲取MapperMethod并緩存
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }
   private final Map<Method, MapperMethod> methodCache;
1、獲取MapperMethod時候先判斷methodCache有沒有,沒有的話那么直接創(chuàng)建然后放入methodCache中(下次調用直接從map中獲取)
  private MapperMethod cachedMapperMethod(Method method) {
    MapperMethod mapperMethod = methodCache.get(method);
    if (mapperMethod == null) {
    //構建mapperMethod方法
      mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
      methodCache.put(method, mapperMethod);
    }
    return mapperMethod;
  }
2、創(chuàng)建MapperMethod對象,SqlCommand和MethodSignature是MapperMethod的內部類
   public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
     //SqlCommand包含了mapper.xml文件中的相關sql語句及屬性信息
    this.command = new SqlCommand(config, mapperInterface, method);
    //MethodSignature包含了mapper接口方法簽名的信息
    this.method = new MethodSignature(config, method);
  }
2.1、獲取SqlCommand
   public SqlCommand(Configuration configuration, Class<?> declaringInterface, Method method) throws BindingException {
     //包名+方法名作為唯一的id
      name = declaringInterface.getName() + "." + method.getName();
      final MappedStatement ms;
      try {
        //根據(jù)id獲取
        ms = configuration.getMappedStatement(name);
      } catch (Exception e) {
        throw new BindingException("Invalid bound statement (not found): " + name, e);
      }
      //獲取crud類型如果是unknown就報錯誤
      type = ms.getSqlCommandType();
      if (type == SqlCommandType.UNKNOWN) {
        throw new BindingException("Unknown execution method for: " + name);
      }
    }
2.2、獲取MethodSignature
 public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {
      Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);
      if (resolvedReturnType instanceof Class<?>) {
        this.returnType = (Class<?>) resolvedReturnType;
      } else if (resolvedReturnType instanceof ParameterizedType) {
        this.returnType = (Class<?>) ((ParameterizedType) resolvedReturnType).getRawType();
      } else {
        this.returnType = method.getReturnType();
      }
      this.returnsVoid = void.class.equals(this.returnType);
      this.returnsMany = configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray();
      this.returnsCursor = Cursor.class.equals(this.returnType);
      this.mapKey = getMapKey(method);
      this.returnsMap = this.mapKey != null;
      this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
      this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
      this.paramNameResolver = new ParamNameResolver(configuration, method);
    }

2、執(zhí)行MapperMethod的execute方法

public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    //根據(jù)SqlCommand類型判斷是執(zhí)行curd的哪一個
    //insert
    if (SqlCommandType.INSERT == command.getType()) {
        //將參數(shù)轉換為sql命令的參數(shù)
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.insert(command.getName(), param));
      //update
    } else if (SqlCommandType.UPDATE == command.getType()) {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.update(command.getName(), param));
      //delete
    } else if (SqlCommandType.DELETE == command.getType()) {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.delete(command.getName(), param));
      //select
    } else if (SqlCommandType.SELECT == command.getType()) {
    //MethodSignature方法簽名的返回類型來進行不同的邏輯處理
      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 {
      //當不滿足上面需求的時候進行這一步操作,先進行參數(shù)解析,包裝為map返回
        Object param = method.convertArgsToSqlCommandParam(args);
        result = sqlSession.selectOne(command.getName(), param);
      }
    } else {
      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;
  }  

3、DefaultSqlSession中執(zhí)行sqlSession.selectOne方法

public <T> T selectOne(String statement, Object parameter) {
    // 調用selectList獲取list列表,然后獲取第一個。如果有多個那么就直接報錯誤
    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;
    }
  }

4、歸根結底還是調用executor的query方法來執(zhí)行

public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
        ////根據(jù)statementId獲取MappedStatement,MappedStatement封裝了增刪改查的詳細信息
      MappedStatement ms = configuration.getMappedStatement(statement);
      //調用executor的query
      List<E> result = executor.<E>query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
      return result;
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
//包裝參數(shù)為map類型
  private Object wrapCollection(final Object object) {
    if (object instanceof List) {
      StrictMap<Object> map = new StrictMap<Object>();
      map.put("list", object);
      return map;
    } else if (object != null && object.getClass().isArray()) {
      StrictMap<Object> map = new StrictMap<Object>();
      map.put("array", object);
      return map;
    }
    return object;
  }

5、執(zhí)行cachingExecutor的query方法,

public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    //獲取解析過動態(tài)標簽的sql,BoundSql包含當前sql語句的詳細信息和參數(shù)
    BoundSql boundSql = ms.getBoundSql(parameterObject);
    //創(chuàng)建緩存key
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

6、獲取BoundSql

public BoundSql getBoundSql(Object parameterObject) {
/*獲取解析過動態(tài)標簽的sql*/
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings == null || parameterMappings.size() <= 0) {
      boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
    }

    // 檢查參數(shù)映射中是否存在嵌套的resultMap并設置boolean標記
    for (ParameterMapping pm : boundSql.getParameterMappings()) {
      String rmId = pm.getResultMapId();
      if (rmId != null) {
        ResultMap rm = configuration.getResultMap(rmId);
        if (rm != null) {
          hasNestedResultMaps |= rm.hasNestedResultMaps();
        }
      }
    }

    return boundSql;
  }

7、執(zhí)行BaseExecutor的query

public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
   //獲取本地緩存中的數(shù)據(jù)
    Cache cache = ms.getCache();
    //緩存為空則直接調用
    if (cache != null) {
      flushCacheIfRequired(ms);
      if (ms.isUseCache() && resultHandler == null) { 
        ensureNoOutParams(ms, key, parameterObject, boundSql);
        if (!dirty) {
          cache.getReadWriteLock().readLock().lock();
          try {
            @SuppressWarnings("unchecked")
            List<E> cachedList = (List<E>) cache.getObject(key);
            if (cachedList != null) return cachedList;
          } finally {
            cache.getReadWriteLock().readLock().unlock();
          }
        }
        List<E> list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
        tcm.putObject(cache, key, list); // issue #578. Query must be not synchronized to prevent deadlocks
        return list;
      }
    }
    return delegate.<E>query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

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();
      }
      deferredLoads.clear(); // issue #601
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        clearLocalCache(); // issue #482
      }
    }
    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 {
    //子類實現(xiàn)具體的查詢邏輯
      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;
  }

8、這里在SimpleExecutor中真正的執(zhí)行query(根據(jù)配置還可以在選擇其他的Executor)

 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();
     //創(chuàng)建statementhandler,statementhandler是創(chuàng)建原生statement的處理器
      StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, rowBounds, resultHandler, boundSql);
     //準備Statement
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

9、構建StatementHandler

 public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
   /*構造RoutingStatementHandler*/
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
   //攔截器執(zhí)行
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
  }
  //終于柳暗花明了
  public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
//根據(jù)MappedStatement中的statement類型來創(chuàng)建響應的statement處理器
    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());
    }

  }

10、statement的準備

  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    //獲取數(shù)據(jù)庫連接
    Connection connection = getConnection(statementLog);
    //進行響應的statement的轉唄工作
    stmt = handler.prepare(connection);
    //給statement設置參數(shù)
    handler.parameterize(stmt);
    return stmt;
  }
public Statement prepare(Connection connection) throws SQLException {
    ErrorContext.instance().sql(boundSql.getSql());
    Statement statement = null;
    try {
    //實例化對象的statement
      statement = instantiateStatement(connection);
      //設置超時時間
      setStatementTimeout(statement);
      //設置FetchSize
      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);
    }
  }
實例化過程以預處理statement為例:
  protected Statement instantiateStatement(Connection connection) throws SQLException {
    String sql = boundSql.getSql();
    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() != null) {
      return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
    } else {
      return connection.prepareStatement(sql);
    }
  }

11、設置參數(shù)過程

public void setParameters(PreparedStatement ps) {
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings != null) {
        //遍歷參數(shù)綁定映射列表
        for (int i = 0; i < parameterMappings.size(); i++) {
            ParameterMapping parameterMapping = parameterMappings.get(i);
            if (parameterMapping.getMode() != ParameterMode.OUT) {
                Object value;
                //獲取參數(shù)綁定的屬性名稱
                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);
                    //根據(jù)屬性名稱從參數(shù)對象中反射獲取對應的值
                    value = metaObject.getValue(propertyName);
                }
                //獲取類型處理器
                TypeHandler typeHandler = parameterMapping.getTypeHandler();
                //獲取jdbcType類型
                JdbcType jdbcType = parameterMapping.getJdbcType();
                if (value == null && jdbcType == null) {
                    jdbcType = configuration.getJdbcTypeForNull();
                }
                try {
                    /*為PreparedStatement設置值*/
                    typeHandler.setParameter(ps, i + 1, value, jdbcType);
                } catch (TypeException e) {
                    throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
                } catch (SQLException e) {
                    throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
                }
            }
        }
    }
}

12、最后執(zhí)行查詢并進行參數(shù)封裝

  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    //執(zhí)行sql語句
    ps.execute();
    //參數(shù)封裝
    return resultSetHandler.<E> handleResultSets(ps);
  }
select執(zhí)行流程
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容