第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路由到具体的处理器:
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断点:
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类型:
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语句处理器体系:
- 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是如何协作的?