Mybatis源碼分析4--StatementHandler

MyBatis sql執(zhí)行過程如下圖所示:

MyBatis sql執(zhí)行過程

SqlSession將執(zhí)行過程委托給Executor,Executor又將執(zhí)行過程交給StatementHandler具體執(zhí)行。

下面我們對StatementHandler的設計進行具體分析。

StatementHandler類繼承結構
  • SimpleStatementHandler:用于處理Statement對象的數(shù)據庫操作

  • PreparedStatementHandler:用于處理PreparedStatement對象的數(shù)據庫操作

  • CallableStatementHandler:用于處理CallableStatement對象的數(shù)據庫操作(存儲過程)

  • RoutingStatementHandler:用于創(chuàng)建上面三種Handler的策略類


BaseStatementHandler

在BaseStatementHandler中定義了生成Statement對象的基本算法結構,而具體生成Statement的類型和算法,則由子類自行決定:

public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
    ErrorContext.instance().sql(boundSql.getSql());
    Statement statement = null;
    try {
      //由子類自行決定要生成的Statement類型和算法
      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);
    }
  }

SimpleStatementHandler

SimpleStatementHandler是利用Statement對象進行數(shù)據庫操作的相關算法定義。由instantiateStatement方法生成一個Statement對象。

protected Statement instantiateStatement(Connection connection) throws SQLException {
    if (mappedStatement.getResultSetType() != null) {
      return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
    } else {
      return connection.createStatement();
    }
  }

因為Statement不支持?形式的參數(shù)占位符,所以其parameterize方法時空的:

public void parameterize(Statement statement) throws SQLException {
    // N/A
  }

PreparedStatementHandler

PreparedStatementHandler定義了生成一個PrepareStatement對象,和對PrepareStatement對象進行參數(shù)填充的方法。

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);
    }
  }
public void parameterize(Statement statement) throws SQLException {
    //將參數(shù)填充委托給parameterHandler進行處理
    parameterHandler.setParameters((PreparedStatement) statement);
  }

CallableStatementHandler

CallableStatementHandler定義了生成一個CallableStatement對象,和對CallableStatement對象進行參數(shù)填充的方法。

protected Statement instantiateStatement(Connection connection) throws SQLException {
    String sql = boundSql.getSql();
    if (mappedStatement.getResultSetType() != null) {
      return connection.prepareCall(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
    } else {
      return connection.prepareCall(sql);
    }
  }

ParameterHandler

ParameterHandler負責對Statement的參數(shù)進行填充和處理。MyBatis提供了一個默認實現(xiàn)DefaultParameterHandler。

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)) { // issue #448 ask first for additional params
            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.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);
          }
        }
      }
    }
  }

StatementHandler的創(chuàng)建過程

Executor每次執(zhí)行select & update,都會創(chuàng)建一個StatementHandler對象。

public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      //新建一個StatementHandler
      StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.update(stmt);
    } finally {
      closeStatement(stmt);
    }
  }

public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    //根據配置的類型創(chuàng)建不同的StatementHandler
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
  }

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

  }

StatementType參數(shù)可以在SQL Mapper文件中進行配置:

<select id="select" resultType="mybatis.bean.User" statementType="STATEMENT">
        select * from user_info where id = #{id}
    </select>

StatementType有三種不同的類型:

public enum StatementType {
  STATEMENT, PREPARED, CALLABLE
}

默認為PREPARED。


總結

  • SimpleStatementHandler
Statement stm = conn.createStatement()
return stm.execute(sql);
  • PreparedStatementHandler
PreparedStatement pstm = conn.prepareStatement(sql);
pstm.setString(1, "Hello");
return pstm.execute();
  • CallableStatementHandler
CallableStatement cs = conn.prepareCall("{call pr_add(?,?,?)}");
cs.registerOutParameter(3, Types.INTEGER);
cs.setInt(1, 10);
cs.setString(2, "Hello");
cs.execute();
return cs.getInt(3);
  • RoutingStatementHandler

根據mapper文件中配置的type類型創(chuàng)建相應的上述StatementHandler類型

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 1 引言# 本文主要講解JDBC怎么演變到Mybatis的漸變過程,重點講解了為什么要將JDBC封裝成Mybait...
    七寸知架構閱讀 77,557評論 36 979
  • 本節(jié)介紹Statement接口及其子類PreparedStatement和CallableStatement。 它...
    zlb閱讀 1,242評論 0 0
  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,554評論 19 139
  • Java數(shù)據持久化之mybatis 一. mybatis簡介 1.1 原始的JDBC操作: Java 通過 Jav...
    小Q逛逛閱讀 5,403評論 0 16
  • 一 阿黃做夢都想和小魚姑娘在法國的普羅旺斯結婚。他在年少的時候曾在網上看到了普羅旺斯的圖片,那兒簡直就是人間天堂。...
    江中云閱讀 1,311評論 0 0

友情鏈接更多精彩內容