Mybatis源碼筆記之淺析ParameterHandler

概念

職責

ParameterHandler是用來設置參數規則的。StatementHandler中介紹到,其SimpleExecutor中調用prepare()方法之後,接下來StatementHandler就是使用parameterize來設置參數。以SampleExecutor爲例,具體代碼如下:

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    Connection connection = getConnection(statementLog);
    stmt = handler.prepare(connection, transaction.getTimeout());
    //解析並設置參數
    handler.parameterize(stmt);
    return stmt;
  }
  
 @Override
  public void parameterize(Statement statement) throws SQLException {
    parameterHandler.setParameters((PreparedStatement) statement);
  }

類圖

進入源碼,該接口很簡單,且只有一個默認實現類DefaultParameterHandler

public interface ParameterHandler {
  Object getParameterObject();
  void setParameters(PreparedStatement ps)
      throws SQLException;

}
  • getParameterObject()用於讀取參數;
  • setParameter():用於對PreparedStatementHandler的參數賦值;
    在這裏插入圖片描述

源碼

ParameterHandler對象創建

對於ParameterHandler對象的創建過程,首先拋出結論:該對象是在創建StatementHandler對象的同時被創建完成。StatementHandler文章中我們談論到其依賴ParameterHandler和ResultSetHandler,下面我們進入正題。

上面談到了該對象在StatementHandler對象創建時被創建,所以我們從StatementHandler對象位置開始跟蹤:

  • 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();
      //Configuration中獲取StatementHandler,跟進去
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }
  • Configuration
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
//創建StatementHandler
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
  }

跟進RoutingStatementHandler,我們以SimpleStatementHandler爲例,其餘*StatementHandler對象創建一樣,調用其父類BaseStatementHandler構造方法,所以我們跟蹤到其父類的構造方法中。

  • SimpleStatementHandler
public SimpleStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
  }
  • BaseStatementHandler
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 對象的創建交給Configuration類操作;
從上面幾步源碼的跟蹤:Configuration類依次完成了StatementHandler、parameterHandler 、resultSetHandler 對象的創建過程。
那麼繼續迴歸到Configuration中。

  • Configuration
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
    ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
    parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
    return parameterHandler;
  }

ParameterHandler解析參數

上面完成了StatementHandler,ParameterHandler和ResultSetHandler對象的創建,基本工作已準備完成,下面繼續迴歸到SimpleExecutor#prepareStatement()

 @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();
      //Configuration中獲取StatementHandler
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      //準備Statement對象
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

 private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    Connection connection = getConnection(statementLog);
    stmt = handler.prepare(connection, transaction.getTimeout());
    handler.parameterize(stmt);
    return stmt;
  }
  • PrepareStatementHandler
@Override
  public void parameterize(Statement statement) throws SQLException {
    parameterHandler.setParameters((PreparedStatement) statement);
  }

代碼裏看到是parameterHandler對象調用,上面我們瞭解到ParameterHandler接口僅有一個實現類即DefaultParameterHandler,因此Debug進去。

  • DefaultParameterHandler
 @Override
  public void setParameters(PreparedStatement ps) {
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
    // parameterMappings 就是對 #{} 或者 ${} 裏面參數的封裝
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings != null) {
      // 如果是參數化的SQL,便需要循環取出並設置參數的值
      for (int i = 0; i < parameterMappings.size(); i++) {
        ParameterMapping parameterMapping = parameterMappings.get(i);
        // 如果參數類型不是 OUT ,這個類型與 CallableStatementHandler 有關
        // 因爲存儲過程不存在輸出參數,所以參數不是輸出參數的時候,就需要設置。
        if (parameterMapping.getMode() != ParameterMode.OUT) {
          Object value;
          // 得到#{}  中的屬性名
          String propertyName = parameterMapping.getProperty();
          // 如果 propertyName 是 Map 中的key
          if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
            // 通過key 來得到 additionalParameter 中的value值
            value = boundSql.getAdditionalParameter(propertyName);
          } else if (parameterObject == null) {
            // 如果不是 additionalParameters 中的key,而且傳入參數是 null, 則value 就是null
            value = null;
          }
          // 如果 typeHandlerRegistry 中已經註冊了這個參數的 Class對象,即它是Primitive 或者是String 的話
          else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
            value = parameterObject;
          } else {
            // 否則就是 Map
            MetaObject metaObject = configuration.newMetaObject(parameterObject);
            value = metaObject.getValue(propertyName);
          }
          // 在通過SqlSource 的parse 方法得到parameterMappings 的具體實現中,我們會得到parameterMappings的typeHandler
          TypeHandler typeHandler = parameterMapping.getTypeHandler();
          // 獲取typeHandler 的jdbc type
          JdbcType jdbcType = parameterMapping.getJdbcType();
          if (value == null && jdbcType == null) {
            jdbcType = configuration.getJdbcTypeForNull();
          }
          try {
            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);
          }
        }
      }
    }
  }

總結

總結一下ParameterHandler整體大致流程走向,以下面時序圖爲例
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章