大懶使小懶之StatementHandler

大懶使小懶之StatementHandler

前一節咱們已經在看到了sql執行的背後高手Executor(說是高手其實就是SqlSession的高級員工),術業有專攻,當領導任務下達給Executor以後,Executor制定大致的行動方向,接下來就把一切具體的實現交給了本身的小弟StatementHandler,真是名副其實的大懶使小懶呀!java

首先讓咱們來看看StatementHandler中都有誰。sql

StatementHandler類結構圖

接口設計數據庫

public interface StatementHandler {
  Statement prepare(Connection connection, Integer transactionTimeout)
      throws SQLException;
    
  void parameterize(Statement statement)
      throws SQLException;

  void batch(Statement statement)
      throws SQLException;

  int update(Statement statement)
      throws SQLException;

  <E> List<E> query(Statement statement, ResultHandler resultHandler)
      throws SQLException;

  <E> Cursor<E> queryCursor(Statement statement)
      throws SQLException;

  BoundSql getBoundSql();

  ParameterHandler getParameterHandler();

}

根據jdbc中不一樣的statement,核心的三個子類:app

一、SimpleStatementHandler:用於處理Statement數據庫操做ide

二、PreparedStatementHandler:用於處理PreparedStatement數據庫操做函數

三、CallableStatementHandler:用於處理CallableStatement數據庫操做.net

他們三個都繼承自同一個基類BaseStatementHandler,讓咱們來看看這個類設計

BaseStatementHandler 對StatementHandler接口的方法進行了實現,其中只有一個抽象方法交由子類進行實現3d

protected abstract Statement instantiateStatement(Connection connection) throws SQLException;

根據方法名,咱們能夠大概知道這個方法是不一樣子類實例化不一樣statement的方法。咱們逐一來看一下。code

/**
* SimpleStatementHandler
**/
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
  //mapper 中未指定結果集類型,直接建立Statement對象
  if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
    return connection.createStatement();
  } else {//根據所給定的結果集類型,建立Statement對象
    return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
  }
}

**注:**關於結果集類型可參考 jdbc ResultSetType 說明

/**
* PreparedStatementHandler
**/
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
  //獲得執行的Sql
  String sql = boundSql.getSql();
  //判斷是否有主鍵生成器(insert語句須要)
  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);
    }
  //若是沒有,而且ResultSetType是DEFAULT,根據SQL建立 prepareStatement對象
  } else if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
    return connection.prepareStatement(sql);
  } else {
    return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
  }
}
/**
* CallableStatementHandler
**/
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
   //獲得執行的Sql
  String sql = boundSql.getSql();
  if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
    //若是ResultSetType是DEFAULT,根據SQL建立 prepareStatement對象
    return connection.prepareCall(sql);
  } else {
    return connection.prepareCall(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
  }
}

根據配置,經過instantiateStatement實例化完成相應的statement以後,接下來讓咱們看看 StatementHandle是如何完成Executor給他的任務的。

不管是query 仍是 update,Executor只讓它執行了兩步任務,準備statement,執行statement。以doQuery爲例咱們來看一看。

//準備statement 
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;
  }

能夠看出,在得到數據庫鏈接以後,Handler經過執行prepare方法,建立好相應的statement。

@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
  ErrorContext.instance().sql(boundSql.getSql());
  Statement statement = null;
  try {
    statement = instantiateStatement(connection);
    setStatementTimeout(statement, transactionTimeout);
    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);
  }
}

驚不驚喜,意不意外,prepare方法其實就是執行了instantiateStatement方法,並設置了超時時間和每次獲取數據的數量。

接下來就是進行參數的設置。不一樣StatementHandler經過實現parameterize方法實現不一樣的參數設置方式。

/*
** SimpleStatementHandler.parameterize(Statement statement)
** statement直接執行sql,所以不須要參數設置,此處爲空
*/
@Override
public void parameterize(Statement statement) {
  // N/A
}

/*
** PreparedStatementHandler.parameterize(Statement statement)
** 委託parameterHandler進行參數設置
*/
 @Override
 public void parameterize(Statement statement) throws SQLException {
    parameterHandler.setParameters((PreparedStatement) statement);
 }

/*
** CallableStatementHandler.parameterize(Statement statement)
** 委託parameterHandler進行參數設置
*/
  @Override
  public void parameterize(Statement statement) throws SQLException {
    registerOutputParameters((CallableStatement) statement);
    parameterHandler.setParameters((CallableStatement) statement);
  }

ParameterHandler 有惟一的實現類DefaultParameterHandler,讓咱們看看它是如何實現setParameters方法的。

@Override
public void setParameters(PreparedStatement ps) {
  ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
  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();
        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);
        }
        TypeHandler typeHandler = parameterMapping.getTypeHandler();
        JdbcType jdbcType = parameterMapping.getJdbcType();
        if (value == null && jdbcType == null) {
          jdbcType = configuration.getJdbcTypeForNull();
        }
        try {
          // 委託typeHandler進行參數設置
          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);
        }
      }
    }
  }
}

萬事俱備,只欠東風。一個被安排的明明白白的statement,只須要在適當的時機執行execute() 方法,sql便執行了。至此在經歷了Executor——>StatementHandler這兩個委託者以後,sqlSession終於執行完成了一個SQL語句。PreparedStatementHandler.query()方法爲例:

@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
  PreparedStatement ps = (PreparedStatement) statement;
  //執行sql
  ps.execute();
  //返回結果集
  return resultSetHandler.handleResultSets(ps);
}

關於RoutingStatementHandler

RoutingStatementHandler 做爲 StatementHandler的實現類,卻在實現是沒有一絲一毫本身的實現邏輯,它更像是一個StatementHandler的策略選擇器,根據Statement type 建立不一樣的StatementHandler。全部方法的實現也都是調用相應StatementHandler的具體實現。讓咱們看看它的構造函數:

private final StatementHandler delegate;

public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
  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());
  }
}

至此,關於StatementHandler的介紹就基本上結束了。咱們已經大致上知道一個sql的執行流程:

SqlSession ——>Exeecutor ——>StatementHandler。

然而TypeHandler是如何set參數的?sql執行成功以後resultHandler是如何映射結果集的?這將是咱們接下來主要研究的內容。

相關文章
相關標籤/搜索