一、前言
ParameterHandler
是 Mybatis 四大处理器之一,负责为 PreparedStatement
的 SQL 语句参数动态赋值。
二、结构体系
2.1 接口
// org.apache.ibatis.executor.parameter.ParameterHandler
/**
* 参数处理器,对 {@code PreparedStatement} 设置参数
*/
public interface ParameterHandler {
/**
* 获取参数对象
* @return
*/
Object getParameterObject();
/**
* 对 {@link PreparedStatement} 的参数赋值
* @param ps
* @throws SQLException
*/
void setParameters(PreparedStatement ps) throws SQLException;
}
ParameterHandler
定义了两个简单的接口,一个是获取参数对象,一个是对 PreparedStatement
进行赋值操作。
2.2 继承
Mybatis ParameterHandler
默认只有一个实现类 DefaultParamerter
,这个类最重要的方法是完成对 PreparedStatement
对象的赋值操作。
2.3 DefaultParamerter#setparameters
下图是 DefaultParamerter#setparameters
源码,从 BoundSql
对象中获取所有的参数(每个参数都被封装为 ParameterMapping 对象),循环遍历参数集合,完成参数写入。循环遍历过程如下:
- 确定参数对象。
- 从
ParameterMapping
获取类型处理器 - 由对应的类型处理器给 PreparedStatement 对象赋值
三、生命周期
我们先回顾一下 Mybatis 加载流程:
- 解析配置文件(比如
mybatis-config.xml
),并使用Configuration
配置- 解析全局配置,比如缓存、类型处理器
- 解析 Mapper 映射文件,重要的对象有
BoundSql:
包含 SQL、参数数据MappedStatement:
每个<select|inert|update|delete>
标签都对应一个MappedStatement
,包含所有的标签配置属性。
- 解析配置文件之后,根据配置文件获取
SqlSessionFactory
工厂 - 根据工厂获取
SqlSession
对象,这个对象就是 Mybatis 暴露给用户的接口,利用这个对象就可以进行 SQL 操作。 - 假设我们使用
session.selectOne(statementId)
这最简单的查询语句,捋一下 Mybatis 处理流程。- 首先,我们需要根据
statementId
获取MappedStatement
,这个对象非常重要,里面包含了待执行 SQL 重要信息。 - 接着,使用
Executor
执行器进行query()
方法调用。(Executor初始化请看)。 - 轮到
Executor
执行器,它会判断是当前查询是否已经缓存,如果不存在,则查询数据库,对应方法 (org.apache.ibatis.executor.BaseExecutor#queryFromDatabase()
)。 Executor
并非真正封装 JDBC 调用,而是StatementHandler
详见。先根据配置文件获取StatementHandler
(一般为PreparedStatementHandler
)对象,它封装了底层 JDBC 相应的调用方法。- 在调用
execute()
等方法之前,我们需要对 SQL 进行预编译操作,由上述的StatementHandler
完成。 - 紧接着,对预编译对象
PreparedStatement
对象进行赋值,而这个赋值操作就是今天讲到的ParameterHandler
。 - 再进行
execute()
等方法调用,结果存储在PreparedStatement
。 - 这里,轮到结果处理器
ResuleSetHandler
上场,是它将我们的结果集转换为想要的对象/集合。 - 返回并关闭对应资源 。
- 当然,上述有一些细节并未提及,比如 ID 生成器等,但是这不妨碍你理解整个流程。
- 首先,我们需要根据
从上面看到,类型的解析在步骤f,我们已经可以根据配置类获取足够多的关于类型相关的信息,因此,根据类型获取对应的类型处理器就可以完成类型的转换操作。
3.1 类型处理器在什么时候被确定?
从 图 2.3 源码可以看到,TypeHandler
是直接从 ParameterMapping
对象中直接获取,所以可以追踪 ParameterMapping
在何时创建的就可以找到参数类型处理器何时确定。
3.1.1 ParameterMapping
ParameterMapping
是Mybatis 参数映射类,它负责存储参数已解析的 XML SQL 参数。下图是它定义的成员变量:
3.1.2 确定 TypeHandler
(1)显示配置
若标签上有 parameterType
配置,如:
则在 XML 解析时就能直接确定对应的类型处理器:
(2)隐式配置
当用户没有显示设置参数类型时,则 javaType 为 Object 类型:
别担心,Mybatis 会在赋值时会进一步解析所对应的类型。从上面可以看到,此时 TypeHandler
为 UnknownTypeHandler
,所以我们看一看它到底如何获取真正的类型处理器。
以上是当 Mybatis 解析配置文件时无法确定类型时才会用到的 UnknownTypeHandler
,那为什么一开始确定不了呢? 因为我们无法在一开始就能获取到参数对象,所以如果使用 getClass()
获取对应数据类型信息,也就无法确定参数类型处理器。
3.2 参数在什么时候被写入
3.2.1 何时准备 SQL 语句呢?
当我们调用 SqlSession
API 进行 SQL 语句查询,通过 MappedStatement#getBoundSql(parameter)
就能获取可以预编译的 SQL 详情。
而他也是代理给 SqlSource#getBoundSql()
方法完成。而在 SqlSource继承体系 提到过 SqlSource
,根据不同的类型拥有不同的解析策略。
3.2.2 何时设置参数呢?
既然 SQL 已经准备好,其实参数也是上一步也准备好了。封装在 BoundSql
的 List<ParameterMapping>
参数中。如上图所示。
那在哪里进行参数的设置呢? 其实就是由 ParameterHandler
来完成的。看一下调用栈:
相关源码
五、总结
ParameterHandler
只有一个实现类,而它完成对预编译的 SQL 语句参数赋值操作。