StatementHandler 语句处理
StatementHandler 是 MyBatis 四大核心组件之一,负责创建 Statement 对象、设置参数、执行 SQL 语句。它是 Executor 和 JDBC Statement 之间的桥梁。
StatementHandler 体系结构
Java
StatementHandler(接口)
├── BaseStatementHandler(抽象基类)
│ ├── SimpleStatementHandler // Statement,无预编译
│ ├── PreparedStatementHandler // PreparedStatement,预编译
│ └── CallableStatementHandler // CallableStatement,存储过程
└── RoutingStatementHandler // 路由处理器(装饰器)
RoutingStatementHandler 路由机制
Java
public class RoutingStatementHandler implements StatementHandler {
private final StatementHandler delegate;
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter,
RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
// 根据 StatementType 创建真正的 StatementHandler
switch (ms.getStatementType()) {
case STATEMENT:
delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case PREPARED:
delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case CALLABLE:
delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
default:
throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
}
}
}
路由模式:RoutingStatementHandler 本身不实现功能,仅根据 StatementType 路由到真正的 Handler,典型的路由器模式。
BaseStatementHandler 核心结构
Java
public abstract class BaseStatementHandler implements StatementHandler {
protected final Configuration configuration;
protected final ObjectFactory objectFactory;
protected final TypeHandlerRegistry typeHandlerRegistry;
protected final Executor executor; // 关联的执行器
protected final MappedStatement mappedStatement; // SQL 映射定义
protected final Object parameterObject; // 传入的参数对象
protected final BoundSql boundSql; // 解析后的 SQL 和参数映射
protected final RowBounds rowBounds; // 分页参数
// 参数处理器(由子类创建)
protected ParameterHandler parameterHandler;
// 结果集处理器(由子类创建)
protected ResultSetHandler resultSetHandler;
@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
Statement statement = null;
try {
// 模板方法:由子类实现具体的实例化逻辑
statement = instantiateStatement(connection);
// 设置超时时间
setStatementTimeout(statement, transactionTimeout);
// 设置 fetchSize
setFetchSize(statement);
return statement;
} catch (SQLException e) {
closeStatement(statement);
throw e;
}
}
// 抽象方法,子类实现
protected abstract Statement instantiateStatement(Connection connection) throws SQLException;
}
创建时机
Java
// 在 Configuration 中创建
public StatementHandler newStatementHandler(Executor executor, MappedStatement ms,
Object parameter, RowBounds rowBounds,
ResultHandler resultHandler, BoundSql boundSql) {
StatementHandler handler = new RoutingStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
// 应用插件拦截
return (StatementHandler) interceptorChain.pluginAll(handler);
}
PreparedStatementHandler 实现
创建 PreparedStatement
Java
public class PreparedStatementHandler extends BaseStatementHandler {
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
String sql = boundSql.getSql();
// 判断是否需要返回主键
if (mappedStatement.getKeyGenerator() != null
&& mappedStatement.getKeyColumns() != null) {
String[] keyColumns = mappedStatement.getKeyColumns();
return connection.prepareStatement(sql, keyColumns);
} else {
// 判断是否需要生成主键
if (mappedStatement.getKeyGenerator() != null) {
return connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
}
// 判断是否是结果集类型(如 REF_CURSOR)
else if (configuration.useGeneratedKeys()) {
return connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
} else {
return connection.prepareStatement(sql);
}
}
}
@Override
public void parameterize(Statement statement) throws SQLException {
// 委托给 ParameterHandler 设置参数
parameterHandler.setParameters((PreparedStatement) statement);
}
}
PreparedStatement 创建策略:根据是否需要返回主键(useGeneratedKeys、keyGenerator)选择不同的 prepareStatement 重载方法。
执行查询
Java
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute(); // 执行 SQL
return resultSetHandler.handleResultSets(ps); // 处理结果集
}
执行更新
Java
@Override
public int update(Statement statement) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
int rows = ps.executeUpdate(); // 执行更新
// 处理主键生成
KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
return rows;
}
SimpleStatementHandler 实现
Java
public class SimpleStatementHandler extends BaseStatementHandler {
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
return connection.createStatement();
}
@Override
public void parameterize(Statement statement) throws SQLException {
// Statement 不需要参数化
}
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
String sql = boundSql.getSql();
statement.execute(sql);
return resultSetHandler.handleResultSets(statement);
}
@Override
public int update(Statement statement) throws SQLException {
String sql = boundSql.getSql();
return statement.executeUpdate(sql);
}
}
特点:使用 Statement 而非 PreparedStatement,SQL 语句在运行时动态拼接,存在 SQL 注入风险。仅适用于不接收外部参数的静态 SQL。
CallableStatementHandler 实现
Java
public class CallableStatementHandler extends BaseStatementHandler {
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
String sql = boundSql.getSql();
return connection.prepareCall(sql);
}
@Override
public void parameterize(Statement statement) throws SQLException {
parameterHandler.setParameters((CallableStatement) statement);
}
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
CallableStatement cs = (CallableStatement) statement;
cs.execute();
return resultSetHandler.handleResultSets(cs);
}
@Override
public int update(Statement statement) throws SQLException {
CallableStatement cs = (CallableStatement) statement;
cs.execute();
return cs.getUpdateCount();
}
}
三种 StatementHandler 对比
| 特性 | SimpleStatementHandler | PreparedStatementHandler | CallableStatementHandler |
|---|---|---|---|
| JDBC类型 | Statement | PreparedStatement | CallableStatement |
| SQL预编译 | 否 | 是 | 是 |
| 防SQL注入 | 否 | 是 | 是 |
| 参数绑定 | 手动拼接 | parameterize() | parameterize() |
| 适用场景 | 静态SQL | 参数化SQL | 存储过程 |
| 性能 | 每次解析SQL | 可复用执行计划 | 可复用执行计划 |
| 使用频率 | 极低 | 最常用 | 较少 |
参数绑定流程
Java
BoundSql 对象:
├── sql: "SELECT * FROM users WHERE age > ? AND name LIKE ?"
├── parameterMappings: List<ParameterMapping>
│ ├── [0]: property="minAge", type=Long, mode=IN
│ └── [1]: property="searchName", type=String, mode=IN
└── parameterObject: UserQueryDTO(minAge=18, searchName="张%")
setParameters 实现
text
// ParameterHandler 实现类
public class DefaultParameterHandler implements ParameterHandler {
private final TypeHandlerRegistry typeHandlerRegistry;
private final Object parameterObject;
private final BoundSql boundSql;
@Override
public void setParameters(PreparedStatement ps) {
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
String propertyName = parameterMapping.getProperty();
// 1. 获取参数值
if (boundSql.hasAdditionalParameter(propertyName)) {
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject; // 简单类型直接使用
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
// 2. 获取 TypeHandler
TypeHandler typeHandler = parameterMapping.getTypeHandler();
JdbcType jdbcType = parameterMapping.getJdbcType();
// 3. 设置参数到 PreparedStatement
typeHandler.setParameter(ps, i + 1, value, jdbcType);
}
}
}
}
}
索引注意:PreparedStatement 参数索引从 1 开始,但 parameterMappings 列表从 0 开始,所以设置时使用
i + 1。
调用链路图
text
Executor.doQuery()
|
+-- 创建 StatementHandler
| |
| +-- new RoutingStatementHandler()
| |
| +-- new PreparedStatementHandler()
|
+-- prepareStatement()
| |
| +-- handler.prepare(connection) → instantiateStatement()
| |
| +-- handler.parameterize(stmt) → parameterHandler.setParameters()
|
+-- handler.query(stmt, resultHandler)
| |
| +-- ps.execute()
| |
| +-- resultSetHandler.handleResultSets()
|
+-- 返回结果
超时与 FetchSize 设置
text
protected void setStatementTimeout(Statement stmt, Integer timeout) throws SQLException {
Integer statementTimeout = mappedStatement.getTimeout();
// 优先级:方法参数 > MappedStatement配置 > Configuration全局配置
Integer actualTimeout = timeout != null ? timeout
: statementTimeout != null ? statementTimeout
: configuration.getDefaultStatementTimeout();
stmt.setQueryTimeout(actualTimeout);
}
protected void setFetchSize(Statement stmt) throws SQLException {
Integer fetchSize = mappedStatement.getFetchSize();
if (fetchSize != null) {
stmt.setFetchSize(fetchSize);
}
}
超时优先级:方法传入 > MappedStatement 配置 > Configuration 全局默认值。
要点总结
- RoutingStatementHandler 是路由器:本身不实现功能,根据 StatementType 路由到真正的 Handler
- PreparedStatementHandler 最常用:支持预编译、参数绑定、防 SQL 注入,是默认选择
- 参数绑定委托给 ParameterHandler:StatementHandler 通过 parameterHandler.setParameters() 完成参数设置
- 参数索引从 1 开始:PreparedStatement 设置参数时,循环索引需要 +1
- 超时设置优先级:方法参数 > MappedStatement.timeout > Configuration.defaultStatementTimeout
- 插件拦截点:StatementHandler 创建后会经过 pluginAll() 处理,可在 prepare、parameterize、query、update 等环节拦截
📝 发现内容有误?点击此处直接编辑