美文网首页
Mybatis源码分析4--StatementHandler

Mybatis源码分析4--StatementHandler

作者: zhuke | 来源:发表于2017-09-05 19:31 被阅读0次

    MyBatis sql执行过程如下图所示:

    MyBatis sql执行过程

    SqlSession将执行过程委托给Executor,Executor又将执行过程交给StatementHandler具体执行。

    下面我们对StatementHandler的设计进行具体分析。

    StatementHandler类继承结构
    • SimpleStatementHandler:用于处理Statement对象的数据库操作

    • PreparedStatementHandler:用于处理PreparedStatement对象的数据库操作

    • CallableStatementHandler:用于处理CallableStatement对象的数据库操作(存储过程)

    • RoutingStatementHandler:用于创建上面三种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对象进行数据库操作的相关算法定义。由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不支持?形式的参数占位符,所以其parameterize方法时空的:

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

    PreparedStatementHandler

    PreparedStatementHandler定义了生成一个PrepareStatement对象,和对PrepareStatement对象进行参数填充的方法。

    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 {
        //将参数填充委托给parameterHandler进行处理
        parameterHandler.setParameters((PreparedStatement) statement);
      }
    

    CallableStatementHandler

    CallableStatementHandler定义了生成一个CallableStatement对象,和对CallableStatement对象进行参数填充的方法。

    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的参数进行填充和处理。MyBatis提供了一个默认实现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的创建过程

    Executor每次执行select & update,都会创建一个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) {
        //根据配置的类型创建不同的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参数可以在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类型创建相应的上述StatementHandler类型

    相关文章

      网友评论

          本文标题:Mybatis源码分析4--StatementHandler

          本文链接:https://www.haomeiwen.com/subject/bolpjxtx.html