您好,登录后才能下订单哦!
# 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进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。