mybatis隨筆五之Executor


在上一篇文章我們分析到了mapper接口方法的實現實際上是交由代理類來實現的,並最終調用Executor來查詢,接下來我們對
executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER)這個方法進行分析。
@Override
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    BoundSql boundSql = ms.getBoundSql(parameterObject);
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }
ms.getBoundSql內部調用RawSqlSource的getBoundSql方法,該方法又調用了StaticSqlSource的getBoundSql方法,並在該方法內部初始化了一個BoundSql對象,如下是BoundSql的參數
  private String sql;   //需要執行的sql語句
  private List<ParameterMapping> parameterMappings;   //參數與數據庫列的對應關系
  private Object parameterObject;                     //查詢傳遞的參數
  private Map<String, Object> additionalParameters;
  private MetaObject metaParameters;
createCacheKey是調用的BaseExecutor方法根據mappedStatement的id,rowBounds的offset、limit值、要執行的sql語句、傳遞的參數、environment的id來創建cacheKey。
在query的時候查看是否有cache,如果有則使用cache結果,否則使用內部delegate的query方法,這里跳轉到了BaseExecutor的query方法,該方法內部又使用了
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;
  }
方法比較簡單將查詢結果保存在chche中,調用doQuery方法,BaseExecutor的doQuery方法是個抽象方法,因此這里實際使用的是子類SimpleExecutor的doQuery方法
 @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);
    }
  }
我們先看看newStatementHandler這個方法,這個方法主要做了兩件事情,實例化了一個RoutingStatementHandler對象,將攔截目標是statementHandler的攔截器構成攔截鏈。
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 = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
  }
RoutingStatementHandler采用的是裝飾設計模式,內部delegate委托的是PreparedStatementHandler對象,因此它的構造方法內部去創建了一個PreparedStatementHandler對象
protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    this.configuration = mappedStatement.getConfiguration();
    this.executor = executor;
    this.mappedStatement = mappedStatement;
    this.rowBounds = rowBounds;

    this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
    this.objectFactory = configuration.getObjectFactory();

    if (boundSql == null) { // issue #435, get the key before calculating the statement
      generateKeys(parameterObject);
      boundSql = mappedStatement.getBoundSql(parameterObject);
    }

    this.boundSql = boundSql;

    this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
    this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
  }
構建的過程中也初始化了parameterHandler、resultSetHandler兩個對象,顧名思義一個是用來處理參數的一個是用來處理結果的。
所以在創建StatementHandler的同時其余兩個handler也被創建出來了。
接下來使用prepareStatement來構建參數。
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    Connection connection = getConnection(statementLog);
    stmt = handler.prepare(connection);
    handler.parameterize(stmt);
    return stmt;
  }
在這個方法內部,我們最關注的是倒數二、三句。
handler.prepare類似mapper交由statementHandler的代理對象來執行,若沒有針對其的攔截方法則還是調用RoutingStatementHandler的prepare方法。
public Statement prepare(Connection connection) throws SQLException {
    ErrorContext.instance().sql(boundSql.getSql());
    Statement statement = null;
    try {
      statement = instantiateStatement(connection);
      setStatementTimeout(statement);
      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);
    }
  }
這里主要做了幾件事:返回preparedStatement對象、設置查詢超時時間、設置每次批量返回的結果行數。
handler.parameterize(stmt)方法類似也是交由statementHandler的代理對象來執行,最終也使用RoutingStatementHandler的parameterize方法。
public void parameterize(Statement statement) throws SQLException {
    parameterHandler.setParameters((PreparedStatement) statement);
  }
在這個方法里主要是根據定義的入參的javaType、jdbcType類型來選擇合適的typeHandler來設置參數,因為我們使用的是long類型,因此typeHandler使用的是LongTypeHandler。
這樣我們就把preparedStatement所需的參數全部填充了,最終進入handler.<E>query(stmt, resultHandler)方法。
query方法也會先調用攔截鏈的方法,最后使用RoutingStatementHandler的query方法。
@Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    return resultSetHandler.<E> handleResultSets(ps);
  }

 





  

 





免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM