全部学科
NodeJS全栈
nodejs
Python全栈
python
小程序首页
📅 2026-05-20 8 分钟 ✍️ juanwangdev

StatementHandler 语句处理

StatementHandler 是 MyBatis 四大核心组件之一,负责创建 Statement 对象、设置参数、执行 SQL 语句。它是 Executor 和 JDBC Statement 之间的桥梁。

StatementHandler 体系结构

Java
StatementHandler(接口)
  ├── BaseStatementHandler(抽象基类)
  │     ├── SimpleStatementHandler   // Statement,无预编译
  │     ├── PreparedStatementHandler // PreparedStatement,预编译
  │     └── CallableStatementHandler // CallableStatement,存储过程
  └── RoutingStatementHandler        // 路由处理器(装饰器)

RoutingStatementHandler 路由机制

Java
public class RoutingStatementHandler implements StatementHandler {
  
  private final StatementHandler delegate;
  
  public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, 
                                  RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    // 根据 StatementType 创建真正的 StatementHandler
    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());
    }
  }
}

路由模式:RoutingStatementHandler 本身不实现功能,仅根据 StatementType 路由到真正的 Handler,典型的路由器模式。

BaseStatementHandler 核心结构

Java
public abstract class BaseStatementHandler implements StatementHandler {
  
  protected final Configuration configuration;
  protected final ObjectFactory objectFactory;
  protected final TypeHandlerRegistry typeHandlerRegistry;
  
  protected final Executor executor;           // 关联的执行器
  protected final MappedStatement mappedStatement;  // SQL 映射定义
  
  protected final Object parameterObject;      // 传入的参数对象
  protected final BoundSql boundSql;           // 解析后的 SQL 和参数映射
  protected final RowBounds rowBounds;         // 分页参数
  
  // 参数处理器(由子类创建)
  protected ParameterHandler parameterHandler;
  
  // 结果集处理器(由子类创建)
  protected ResultSetHandler resultSetHandler;
  
  @Override
  public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
    Statement statement = null;
    try {
      // 模板方法:由子类实现具体的实例化逻辑
      statement = instantiateStatement(connection);
      
      // 设置超时时间
      setStatementTimeout(statement, transactionTimeout);
      
      // 设置 fetchSize
      setFetchSize(statement);
      
      return statement;
    } catch (SQLException e) {
      closeStatement(statement);
      throw e;
    }
  }
  
  // 抽象方法,子类实现
  protected abstract Statement instantiateStatement(Connection connection) throws SQLException;
}

创建时机

Java
// 在 Configuration 中创建
public StatementHandler newStatementHandler(Executor executor, MappedStatement ms, 
                                             Object parameter, RowBounds rowBounds, 
                                             ResultHandler resultHandler, BoundSql boundSql) {
  StatementHandler handler = new RoutingStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
  
  // 应用插件拦截
  return (StatementHandler) interceptorChain.pluginAll(handler);
}

PreparedStatementHandler 实现

创建 PreparedStatement

Java
public class PreparedStatementHandler extends BaseStatementHandler {
  
  @Override
  protected Statement instantiateStatement(Connection connection) throws SQLException {
    String sql = boundSql.getSql();
    
    // 判断是否需要返回主键
    if (mappedStatement.getKeyGenerator() != null 
        && mappedStatement.getKeyColumns() != null) {
      String[] keyColumns = mappedStatement.getKeyColumns();
      return connection.prepareStatement(sql, keyColumns);
    } else {
      // 判断是否需要生成主键
      if (mappedStatement.getKeyGenerator() != null) {
        return connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
      }
      // 判断是否是结果集类型(如 REF_CURSOR)
      else if (configuration.useGeneratedKeys()) {
        return connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
      } else {
        return connection.prepareStatement(sql);
      }
    }
  }
  
  @Override
  public void parameterize(Statement statement) throws SQLException {
    // 委托给 ParameterHandler 设置参数
    parameterHandler.setParameters((PreparedStatement) statement);
  }
}

PreparedStatement 创建策略:根据是否需要返回主键(useGeneratedKeys、keyGenerator)选择不同的 prepareStatement 重载方法。

执行查询

Java
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
  PreparedStatement ps = (PreparedStatement) statement;
  ps.execute();  // 执行 SQL
  return resultSetHandler.handleResultSets(ps);  // 处理结果集
}

执行更新

Java
@Override
public int update(Statement statement) throws SQLException {
  PreparedStatement ps = (PreparedStatement) statement;
  int rows = ps.executeUpdate();  // 执行更新
  
  // 处理主键生成
  KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
  keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
  
  return rows;
}

SimpleStatementHandler 实现

Java
public class SimpleStatementHandler extends BaseStatementHandler {
  
  @Override
  protected Statement instantiateStatement(Connection connection) throws SQLException {
    return connection.createStatement();
  }
  
  @Override
  public void parameterize(Statement statement) throws SQLException {
    // Statement 不需要参数化
  }
  
  @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    String sql = boundSql.getSql();
    statement.execute(sql);
    return resultSetHandler.handleResultSets(statement);
  }
  
  @Override
  public int update(Statement statement) throws SQLException {
    String sql = boundSql.getSql();
    return statement.executeUpdate(sql);
  }
}

特点:使用 Statement 而非 PreparedStatement,SQL 语句在运行时动态拼接,存在 SQL 注入风险。仅适用于不接收外部参数的静态 SQL。

CallableStatementHandler 实现

Java
public class CallableStatementHandler extends BaseStatementHandler {
  
  @Override
  protected Statement instantiateStatement(Connection connection) throws SQLException {
    String sql = boundSql.getSql();
    return connection.prepareCall(sql);
  }
  
  @Override
  public void parameterize(Statement statement) throws SQLException {
    parameterHandler.setParameters((CallableStatement) statement);
  }
  
  @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    CallableStatement cs = (CallableStatement) statement;
    cs.execute();
    return resultSetHandler.handleResultSets(cs);
  }
  
  @Override
  public int update(Statement statement) throws SQLException {
    CallableStatement cs = (CallableStatement) statement;
    cs.execute();
    return cs.getUpdateCount();
  }
}

三种 StatementHandler 对比

特性SimpleStatementHandlerPreparedStatementHandlerCallableStatementHandler
JDBC类型StatementPreparedStatementCallableStatement
SQL预编译
防SQL注入
参数绑定手动拼接parameterize()parameterize()
适用场景静态SQL参数化SQL存储过程
性能每次解析SQL可复用执行计划可复用执行计划
使用频率极低最常用较少

参数绑定流程

Java
BoundSql 对象:
├── sql: "SELECT * FROM users WHERE age > ? AND name LIKE ?"
├── parameterMappings: List<ParameterMapping>
│     ├── [0]: property="minAge", type=Long, mode=IN
│     └── [1]: property="searchName", type=String, mode=IN
└── parameterObject: UserQueryDTO(minAge=18, searchName="张%")

setParameters 实现

text
// ParameterHandler 实现类
public class DefaultParameterHandler implements ParameterHandler {
  
  private final TypeHandlerRegistry typeHandlerRegistry;
  private final Object parameterObject;
  private final BoundSql boundSql;
  
  @Override
  public void setParameters(PreparedStatement ps) {
    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();
          
          // 1. 获取参数值
          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);
          }
          
          // 2. 获取 TypeHandler
          TypeHandler typeHandler = parameterMapping.getTypeHandler();
          JdbcType jdbcType = parameterMapping.getJdbcType();
          
          // 3. 设置参数到 PreparedStatement
          typeHandler.setParameter(ps, i + 1, value, jdbcType);
        }
      }
    }
  }
}

索引注意:PreparedStatement 参数索引从 1 开始,但 parameterMappings 列表从 0 开始,所以设置时使用 i + 1

调用链路图

text
Executor.doQuery()
    |
    +-- 创建 StatementHandler
    |       |
    |       +-- new RoutingStatementHandler()
    |             |
    |             +-- new PreparedStatementHandler()
    |
    +-- prepareStatement()
    |       |
    |       +-- handler.prepare(connection)  → instantiateStatement()
    |       |
    |       +-- handler.parameterize(stmt)   → parameterHandler.setParameters()
    |
    +-- handler.query(stmt, resultHandler)
    |       |
    |       +-- ps.execute()
    |       |
    |       +-- resultSetHandler.handleResultSets()
    |
    +-- 返回结果

超时与 FetchSize 设置

text
protected void setStatementTimeout(Statement stmt, Integer timeout) throws SQLException {
  Integer statementTimeout = mappedStatement.getTimeout();
  
  // 优先级:方法参数 > MappedStatement配置 > Configuration全局配置
  Integer actualTimeout = timeout != null ? timeout 
                      : statementTimeout != null ? statementTimeout 
                      : configuration.getDefaultStatementTimeout();
  
  stmt.setQueryTimeout(actualTimeout);
}

protected void setFetchSize(Statement stmt) throws SQLException {
  Integer fetchSize = mappedStatement.getFetchSize();
  
  if (fetchSize != null) {
    stmt.setFetchSize(fetchSize);
  }
}

超时优先级:方法传入 > MappedStatement 配置 > Configuration 全局默认值。

要点总结

  • RoutingStatementHandler 是路由器:本身不实现功能,根据 StatementType 路由到真正的 Handler
  • PreparedStatementHandler 最常用:支持预编译、参数绑定、防 SQL 注入,是默认选择
  • 参数绑定委托给 ParameterHandler:StatementHandler 通过 parameterHandler.setParameters() 完成参数设置
  • 参数索引从 1 开始:PreparedStatement 设置参数时,循环索引需要 +1
  • 超时设置优先级:方法参数 > MappedStatement.timeout > Configuration.defaultStatementTimeout
  • 插件拦截点:StatementHandler 创建后会经过 pluginAll() 处理,可在 prepare、parameterize、query、update 等环节拦截

📝 发现内容有误?点击此处直接编辑

← 上一篇 SqlSessionFactory 构建流程
下一篇 → 慢查询定位与优化
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库