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执行的代码非常简单:
- 从configuration中,构造StatementHandler。
- 调用StatementHandler,构造JDBC的Statement。
- 调用StatementHandler的parameterize,设置参数。
- 调用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 : delegatesStatementHandler有四个核心实现类,关系与作用如下:
- 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:
- 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);
}
}- 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);
}
}
- 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-Plus3.2.0 MetaObjectHandler 无法进行公共字段全局填充
- MyBatis-Plus中MetaObjectHandler没生效完美解决
- Mybatis实现自定义的typehandler三步曲
- MyBatis自定义typeHandler的完整实例
- 解决mybatisplus MetaObjectHandler 失效的问题
- Mybatis实现自定义类型转换器TypeHandler的方法
- mybatis中resulthandler的用法
- 解决Mybatis-plus自定义TypeHandler查询映射结果一直为null问题
- Mybatis中自定义TypeHandler处理枚举详解
- Mybatis自定义TypeHandler解决特殊类型转换问题详解
- Mybatis实战之TypeHandler高级进阶
相关文章
PowerJob的QueryConvertUtils工作流程源码解读
这篇文章主要为大家介绍了PowerJob的QueryConvertUtils工作流程源码解读,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪2024-01-01
springboot log4j2.xml如何读取application.yml中属性值
这篇文章主要介绍了springboot log4j2.xml如何读取application.yml中属性值问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教2023-12-12
Java inputstream和outputstream使用详解
这篇文章主要介绍了Java inputstream和outputstream使用详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下2021-08-08
浅谈java.util.concurrent包中的线程池和消息队列
这篇文章主要介绍了浅谈java.util.concurrent包中的线程池和消息队列,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧2019-08-08


最新评论