Mybatis StatementHandler的使用小结

 更新时间:2026年03月30日 08:33:30   作者:灵魂猎手  
本文主要介绍了Mybatis StatementHandler的使用小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

一、从Executor说起

之前着重讲解了Executor实现的一二级缓存功能,现在我们回到Executor的核心的执行SQL的能力,以SimpleExecutor为例:

@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
  Statement stmt = null;
  try {
    Configuration configuration = ms.getConfiguration();
    // 创建 StatementHandler
    StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
    // 准备 Statement
    stmt = prepareStatement(handler, ms.getStatementLog());
    // 执行查询
    return handler.query(stmt, resultHandler);
  } finally {
    closeStatement(stmt);
  }
}

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
  Statement stmt;
  Connection connection = getConnection(statementLog);
  // 调用 StatementHandler 的 prepare 方法创建 Statement
  stmt = handler.prepare(connection, transaction.getTimeout());
  // 调用 StatementHandler 的 parameterize 方法设置参数
  handler.parameterize(stmt);
  return stmt;
}

通过源码可以看到,SQL执行的代码非常简单:

  1. 从configuration中,构造StatementHandler。
  2. 调用StatementHandler,构造JDBC的Statement。
  3. 调用StatementHandler的parameterize,设置参数。
  4. 调用StatementHandler的query方法(更新则调用update方法)。

简而言之,Executor通过StatementHandler,实现SQL执行的功能。

二、StatementHandler接口方法

StatementHandler 接口定义了与数据库交互的核心方法,覆盖了从SQL准备、参数设置到执行的完整流程:

方法签名作用描述
Statement prepare(Connection connection, Integer transactionTimeout)创建并初始化数据库执行语句(Statement/PreparedStatement),完成 SQL 预编译
void parameterize(Statement statement)为预编译 SQL 设置参数(将 Java 对象映射到 SQL 占位符?)
int batch(Statement statement)执行批量 SQL 操作(如批量插入 / 更新),返回批处理数量
int update(Statement statement)执行增删改操作,返回影响的行数
List<Object> query(Statement statement, ResultHandler resultHandler)执行查询操作,通过 ResultHandler 将结果集映射为 Java 对象
<T> Cursor<T> queryCursor(Statement statement)执行查询并返回 Cursor 对象,用于流式处理大数据集
BoundSql getBoundSql()获取封装 SQL 语句和参数信息的 BoundSql 对象
ParameterHandler getParameterHandler()获取参数处理器 ParameterHandler(用于参数设置)

三、代码实现

3.1 概述

classDiagram
    direction TB
    class StatementHandler {
        <<interface>>
        +prepare(Connection, Integer) Statement
        +parameterize(Statement) void
        +batch(Statement) int
        +update(Statement) int
        +query(Statement, ResultHandler) List~Object~
        +queryCursor(Statement) Cursor~T~
        +getBoundSql() BoundSql
        +getParameterHandler() ParameterHandler
    }
    class BaseStatementHandler {
        <<abstract>>
        -configuration: Configuration
        -executor: Executor
        -mappedStatement: MappedStatement
        -parameterHandler: ParameterHandler
        -resultSetHandler: ResultSetHandler
        -boundSql: BoundSql
        +prepare(Connection, Integer) Statement
        +getBoundSql() BoundSql
        +getParameterHandler() ParameterHandler
    }
    class SimpleStatementHandler
    class PreparedStatementHandler
    class CallableStatementHandler
    class RoutingStatementHandler
    StatementHandler <|-- BaseStatementHandler
    BaseStatementHandler <|-- SimpleStatementHandler
    BaseStatementHandler <|-- PreparedStatementHandler
    BaseStatementHandler <|-- CallableStatementHandler
    StatementHandler <|-- RoutingStatementHandler
    RoutingStatementHandler *-- StatementHandler : delegates

StatementHandler有四个核心实现类,关系与作用如下:

  • BaseStatementHandler:模板模式的抽象基类,定义了一些基础属性&方法,核心实现了prepare,定义了抽象的instantiateStatement方法,留给子类是实现。有三个子类:
    • SimpleStatementHandler 对应 JDBC 的 Statement,处理无参数静态 SQL;
    • PreparedStatementHandler 对应 PreparedStatement,处理带参数预编译 SQL(默认使用);
    • CallableStatementHandler 对应 CallableStatement,用于调用存储过程。
  • RoutingStatementHandler 是路由处理器,自身不处理逻辑,而是根据MappedStatement的statementType动态选择上述三个实现类之一进行委派,简化了客户端调用。

3.2RoutingStatementHandler- Handler路由器

RoutingStatementHandler是典型的代理模式,在构造函数中,根据MappedStatement的statementType选择具体的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());
  }
}
@Override  
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {  
    return delegate.prepare(connection, transactionTimeout);  
}
// 其他代理方法忽略。。。

PS:MappedStatement的statementType默认是PREPARED,可以在xml或者注解中配置statementType属性。

3.3 BaseStatementHandler 模板模式的基类

BaseStatementHandler源码中,比较重要的是这个prepare方法,实现了通用流程,具体的JDBC的Statement创建逻辑,由子类通过instantiateStatement方法实现。

@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);
    // 设置fetchSize
    setFetchSize(statement);
    return statement;
  } catch (SQLException e) {
    closeStatement(statement);
    throw e;
  }
}
// 需要子类实现
protected abstract Statement instantiateStatement(Connection connection) throws SQLException;

3.4 instantiateStatement方法

各个子类的instantiateStatement发放,就是使用不同的姿势调用JDBC的API:

  1. SimpleStatementHandler 对应 JDBC 的 Statement
@Override  
protected Statement instantiateStatement(Connection connection) throws SQLException {  
    if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {  
        return connection.createStatement();  
    } else {  
        return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);  
    }  
}
  1. PreparedStatementHandler 对应 PreparedStatement
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
  String sql = boundSql.getSql();
  // 处理自增主键
  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);
  }
}
  1. CallableStatementHandler 对应 CallableStatement:
@Override  
protected Statement instantiateStatement(Connection connection) throws SQLException {  
    String sql = boundSql.getSql();  
    if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {  
        return connection.prepareCall(sql);  
    } else {  
        return connection.prepareCall(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);  
    }  
}

3.5 处理参数&结果

StatementHandler的参数处理&结果处理,分别委托给ParameterHandler&ResultSetHandler(注意不是ResultHandler)。可以说,Mybatis把单一职责进行到底。

PreparedStatementHandler参数&结果处理:

@Override
public void parameterize(Statement statement) throws SQLException {
  // 委托ParameterHandler设置参数
  parameterHandler.setParameters((PreparedStatement) statement);
}
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
  PreparedStatement ps = (PreparedStatement) statement;
  ps.execute();
  // 委托ResultSetHandler处理结果集
  return resultSetHandler.handleResultSets(ps);
}

四、小结

StatementHandler是MyBatis中负责与数据库交互的核心组件,封装了JDBC的Statement相关操作,提供了统一的接口:

  • SQL 语句的准备(预编译)
  • 参数绑定
  • SQL执行(查询、更新、批量操作等),这里也包含了结果处理。

StatementHandler实现过程中,使用了模板、策略以及代理模式。并且很好的体现了单一职责原则,参数处理&结果处理,分别委托给ParameterHandler&ResultSetHandler(注意不是ResultHandler),这两个Handler我们后续再讲。

到此这篇关于Mybatis StatementHandler的使用小结的文章就介绍到这了,更多相关Mybatis StatementHandler内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • MyBatis 使用权威指南

    MyBatis 使用权威指南

    MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集,本文给大家介绍MyBatis 使用指南,一起看看吧
    2017-03-03
  • Minio的使用教程

    Minio的使用教程

    Minio是一个开源的对象存储服务,兼容Amazon S3接口,适合存储大容量非结构化数据,它部署简单,支持海量存储和分布式部署,通过配置文件和systemd服务,可以轻松设置和管理Minio服务,本文介绍Minio的使用教程,感兴趣的朋友一起看看吧
    2025-02-02
  • Spring @Primary和@Qualifier注解原理解析

    Spring @Primary和@Qualifier注解原理解析

    这篇文章主要介绍了Spring @Primary和@Qualifier注解原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-04-04
  • 使用chatgpt实现微信聊天小程序的代码示例

    使用chatgpt实现微信聊天小程序的代码示例

    这篇文章主要介绍了使用chatgpt实现微信聊天小程序(秒回复),文中有详细的代码示例,对大家了解chatgpt聊天有一定的帮助,感兴趣的同学可以参考阅读
    2023-05-05
  • 如何解决IDEA中JSP页面部分出现绿色背景色问题

    如何解决IDEA中JSP页面部分出现绿色背景色问题

    这篇文章主要介绍了如何解决IDEA中JSP页面部分出现绿色背景色问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-12-12
  • maven关于pom文件中的relativePath标签使用

    maven关于pom文件中的relativePath标签使用

    在Maven项目中,子工程通过<relativePath>标签指定父工程的pom.xml位置,以确保正确继承父工程的配置,这个标签可以配置为默认值、空值或自定义值,默认情况下,Maven会向上一级目录寻找父pom;若配置为空值
    2024-09-09
  • MyBatis执行SQL的两种方式小结

    MyBatis执行SQL的两种方式小结

    本文主要介绍了MyBatis执行SQL的两种方式小结,主要包括SqlSession 发送SQL和SqlSession获取Mapper接口,通过Mapper接口发送SQL,具有一定的参考价值,感兴趣的可以了解一下
    2023-10-10
  • 使用Netty快速实现一个群聊功能的示例详解

    使用Netty快速实现一个群聊功能的示例详解

    这篇文章主要为大家详细介绍了如何利用 Netty 框架开发一个 WebSocket 服务端,从而实现一个简单的在线聊天功能,感兴趣的小伙伴可以了解下
    2023-11-11
  • 快速解决idea打开某个项目卡住的问题

    快速解决idea打开某个项目卡住的问题

    这篇文章主要介绍了解决idea打开某个项目卡住的问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-08-08
  • Springboot项目集成SkyWalking链路追踪系统实战指南

    Springboot项目集成SkyWalking链路追踪系统实战指南

    本文详解SpringBoot集成SkyWalking链路追踪的步骤,涵盖Agent部署、配置参数设置(含服务名、后端地址、认证token)、多启动方式及日志对接方法,但未充分说明环境变量配置细节和gRPC认证参数的使用场景,需要的朋友跟随小编一起看看吧
    2025-08-08

最新评论