第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 本篇学习目标
通过本文你将能够:
- 深入理解StatementHandler语句处理器的设计思想和职责分工
- 掌握RoutingStatementHandler的路由机制和选择策略
- 理解PreparedStatementHandler、CallableStatementHandler、SimpleStatementHandler的具体实现
- 掌握SQL语句的预处理、参数设置和结果处理流程
- 了解StatementHandler与ParameterHandler、ResultSetHandler的协作关系
- 具备自定义StatementHandler扩展开发的能力
2. StatementHandler语句处理器体系总览
2.1 语句处理器继承关系图
2.2 语句处理器职责分工
| 处理器类型 | 核心职责 | 适用场景 | 特点 |
|---|---|---|---|
| RoutingStatementHandler | 路由选择具体的StatementHandler | 所有场景的入口 | 根据StatementType路由到具体实现 |
| PreparedStatementHandler | 处理PreparedStatement | 参数化查询 | 支持参数绑定,防止SQL注入 |
| CallableStatementHandler | 处理CallableStatement | 存储过程调用 | 支持输入输出参数 |
| SimpleStatementHandler | 处理Statement | 静态SQL | 简单直接,无参数绑定 |
2.3 语句处理器协作关系
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断点:
RoutingStatementHandler.RoutingStatementHandler()- 路由选择逻辑RoutingStatementHandler.prepare()- 委托给具体处理器
BaseStatementHandler断点:
BaseStatementHandler.parameterize()- 参数设置委托BaseStatementHandler.query()- 查询执行流程BaseStatementHandler.update()- 更新执行流程
PreparedStatementHandler断点:
PreparedStatementHandler.instantiateStatement()- PreparedStatement创建PreparedStatementHandler.prepare()- Statement准备过程
CallableStatementHandler断点:
CallableStatementHandler.registerOutputParameters()- 输出参数注册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语句处理器体系:
- RoutingStatementHandler:路由处理器,根据StatementType选择具体的处理器实现
- BaseStatementHandler:抽象基类,提供通用的功能和组件管理
- PreparedStatementHandler:预编译处理器,支持参数绑定,防止SQL注入
- CallableStatementHandler:存储过程处理器,支持输入输出参数
- SimpleStatementHandler:简单处理器,处理静态SQL语句
重要提示:StatementHandler是MyBatis执行层的重要组成部分,负责SQL语句的预处理、参数设置和结果处理。理解StatementHandler的工作机制对于掌握MyBatis的SQL执行流程至关重要。
在下一篇文章中,我们将深入分析ParameterHandler参数处理机制,了解SQL参数的设置和类型转换过程。
思考题:
- 为什么MyBatis要设计RoutingStatementHandler?直接使用具体的StatementHandler不行吗?
- PreparedStatementHandler相比SimpleStatementHandler有什么优势?为什么是默认选择?
- CallableStatementHandler如何处理存储过程的输出参数?与普通查询有什么区别?
- StatementHandler与ParameterHandler、ResultSetHandler是如何协作的?