【源码解读之 Mybatis】【核心篇】-- 第6篇:StatementHandler语句处理器

46 阅读16分钟

第6篇:StatementHandler语句处理器

1. 学习目标确认

1.0 第5篇思考题解答

在深入学习StatementHandler语句处理器之前,让我们先回顾并解答第5篇中提出的思考题,这将帮助我们更好地理解StatementHandler在整个架构中的作用。

思考题1:为什么MyBatis要设计多种Executor类型?它们各自的优势是什么?

答案要点

  • SimpleExecutor:简单可靠,每次创建新Statement,适合大多数场景,资源管理清晰
  • ReuseExecutor:重用Statement对象,减少创建开销,适合重复执行相同SQL的场景
  • BatchExecutor:批量执行多个SQL,大幅减少数据库交互次数,适合批量数据操作
  • CachingExecutor:装饰器模式实现二级缓存,提升查询性能,适合读多写少的场景
  • 设计理念:不同场景有不同需求,通过多种执行器满足不同性能要求

StatementHandler的作用:StatementHandler是Executor的核心组件,负责具体的SQL语句预处理、参数设置和结果处理。

思考题2:BaseExecutor的模板方法模式是如何实现的?这种设计有什么优势?

答案要点

  • 模板方法定义:BaseExecutor定义了query()和update()的标准流程,包含缓存检查、错误处理、延迟加载等通用逻辑
  • 抽象方法实现:子类只需实现doQuery()和doUpdate()等具体方法,专注于自己的核心逻辑
  • 优势体现:代码复用、逻辑统一、扩展性强、维护性好
  • 一级缓存管理:BaseExecutor统一管理一级缓存的生命周期和策略

与StatementHandler的协作:BaseExecutor通过StatementHandler执行具体的SQL操作,StatementHandler是模板方法中具体实现的关键组件。

思考题3:CachingExecutor的装饰器模式是如何工作的?与一级缓存有什么区别?

答案要点

  • 装饰器模式:CachingExecutor包装其他执行器,在不修改原执行器的基础上添加缓存功能
  • 二级缓存特点:跨SqlSession共享,支持事务性,配置更灵活
  • 一级缓存特点:SqlSession级别,自动管理,生命周期与SqlSession绑定
  • 事务缓存管理:通过TransactionalCacheManager管理缓存的事务性,确保数据一致性

StatementHandler的执行:CachingExecutor在缓存未命中时,委托给被装饰的执行器,最终通过StatementHandler执行SQL。

思考题4:在什么场景下应该使用BatchExecutor?使用时需要注意什么问题?

答案要点

  • 适用场景:批量插入、更新、删除,大量数据的批量处理,数据迁移等
  • 性能优势:减少网络往返次数,提升批量操作的吞吐量
  • 注意事项:必须调用flushStatements()才能执行,事务边界管理,内存使用控制
  • 使用限制:只支持相同类型的SQL批量操作,需要手动管理批量提交

StatementHandler的批量处理:BatchExecutor通过StatementHandler的batch()方法将多个操作添加到批量队列中。

1.1 本篇学习目标

通过本文你将能够:

  1. 深入理解StatementHandler语句处理器的设计思想和职责分工
  2. 掌握RoutingStatementHandler的路由机制和选择策略
  3. 理解PreparedStatementHandler、CallableStatementHandler、SimpleStatementHandler的具体实现
  4. 掌握SQL语句的预处理、参数设置和结果处理流程
  5. 了解StatementHandler与ParameterHandler、ResultSetHandler的协作关系
  6. 具备自定义StatementHandler扩展开发的能力

2. StatementHandler语句处理器体系总览

2.1 语句处理器继承关系图

classDiagram
    class StatementHandler {
        <<interface>>
        +prepare(Connection, Integer) Statement
        +parameterize(Statement) void
        +batch(Statement) void
        +update(Statement) int
        +query(Statement, ResultHandler) List~E~
        +queryCursor(Statement) Cursor~E~
        +getBoundSql() BoundSql
        +getParameterHandler() ParameterHandler
        +getResultSetHandler() ResultSetHandler
    }
  
    class BaseStatementHandler {
        <<abstract>>
        #configuration Configuration
        #executor Executor
        #mappedStatement MappedStatement
        #parameterObject Object
        #rowBounds RowBounds
        #resultHandler ResultHandler
        #boundSql BoundSql
        #parameterHandler ParameterHandler
        #resultSetHandler ResultSetHandler
        #prepareStatement(Connection, String) PreparedStatement
        #instantiateStatement(Connection) Statement
    }
  
    class RoutingStatementHandler {
        -delegate StatementHandler
        +RoutingStatementHandler(Executor, MappedStatement, Object, RowBounds, ResultHandler, BoundSql)
        +prepare(Connection, Integer) Statement
        +parameterize(Statement) void
        +batch(Statement) void
        +update(Statement) int
        +query(Statement, ResultHandler) List~E~
        +queryCursor(Statement) Cursor~E~
        +getBoundSql() BoundSql
        +getParameterHandler() ParameterHandler
        +getResultSetHandler() ResultSetHandler
    }
  
    class PreparedStatementHandler {
        -preparedStatement PreparedStatement
        +prepare(Connection, Integer) PreparedStatement
        +parameterize(PreparedStatement) void
        +batch(PreparedStatement) void
        +update(PreparedStatement) int
        +query(PreparedStatement, ResultHandler) List~E~
        +queryCursor(PreparedStatement) Cursor~E~
    }
  
    class CallableStatementHandler {
        -callableStatement CallableStatement
        +prepare(Connection, Integer) CallableStatement
        +parameterize(CallableStatement) void
        +batch(CallableStatement) void
        +update(CallableStatement) int
        +query(CallableStatement, ResultHandler) List~E~
        +queryCursor(CallableStatement) Cursor~E~
    }
  
    class SimpleStatementHandler {
        -statement Statement
        +prepare(Connection, Integer) Statement
        +parameterize(Statement) void
        +batch(Statement) void
        +update(Statement) int
        +query(Statement, ResultHandler) List~E~
        +queryCursor(Statement) Cursor~E~
    }
  
    StatementHandler <|.. BaseStatementHandler
    StatementHandler <|.. RoutingStatementHandler
    BaseStatementHandler <|-- PreparedStatementHandler
    BaseStatementHandler <|-- CallableStatementHandler
    BaseStatementHandler <|-- SimpleStatementHandler
    RoutingStatementHandler --> StatementHandler : delegates to

2.2 语句处理器职责分工

处理器类型核心职责适用场景特点
RoutingStatementHandler路由选择具体的StatementHandler所有场景的入口根据StatementType路由到具体实现
PreparedStatementHandler处理PreparedStatement参数化查询支持参数绑定,防止SQL注入
CallableStatementHandler处理CallableStatement存储过程调用支持输入输出参数
SimpleStatementHandler处理Statement静态SQL简单直接,无参数绑定

2.3 语句处理器协作关系

sequenceDiagram
    participant Executor as Executor
    participant SH as StatementHandler
    participant PH as ParameterHandler
    participant RSH as ResultSetHandler
    participant DB as Database
  
    Executor->>SH: newStatementHandler(ms, param, rowBounds, handler, boundSql)
    SH->>SH: prepare(connection, timeout)
    SH->>DB: create Statement
    DB-->>SH: Statement
  
    SH->>PH: parameterize(statement)
    PH->>DB: set parameters
    DB-->>PH: parameters set
  
    SH->>DB: execute query/update
    DB-->>SH: ResultSet/UpdateCount
  
    SH->>RSH: handleResultSets(statement)
    RSH-->>SH: List<Object>
    SH-->>Executor: results

3. RoutingStatementHandler路由处理器

3.1 核心实现

RoutingStatementHandler是StatementHandler的入口,负责根据StatementType路由到具体的处理器:

package org.apache.ibatis.executor.statement;

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.StatementType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.sql.Connection;
import java.sql.Statement;
import java.util.List;

/**
 * 路由语句处理器:根据StatementType路由到具体的StatementHandler实现
 * 这是StatementHandler的入口,采用策略模式
 */
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class RoutingStatementHandler implements StatementHandler {

    private final StatementHandler delegate;

    /**
     * 构造方法:根据StatementType创建对应的StatementHandler
     */
    public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {

        // 根据StatementType选择具体的StatementHandler实现
        switch (ms.getStatementType()) {
            case STATEMENT:
                delegate = new SimpleStatementHandler(executor, ms, parameterObject, rowBounds, resultHandler, boundSql);
                break;
            case PREPARED:
                delegate = new PreparedStatementHandler(executor, ms, parameterObject, rowBounds, resultHandler, boundSql);
                break;
            case CALLABLE:
                delegate = new CallableStatementHandler(executor, ms, parameterObject, rowBounds, resultHandler, boundSql);
                break;
            default:
                throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
        }
    }

    @Override
    public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
        return delegate.prepare(connection, transactionTimeout);
    }

    @Override
    public void parameterize(Statement statement) throws SQLException {
        delegate.parameterize(statement);
    }

    @Override
    public void batch(Statement statement) throws SQLException {
        delegate.batch(statement);
    }

    @Override
    public int update(Statement statement) throws SQLException {
        return delegate.update(statement);
    }

    @Override
    public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        return delegate.query(statement, resultHandler);
    }

    @Override
    public <E> Cursor<E> queryCursor(Statement statement) throws SQLException {
        return delegate.queryCursor(statement);
    }

    @Override
    public BoundSql getBoundSql() {
        return delegate.getBoundSql();
    }

    @Override
    public ParameterHandler getParameterHandler() {
        return delegate.getParameterHandler();
    }

    @Override
    public ResultSetHandler getResultSetHandler() {
        return delegate.getResultSetHandler();
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }
}

3.2 路由策略

RoutingStatementHandler根据MappedStatement中的StatementType进行路由:

// StatementType枚举定义
public enum StatementType {
    STATEMENT,    // 对应Statement(静态SQL)
    PREPARED,     // 对应PreparedStatement(参数化SQL)
    CALLABLE      // 对应CallableStatement(存储过程)
}

// 路由逻辑
switch (ms.getStatementType()) {
    case STATEMENT:
        // 使用SimpleStatementHandler处理静态SQL
        delegate = new SimpleStatementHandler(...);
        break;
    case PREPARED:
        // 使用PreparedStatementHandler处理参数化SQL(默认)
        delegate = new PreparedStatementHandler(...);
        break;
    case CALLABLE:
        // 使用CallableStatementHandler处理存储过程
        delegate = new CallableStatementHandler(...);
        break;
}

4. BaseStatementHandler抽象基类

4.1 核心实现

BaseStatementHandler是所有具体StatementHandler的基类,提供通用的功能和属性:

package org.apache.ibatis.executor.statement;

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.ParameterMode;
import org.apache.ibatis.mapping.StatementType;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.type.TypeHandlerRegistry;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;

/**
 * 语句处理器抽象基类
 * 提供StatementHandler的通用功能和属性管理
 */
public abstract class BaseStatementHandler implements StatementHandler {

    // MyBatis全局配置对象
    protected final Configuration configuration;
    // 执行器,用于执行延迟加载等操作
    protected final Executor executor;
    // SQL映射语句,包含SQL和结果映射信息
    protected final MappedStatement mappedStatement;
    // 参数对象,传递给SQL的参数
    protected final Object parameterObject;
    // 分页参数
    protected final RowBounds rowBounds;
    // 结果处理器,用于自定义结果处理
    protected final ResultHandler resultHandler;
    // 绑定的SQL对象,包含最终执行的SQL和参数映射
    protected final BoundSql boundSql;
    // 参数处理器,负责设置SQL参数
    protected final ParameterHandler parameterHandler;
    // 结果集处理器,负责处理查询结果
    protected final ResultSetHandler resultSetHandler;

    /**
     * 构造方法
     */
    public BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        this.configuration = mappedStatement.getConfiguration();
        this.executor = executor;
        this.mappedStatement = mappedStatement;
        this.parameterObject = parameterObject;
        this.rowBounds = rowBounds;
        this.resultHandler = resultHandler;
        this.boundSql = boundSql;
      
        // 创建参数处理器
        this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
      
        // 创建结果集处理器
        this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
    }

    /**
     * 准备Statement对象
     * 子类实现具体的Statement创建逻辑
     */
    @Override
    public abstract Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException;

    /**
     * 设置参数
     * 委托给ParameterHandler处理
     */
    @Override
    public void parameterize(Statement statement) throws SQLException {
        parameterHandler.setParameters(statement);
    }

    /**
     * 批量操作
     * 委托给ParameterHandler处理
     */
    @Override
    public void batch(Statement statement) throws SQLException {
        parameterHandler.batch(statement);
    }

    /**
     * 执行更新操作
     * 委托给具体的Statement执行
     */
    @Override
    public int update(Statement statement) throws SQLException {
        PreparedStatement ps = (PreparedStatement) statement;
        ps.execute();
        int rows = ps.getUpdateCount();
        Object parameterObject = boundSql.getParameterObject();
        KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
        keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
        return rows;
    }

    /**
     * 执行查询操作
     * 委托给ResultSetHandler处理结果集
     */
    @Override
    public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        PreparedStatement ps = (PreparedStatement) statement;
        ps.execute();
        return resultSetHandler.handleResultSets(ps);
    }

    /**
     * 执行游标查询
     * 委托给ResultSetHandler处理
     */
    @Override
    public <E> Cursor<E> queryCursor(Statement statement) throws SQLException {
        PreparedStatement ps = (PreparedStatement) statement;
        ps.execute();
        return resultSetHandler.handleCursorResultSets(ps);
    }

    @Override
    public BoundSql getBoundSql() {
        return boundSql;
    }

    @Override
    public ParameterHandler getParameterHandler() {
        return parameterHandler;
    }

    @Override
    public ResultSetHandler getResultSetHandler() {
        return resultSetHandler;
    }

    /**
     * 准备PreparedStatement对象
     * 子类可以调用此方法创建PreparedStatement
     */
    protected PreparedStatement prepareStatement(Connection connection, String sql) throws SQLException {
        String mappedStatementId = mappedStatement.getId();
        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);
        }
    }

    /**
     * 实例化Statement对象
     * 子类实现具体的Statement创建逻辑
     */
    protected abstract Statement instantiateStatement(Connection connection) throws SQLException;
}

4.2 组件协作

BaseStatementHandler通过组合模式管理ParameterHandler和ResultSetHandler:

// 组件创建
this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);

// 委托执行
@Override
public void parameterize(Statement statement) throws SQLException {
    parameterHandler.setParameters(statement);  // 委托给ParameterHandler
}

@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    return resultSetHandler.handleResultSets(ps);  // 委托给ResultSetHandler
}

5. PreparedStatementHandler预编译处理器

5.1 核心实现

PreparedStatementHandler是最常用的StatementHandler,处理参数化的SQL语句:

package org.apache.ibatis.executor.statement;

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;

/**
 * 预编译语句处理器
 * 处理PreparedStatement,支持参数绑定,防止SQL注入
 * 这是MyBatis默认使用的StatementHandler
 */
public class PreparedStatementHandler extends BaseStatementHandler {

    /**
     * 构造方法
     */
    public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        super(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
    }

    /**
     * 准备PreparedStatement对象
     */
    @Override
    public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
        ErrorContext.instance().sql(boundSql.getSql());
        PreparedStatement statement = null;
        try {
            // 创建PreparedStatement对象
            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);
        }
    }

    /**
     * 实例化PreparedStatement对象
     */
    @Override
    protected Statement instantiateStatement(Connection connection) throws SQLException {
        String sql = boundSql.getSql();
      
        // 根据KeyGenerator类型创建不同的PreparedStatement
        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 {
            // 普通PreparedStatement
            return connection.prepareStatement(sql);
        }
    }

    /**
     * 设置参数
     * 委托给ParameterHandler处理
     */
    @Override
    public void parameterize(Statement statement) throws SQLException {
        parameterHandler.setParameters((PreparedStatement) statement);
    }

    /**
     * 批量操作
     */
    @Override
    public void batch(Statement statement) throws SQLException {
        PreparedStatement ps = (PreparedStatement) statement;
        ps.addBatch();
    }

    /**
     * 执行更新操作
     */
    @Override
    public int update(Statement statement) throws SQLException {
        PreparedStatement ps = (PreparedStatement) statement;
        ps.execute();
        int rows = ps.getUpdateCount();
        Object parameterObject = boundSql.getParameterObject();
        KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
      
        // 处理主键生成
        keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
      
        return rows;
    }

    /**
     * 执行查询操作
     */
    @Override
    public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        PreparedStatement ps = (PreparedStatement) statement;
        ps.execute();
        return resultSetHandler.handleResultSets(ps);
    }

    /**
     * 执行游标查询
     */
    @Override
    public <E> Cursor<E> queryCursor(Statement statement) throws SQLException {
        PreparedStatement ps = (PreparedStatement) statement;
        ps.execute();
        return resultSetHandler.handleCursorResultSets(ps);
    }

    /**
     * 设置Statement超时时间
     */
    private void setStatementTimeout(Statement stmt, Integer transactionTimeout) throws SQLException {
        Integer queryTimeout = null;
        if (mappedStatement.getTimeout() != null) {
            queryTimeout = mappedStatement.getTimeout();
        } else if (configuration.getDefaultStatementTimeout() != null) {
            queryTimeout = configuration.getDefaultStatementTimeout();
        }
        if (queryTimeout != null) {
            stmt.setQueryTimeout(queryTimeout);
        }
        StatementUtil.applyTransactionTimeout(stmt, transactionTimeout);
    }

    /**
     * 设置获取大小
     */
    private void setFetchSize(Statement stmt) throws SQLException {
        Integer fetchSize = mappedStatement.getFetchSize();
        if (fetchSize != null) {
            stmt.setFetchSize(fetchSize);
            return;
        }
        Integer defaultFetchSize = configuration.getDefaultFetchSize();
        if (defaultFetchSize != null) {
            stmt.setFetchSize(defaultFetchSize);
        }
    }
}

5.2 优势特点

安全性

  • 参数绑定:使用PreparedStatement的参数绑定机制,有效防止SQL注入
  • 类型安全:通过ParameterHandler进行类型转换和参数设置

性能优化

  • SQL预编译:SQL语句在数据库端预编译,提升执行效率
  • 参数复用:相同SQL结构的查询可以复用预编译的Statement

功能丰富

  • 主键生成:支持自动生成主键和获取生成的主键
  • 结果集控制:支持结果集类型和并发模式设置
  • 超时控制:支持查询超时和事务超时设置

6. CallableStatementHandler存储过程处理器

6.1 核心实现

CallableStatementHandler专门处理存储过程调用:

package org.apache.ibatis.executor.statement;

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;

/**
 * 存储过程语句处理器
 * 处理CallableStatement,支持存储过程调用
 * 支持输入参数、输出参数和输入输出参数
 */
public class CallableStatementHandler extends BaseStatementHandler {

    /**
     * 构造方法
     */
    public CallableStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        super(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
    }

    /**
     * 准备CallableStatement对象
     */
    @Override
    public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
        ErrorContext.instance().sql(boundSql.getSql());
        CallableStatement statement = null;
        try {
            // 创建CallableStatement对象
            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);
        }
    }

    /**
     * 实例化CallableStatement对象
     */
    @Override
    protected Statement instantiateStatement(Connection connection) throws SQLException {
        String sql = boundSql.getSql();
      
        // 创建CallableStatement
        if (mappedStatement.getResultSetType() != null) {
            return connection.prepareCall(sql, 
                mappedStatement.getResultSetType().getValue(), 
                ResultSet.CONCUR_READ_ONLY);
        } else {
            return connection.prepareCall(sql);
        }
    }

    /**
     * 设置参数
     * 存储过程支持输入、输出和输入输出参数
     */
    @Override
    public void parameterize(Statement statement) throws SQLException {
        // 注册输出参数
        registerOutputParameters((CallableStatement) statement);
      
        // 设置输入参数
        parameterHandler.setParameters((CallableStatement) statement);
    }

    /**
     * 批量操作
     * 存储过程通常不支持批量操作
     */
    @Override
    public void batch(Statement statement) throws SQLException {
        CallableStatement cs = (CallableStatement) statement;
        cs.addBatch();
    }

    /**
     * 执行更新操作
     */
    @Override
    public int update(Statement statement) throws SQLException {
        CallableStatement cs = (CallableStatement) statement;
        cs.execute();
        int rows = cs.getUpdateCount();
      
        // 处理输出参数
        resultSetHandler.handleOutputParameters(cs);
      
        return rows;
    }

    /**
     * 执行查询操作
     */
    @Override
    public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        CallableStatement cs = (CallableStatement) statement;
        cs.execute();
        List<E> resultList = resultSetHandler.handleResultSets(cs);
      
        // 处理输出参数
        resultSetHandler.handleOutputParameters(cs);
      
        return resultList;
    }

    /**
     * 执行游标查询
     */
    @Override
    public <E> Cursor<E> queryCursor(Statement statement) throws SQLException {
        CallableStatement cs = (CallableStatement) statement;
        cs.execute();
        Cursor<E> cursor = resultSetHandler.handleCursorResultSets(cs);
      
        // 处理输出参数
        resultSetHandler.handleOutputParameters(cs);
      
        return cursor;
    }

    /**
     * 注册输出参数
     * 根据参数映射配置注册输出参数的类型
     */
    private void registerOutputParameters(CallableStatement cs) throws SQLException {
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        for (int i = 0; i < parameterMappings.size(); i++) {
            ParameterMapping parameterMapping = parameterMappings.get(i);
            if (parameterMapping.getMode() == ParameterMode.OUT || parameterMapping.getMode() == ParameterMode.INOUT) {
                if (null != parameterMapping.getJdbcType()) {
                    if (parameterMapping.getNumericScale() != null && 
                        (parameterMapping.getJdbcType() == JdbcType.NUMERIC || parameterMapping.getJdbcType() == JdbcType.DECIMAL)) {
                        cs.registerOutParameter(i + 1, parameterMapping.getJdbcType().TYPE_CODE, parameterMapping.getNumericScale());
                    } else {
                        cs.registerOutParameter(i + 1, parameterMapping.getJdbcType().TYPE_CODE);
                    }
                } else {
                    cs.registerOutParameter(i + 1, parameterMapping.getJavaType());
                }
            }
        }
    }
}

6.2 存储过程特点

参数支持

  • 输入参数:传递数据给存储过程
  • 输出参数:从存储过程获取数据
  • 输入输出参数:既输入又输出的参数

结果处理

  • 结果集:存储过程可以返回多个结果集
  • 输出参数:通过OUT参数返回数据
  • 返回值:存储过程的执行状态

7. SimpleStatementHandler简单处理器

7.1 核心实现

SimpleStatementHandler处理静态SQL语句:

package org.apache.ibatis.executor.statement;

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;

/**
 * 简单语句处理器
 * 处理Statement,用于静态SQL语句
 * 不支持参数绑定,适用于简单的静态SQL
 */
public class SimpleStatementHandler extends BaseStatementHandler {

    /**
     * 构造方法
     */
    public SimpleStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        super(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
    }

    /**
     * 准备Statement对象
     */
    @Override
    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);
        }
    }

    /**
     * 实例化Statement对象
     */
    @Override
    protected Statement instantiateStatement(Connection connection) throws SQLException {
        // 创建普通Statement
        if (mappedStatement.getResultSetType() != null) {
            return connection.createStatement(
                mappedStatement.getResultSetType().getValue(), 
                ResultSet.CONCUR_READ_ONLY);
        } else {
            return connection.createStatement();
        }
    }

    /**
     * 设置参数
     * Statement不支持参数绑定,直接执行SQL
     */
    @Override
    public void parameterize(Statement statement) throws SQLException {
        // Statement不支持参数化,无需设置参数
        // 参数已经直接拼接到SQL中
    }

    /**
     * 批量操作
     */
    @Override
    public void batch(Statement statement) throws SQLException {
        Statement stmt = (Statement) statement;
        stmt.addBatch(boundSql.getSql());
    }

    /**
     * 执行更新操作
     */
    @Override
    public int update(Statement statement) throws SQLException {
        Statement stmt = (Statement) statement;
        stmt.execute(boundSql.getSql());
        int rows = stmt.getUpdateCount();
        Object parameterObject = boundSql.getParameterObject();
        KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
      
        // 处理主键生成
        keyGenerator.processAfter(executor, mappedStatement, stmt, parameterObject);
      
        return rows;
    }

    /**
     * 执行查询操作
     */
    @Override
    public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        Statement stmt = (Statement) statement;
        stmt.execute(boundSql.getSql());
        return resultSetHandler.handleResultSets(stmt);
    }

    /**
     * 执行游标查询
     */
    @Override
    public <E> Cursor<E> queryCursor(Statement statement) throws SQLException {
        Statement stmt = (Statement) statement;
        stmt.execute(boundSql.getSql());
        return resultSetHandler.handleCursorResultSets(stmt);
    }
}

7.2 适用场景

优点

  • 简单直接:无需参数处理,直接执行SQL
  • 性能开销小:没有参数绑定和类型转换开销
  • 灵活性高:支持任意复杂的静态SQL

缺点

  • SQL注入风险:不支持参数绑定,存在SQL注入风险
  • 类型不安全:没有类型检查和转换
  • 维护困难:参数直接拼接在SQL中,不易维护

适用场景

  • DDL语句:CREATE、ALTER、DROP等
  • 简单查询:不需要参数的静态查询
  • 批量操作:大量相同结构的操作

8. 实践案例:自定义StatementHandler

8.1 性能监控StatementHandler

让我们创建一个性能监控StatementHandler,记录SQL执行时间:

package com.example.statement;

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.sql.Statement;
import java.util.List;
import java.util.Properties;

/**
 * 性能监控StatementHandler插件
 * 记录SQL执行时间和执行次数
 */
@Intercepts({
    @Signature(type = org.apache.ibatis.executor.statement.StatementHandler.class, 
               method = "query", 
               args = {Statement.class, ResultHandler.class}),
    @Signature(type = org.apache.ibatis.executor.statement.StatementHandler.class, 
               method = "update", 
               args = {Statement.class})
})
public class PerformanceMonitorStatementHandler implements Interceptor {
  
    private long slowQueryThreshold = 1000; // 慢查询阈值,默认1秒
    private boolean enableStatistics = true; // 是否启用统计
  
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        long startTime = System.currentTimeMillis();
        String methodName = invocation.getMethod().getName();
      
        try {
            // 执行原始方法
            Object result = invocation.proceed();
          
            // 计算执行时间
            long endTime = System.currentTimeMillis();
            long executionTime = endTime - startTime;
          
            // 获取SQL信息
            org.apache.ibatis.executor.statement.StatementHandler statementHandler = 
                (org.apache.ibatis.executor.statement.StatementHandler) invocation.getTarget();
            BoundSql boundSql = statementHandler.getBoundSql();
            String sql = boundSql.getSql();
          
            // 记录执行信息
            recordExecution(methodName, sql, executionTime, result);
          
            return result;
          
        } catch (Throwable e) {
            // 记录异常信息
            long endTime = System.currentTimeMillis();
            long executionTime = endTime - startTime;
            recordException(invocation.getMethod().getName(), executionTime, e);
            throw e;
        }
    }
  
    /**
     * 记录执行信息
     */
    private void recordExecution(String methodName, String sql, long executionTime, Object result) {
        // 慢查询检测
        if (executionTime > slowQueryThreshold) {
            System.out.println(String.format("SLOW QUERY DETECTED: %s executed in %d ms", 
                methodName, executionTime));
            System.out.println("SQL: " + sql);
        }
      
        // 统计信息
        if (enableStatistics) {
            System.out.println(String.format("SQL Execution: %s completed in %d ms", 
                methodName, executionTime));
          
            // 记录结果信息
            if ("query".equals(methodName) && result instanceof List) {
                List<?> resultList = (List<?>) result;
                System.out.println("Result count: " + resultList.size());
            } else if ("update".equals(methodName)) {
                System.out.println("Affected rows: " + result);
            }
        }
    }
  
    /**
     * 记录异常信息
     */
    private void recordException(String methodName, long executionTime, Throwable e) {
        System.err.println(String.format("SQL Execution ERROR: %s failed after %d ms", 
            methodName, executionTime));
        System.err.println("Error: " + e.getMessage());
    }
  
    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }
  
    @Override
    public void setProperties(Properties properties) {
        String threshold = properties.getProperty("slowQueryThreshold");
        if (threshold != null) {
            this.slowQueryThreshold = Long.parseLong(threshold);
        }
      
        String statistics = properties.getProperty("enableStatistics");
        if (statistics != null) {
            this.enableStatistics = Boolean.parseBoolean(statistics);
        }
    }
}

8.2 配置使用

在mybatis-config.xml中配置插件:

<configuration>
    <plugins>
        <plugin interceptor="com.example.statement.PerformanceMonitorStatementHandler">
            <property name="slowQueryThreshold" value="500"/>
            <property name="enableStatistics" value="true"/>
        </plugin>
    </plugins>
</configuration>

8.3 测试代码

package com.example;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.InputStream;

/**
 * StatementHandler测试示例
 * 展示不同类型StatementHandler的使用
 */
public class StatementHandlerExample {
    public static void main(String[] args) throws Exception {
        // 加载MyBatis配置文件
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
      
        // 测试不同类型的StatementHandler
        testPreparedStatementHandler(factory);
        testSimpleStatementHandler(factory);
        testCallableStatementHandler(factory);
    }
  
    /**
     * 测试PreparedStatementHandler(默认)
     * 特点:参数化SQL,防止SQL注入
     */
    private static void testPreparedStatementHandler(SqlSessionFactory factory) {
        System.out.println("=== 测试 PreparedStatementHandler ===");
      
        try (SqlSession session = factory.openSession()) {
            UserMapper mapper = session.getMapper(UserMapper.class);
          
            // 参数化查询
            System.out.println(">>> 参数化查询");
            User user = mapper.findById(1L);
            System.out.println("查询结果: " + user);
          
            // 参数化更新
            System.out.println(">>> 参数化更新");
            user.setEmail("newemail@example.com");
            int updateCount = mapper.updateUser(user);
            System.out.println("更新影响行数: " + updateCount);
          
            System.out.println("注意:PreparedStatementHandler 是默认的处理器,支持参数绑定");
        }
    }
  
    /**
     * 测试SimpleStatementHandler
     * 特点:静态SQL,直接执行
     */
    private static void testSimpleStatementHandler(SqlSessionFactory factory) {
        System.out.println("=== 测试 SimpleStatementHandler ===");
      
        try (SqlSession session = factory.openSession()) {
            // 使用StatementType.STATEMENT的映射
            System.out.println(">>> 静态SQL查询");
            User user = session.selectOne("com.example.UserMapper.findUserByStaticSql", 1L);
            System.out.println("查询结果: " + user);
          
            System.out.println("注意:SimpleStatementHandler 用于静态SQL,不支持参数绑定");
        }
    }
  
    /**
     * 测试CallableStatementHandler
     * 特点:存储过程调用,支持输入输出参数
     */
    private static void testCallableStatementHandler(SqlSessionFactory factory) {
        System.out.println("=== 测试 CallableStatementHandler ===");
      
        try (SqlSession session = factory.openSession()) {
            // 使用StatementType.CALLABLE的映射
            System.out.println(">>> 存储过程调用");
          
            // 调用存储过程
            java.util.Map<String, Object> params = new java.util.HashMap<>();
            params.put("userId", 1L);
            params.put("userName", null); // 输出参数
          
            session.selectOne("com.example.UserMapper.getUserByIdProc", params);
          
            System.out.println("存储过程执行结果: " + params);
          
            System.out.println("注意:CallableStatementHandler 用于存储过程调用,支持输出参数");
        }
    }
}

9. 源码调试指导

9.1 关键断点位置

RoutingStatementHandler断点

  1. RoutingStatementHandler.RoutingStatementHandler() - 路由选择逻辑
  2. RoutingStatementHandler.prepare() - 委托给具体处理器

BaseStatementHandler断点

  1. BaseStatementHandler.parameterize() - 参数设置委托
  2. BaseStatementHandler.query() - 查询执行流程
  3. BaseStatementHandler.update() - 更新执行流程

PreparedStatementHandler断点

  1. PreparedStatementHandler.instantiateStatement() - PreparedStatement创建
  2. PreparedStatementHandler.prepare() - Statement准备过程

CallableStatementHandler断点

  1. CallableStatementHandler.registerOutputParameters() - 输出参数注册
  2. CallableStatementHandler.query() - 存储过程查询执行

9.2 调试技巧

观察Statement类型

// 在StatementHandler创建时观察类型
StatementHandler handler = configuration.newStatementHandler(
    executor, ms, parameter, rowBounds, resultHandler, boundSql);
System.out.println("StatementHandler类型: " + handler.getClass().getSimpleName());

观察SQL执行

// 在prepare方法中观察SQL
Statement stmt = handler.prepare(connection, timeout);
System.out.println("准备执行的SQL: " + boundSql.getSql());

观察参数设置

// 在parameterize方法中观察参数设置
handler.parameterize(stmt);
// 断点观察ParameterHandler.setParameters()方法

10. 易错与排错清单

10.1 常见问题

问题原因解决方案
SQL注入使用SimpleStatementHandler处理用户输入优先使用PreparedStatementHandler
参数绑定失败参数映射配置错误检查ParameterMapping配置
存储过程调用失败输出参数未正确注册确保正确配置OUT参数类型
批量操作不生效忘记调用batch()方法确保在批量操作中调用batch()
Statement泄漏忘记关闭Statement使用try-with-resources或确保关闭

10.2 性能优化建议

  • 选择合适处理器:根据SQL类型选择合适的StatementHandler
  • 参数化查询:优先使用PreparedStatementHandler防止SQL注入
  • 批量操作优化:大量操作使用批量模式
  • 连接池配置:合理配置数据库连接池参数
  • 超时设置:合理设置查询超时和事务超时

11. 小结

通过本文的学习,我们深入了解了MyBatis StatementHandler语句处理器体系:

  1. RoutingStatementHandler:路由处理器,根据StatementType选择具体的处理器实现
  2. BaseStatementHandler:抽象基类,提供通用的功能和组件管理
  3. PreparedStatementHandler:预编译处理器,支持参数绑定,防止SQL注入
  4. CallableStatementHandler:存储过程处理器,支持输入输出参数
  5. SimpleStatementHandler:简单处理器,处理静态SQL语句

重要提示:StatementHandler是MyBatis执行层的重要组成部分,负责SQL语句的预处理、参数设置和结果处理。理解StatementHandler的工作机制对于掌握MyBatis的SQL执行流程至关重要。

在下一篇文章中,我们将深入分析ParameterHandler参数处理机制,了解SQL参数的设置和类型转换过程。


思考题

  1. 为什么MyBatis要设计RoutingStatementHandler?直接使用具体的StatementHandler不行吗?
  2. PreparedStatementHandler相比SimpleStatementHandler有什么优势?为什么是默认选择?
  3. CallableStatementHandler如何处理存储过程的输出参数?与普通查询有什么区别?
  4. StatementHandler与ParameterHandler、ResultSetHandler是如何协作的?