您好,登录后才能下订单哦!
# Mybatis中StatementHandler的作用是什么
## 1. 引言
MyBatis作为一款优秀的持久层框架,其核心设计理念是通过XML或注解将SQL与Java代码解耦。在MyBatis的执行过程中,`StatementHandler`扮演着至关重要的角色。本文将深入剖析`StatementHandler`的设计原理、核心功能及其在SQL执行过程中的关键作用。
## 2. StatementHandler概述
### 2.1 定义与定位
`StatementHandler`是MyBatis四大核心接口之一(其他三个为`Executor`、`ParameterHandler`、`ResultSetHandler`),主要负责:
- JDBC Statement的创建
- 参数设置
- SQL执行
- 结果集处理(委托给`ResultSetHandler`)
```java
public interface StatementHandler {
Statement prepare(Connection connection, Integer transactionTimeout);
void parameterize(Statement statement);
void batch(Statement statement);
int update(Statement statement);
<E> List<E> query(Statement statement, ResultHandler resultHandler);
// 其他方法...
}
SqlSession
└── Executor
└── StatementHandler
├── ParameterHandler
└── ResultSetHandler
StatementHandler
通过prepare()
方法创建Statement对象,处理逻辑包括:
SimpleStatementHandler
(普通Statement)PreparedStatementHandler
(预编译Statement)CallableStatementHandler
(存储过程调用)useGeneratedKeys
选项// 典型实现片段(PreparedStatementHandler)
public Statement prepare(Connection connection, Integer transactionTimeout) {
String sql = boundSql.getSql();
// 处理useGeneratedKeys配置
if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
String[] keyColumnNames = mappedStatement.getKeyColumns();
return connection.prepareStatement(sql, keyColumnNames);
}
return connection.prepareStatement(sql);
}
parameterize()
方法实现动态SQL参数绑定:
TypeHandler
处理Java与JDBC类型转换<if>
,<foreach>
等动态标签生成的参数public void parameterize(Statement statement) {
parameterHandler.setParameters((PreparedStatement) statement);
}
根据操作类型选择不同执行方法:
方法 | 用途 | 典型实现类 |
---|---|---|
update() | 执行INSERT/UPDATE/DELETE | PreparedStatementHandler |
query() | 执行SELECT查询 | RoutingStatementHandler |
batch() | 批量操作 | BatchStatementHandler |
MyBatis通过不同的实现类处理各种场景:
实现类 | 用途 |
---|---|
SimpleStatementHandler | 处理简单无参SQL |
PreparedStatementHandler | 处理预编译SQL(占位符替换) |
CallableStatementHandler | 处理存储过程调用 |
作为门面类,根据MappedStatement的statementType自动路由到具体实现:
public class RoutingStatementHandler implements StatementHandler {
private final StatementHandler delegate;
public RoutingStatementHandler(Executor executor, MappedStatement ms,
Object parameter, RowBounds rowBounds, ResultHandler resultHandler,
BoundSql boundSql) {
switch (ms.getStatementType()) {
case STATEMENT: delegate = new SimpleStatementHandler(...); break;
case PREPARED: delegate = new PreparedStatementHandler(...); break;
case CALLABLE: delegate = new CallableStatementHandler(...); break;
default: throw new ExecutorException("Unknown statement type...");
}
}
}
专用于批量操作的实现,核心逻辑在batch()
方法中:
public void batch(Statement statement) {
PreparedStatement ps = (PreparedStatement) statement;
ps.addBatch(); // 添加到批处理
}
通过动态代理实现插件扩展:
public StatementHandler plugin(StatementHandler target) {
return Plugin.wrap(target, this);
}
@Intercepts({
@Signature(type=StatementHandler.class,
method="prepare",
args={Connection.class,Integer.class})
})
public class MyPlugin implements Interceptor {
@Override
public Object intercept(Invocation invocation) {
// 前置处理
Object result = invocation.proceed();
// 后置处理
return result;
}
}
Executor
通过newStatementHandler()
方法创建StatementHandler
实例:
public StatementHandler newStatementHandler(
Executor executor, MappedStatement ms,
Object parameter, RowBounds rowBounds,
ResultHandler resultHandler, BoundSql boundSql) {
StatementHandler handler = new RoutingStatementHandler(...);
handler = (StatementHandler) interceptorChain.pluginAll(handler);
return handler;
}
参数处理流程:
1. StatementHandler
调用parameterize()
2. 委托给ParameterHandler.setParameters()
3. 使用TypeHandler
完成类型转换
结果集处理流程:
1. StatementHandler
执行query()
2. 获取ResultSet
后委托给ResultSetHandler
3. 最终返回映射后的Java对象
通过动态代理实现关联对象的延迟加载:
public <E> List<E> query(Statement statement, ResultHandler resultHandler) {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
// 交给ResultSetHandler处理,可能返回代理对象
return resultSetHandler.handleResultSets(ps);
}
CallableStatementHandler
专为存储过程设计:
{call proc_name(?,?)}
语法通过Statement
缓存提高性能(需注意连接池配置):
<setting name="defaultStatementTimeout" value="25"/>
<setting name="cacheEnabled" value="true"/>
使用BatchExecutor
配合BatchStatementHandler
:
SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);
try {
for (User user : users) {
session.insert("insertUser", user);
}
session.commit(); // 一次性提交
} finally {
session.close();
}
典型异常:
org.apache.ibatis.type.TypeException: Could not set parameters...
排查步骤: 1. 检查参数类型与TypeHandler匹配 2. 验证#{param}与JavaBean属性名一致 3. 使用日志插件查看真实参数
安全实践:
- 始终使用#{}
而非${}
- 对动态表名/列名做白名单校验
- 实现自定义StatementHandler
进行安全过滤
StatementHandler
作为MyBatis执行过程的核心枢纽,承担着:
1. SQL执行门户:所有数据库操作必经之路
2. 扩展关键点:插件机制的主要拦截目标
3. 性能影响点:Statement的创建/重用直接影响系统性能
4. 安全控制点:SQL注入防御的最后防线
理解其工作原理对于: - 深度定制MyBatis - 性能调优 - 复杂问题排查 都具有重要意义。
附录:关键类图
@startuml
interface StatementHandler {
+prepare()
+parameterize()
+query()
+update()
}
class RoutingStatementHandler {
-delegate: StatementHandler
}
class PreparedStatementHandler {
-parameterHandler: ParameterHandler
-resultSetHandler: ResultSetHandler
}
StatementHandler <|.. RoutingStatementHandler
StatementHandler <|.. PreparedStatementHandler
RoutingStatementHandler o-- PreparedStatementHandler
@enduml
参考文献 1. MyBatis 3.5.x 源码 2. 《MyBatis技术内幕》- 徐郡明 3. MyBatis官方文档 “`
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。