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

第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 { <> +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 { <> #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 SH-->>Executor: results

3. RoutingStatementHandler路由处理器

3.1 核心实现

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

java 复制代码
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进行路由:

java 复制代码
// 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的基类,提供通用的功能和属性:

java 复制代码
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:

java 复制代码
// 组件创建
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语句:

java 复制代码
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专门处理存储过程调用:

java 复制代码
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语句:

java 复制代码
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执行时间:

java 复制代码
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中配置插件:

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

8.3 测试代码

java 复制代码
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类型

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

观察SQL执行

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

观察参数设置

java 复制代码
// 在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是如何协作的?
相关推荐
lunzi_fly4 小时前
【源码解读之 Mybatis】【核心篇】-- 第6篇:StatementHandler语句处理器
mybatis
optimistic_chen5 小时前
【Java EE进阶 --- SpringBoot】Mybatis操作数据库(基础二)
xml·数据库·spring boot·笔记·java-ee·mybatis
ss27316 小时前
手写MyBatis第107弹:@MapperScan原理与SqlSessionTemplate线程安全机制
java·开发语言·后端·mybatis
二宝15221 小时前
黑马商城day1-MyBatis-Plus
java·开发语言·mybatis
龙猫蓝图2 天前
wrapper+ xml文件进行SQL编写
mybatis
ss2732 天前
手写MyBatis第104弹:SqlSession从工厂构建到执行器选择的深度剖析
java·开发语言·后端·mybatis
一路向北_Coding2 天前
MyBatis Generator让你优雅的写SQL
mysql·mybatis
莫陌尛.2 天前
SSM(Spring+SpringMVC+Mybatis)整合
java·spring·mybatis
2301_797604242 天前
d41:MyBatisPlus入门,注解,配置,条件构造器,自定义SQL,IService
sql·mybatis