StatementHandler的整体结构

PreparedStatementHandler实现自StatementHandler,我们先看下StatementHandler的结构

StatementHandler的结构

StatementHandler的执行流程

StatementHandler的执行流程 (2)

请求参数处理

所谓的请求参数处理,就是将入参转换为JDBC参数的过程。主要分为两个步骤,一是参数的转换,二是参数的映射。

Mybatis请求参数处理涉及内容

参数的转换

如果是单个参数,我们的请求参数会被转换为一个Object对象;如果是多个请求参数,或者单个参数但是被加了@Param注解,那么我们的入参是会被转换成Map。举个例子:

1
2
@Select("SELECT * FROM T_USER WHERE id = #{user.id} OR USER_NAME = #{name}")
UserPO selectUserWithPo(@Param("user") UserPO userPO, @Param("name") String name);

image-20240608190207826

参数转换的源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// ParamNameResolver
public Object getNamedParams(Object[] args) {
final int paramCount = names.size();
if (args == null || paramCount == 0) {
return null;
}
if (!hasParamAnnotation && paramCount == 1) {// 只有一个参数,且没有@Param注解,直接用本身的值
Object value = args[names.firstKey()];
return wrapToMapIfCollection(value, useActualParamName ? names.get(names.firstKey()) : null);
} else {
// 否则,所有参数都会被放入到一个Map中
final Map<String, Object> param = new ParamMap<>();
int i = 0;
for (Map.Entry<Integer, String> entry : names.entrySet()) {
param.put(entry.getValue(), args[entry.getKey()]);
// add generic param names (param1, param2, ...)
final String genericParamName = GENERIC_NAME_PREFIX + (i + 1);
// ensure not to overwrite parameter named with @Param
if (!names.containsValue(genericParamName)) {
param.put(genericParamName, args[entry.getKey()]);
}
i++;
}
return param;
}
}

参数的映射处理

Mybatis会为每个参数生成一个ParameterMapping,并收集到List中。然后遍历这个集合,再根据具体的参数和jdbcType获取这个参数对应的TypeHandler,比如IntegerTypeHandler,StringTypeHandler等等,然后通过TypeHandler设置属性值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// DefaultParameterHandler
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
// 每有一个入参,就会生成一个对应的ParameterMapping
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
MetaObject metaObject = null;
for (int i = 0; i < parameterMappings.size(); i++) { // 遍历ParameterMapping
ParameterMapping parameterMapping = parameterMappings.get(i);
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
// 参数值,举例:user.id
String propertyName = parameterMapping.getProperty();
// 获取value值
if (boundSql.hasAdditionalParameter(propertyName)) { // 是否有额外参数
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
// 只有一个参数
value = parameterObject;
} else {
if (metaObject == null) {
metaObject = configuration.newMetaObject(parameterObject);
}
value = metaObject.getValue(propertyName);
}
TypeHandler typeHandler = parameterMapping.getTypeHandler();
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
try {
// 如果typeHandler是UnknownTypeHandler,调用UnknownTypeHandler#resolveTypeHandler()来获取TypeHandler,
// 最后再调用setParameter()设置参数。
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (TypeException | SQLException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
}
}
}
}
}

结果集处理

结果集是如何被处理为我们需要的javaBean的呢?也大致分为三块儿内容

Mybatis结果集处理

简单来说,先遍历resultMap,然后再遍历结果集处理每一行的数据。在处理一行数据的过程中,会获取对象的属性集合,然后遍历属性的集合,获取每个属性对应的TypeHandler,并通过调用这个TypeHandler的getResult()从结果集中获取这个属性对应的值,然后将这个属性放到一个集合中返回,然后通过ObjectFactory将这个属性的集合构造出一个对象返回,最后将这个对象放入到ResultHandler中的集合中,整个流程结束。

来一个简单的时序图(加载比较慢…):