这是我参与8月更文挑战的第25天,活动详情查看:8月更文挑战
时序图
sequenceDiagram
participant A as MapperMethod
participant B as MethodSignature
participant C as ParamNameResolver
participant D as DefaultSqlSession
A ->> A : executeForMany
A ->> B : convertArgsToSqlCommandParam
B ->> C : getNamedParams
C -->> A : Object param
C ->> D : selectList
D -->> C : List<E>
C -->> A : Object result
详细步骤
MapperMethod#executeForMany
/**
* 查询多个结果
* @param sqlSession session
* @param args 当前查询的实际参数
* @param <E>
* @return
*/
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
List<E> result;
// 参数名称与参数值的映射
Object param = method.convertArgsToSqlCommandParam(args);
if (method.hasRowBounds()) {// 判断是否有RowBounds参数
// 获取RowBounds参数
RowBounds rowBounds = method.extractRowBounds(args);
// 带RowBounds的查询
result = sqlSession.selectList(command.getName(), param, rowBounds);
} else {
// 不带RowBounds的查询
result = sqlSession.selectList(command.getName(), param);
}
// issue #510 Collections & arrays support
if (!method.getReturnType().isAssignableFrom(result.getClass())) {
if (method.getReturnType().isArray()) {
return convertToArray(result);
} else {
return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
}
}
return result;
}
ParamNameResolver#getNamedParams
- 此方法是处理参数值的映射。
- 参数名称的处理见Mybatis源码之Mapper方法执行流程
/**
* <p>
* A single non-special parameter is returned without a name.
* Multiple parameters are named using the naming rule.
* In addition to the default names, this method also adds the generic names (param1, param2,
* ...).
* </p>
* <p>
* 将被解析的方法中的参数名称列表与传入的`Object[] args`进行对应,返回对应关系。
* <p>
* 如果只有一个参数,直接返回参数
* 如果有多个参数,则进行与之前解析出的参数名称进行对应,返回对应关系
*
* 参数名称:参数值
* paramx:参数值
*/
public Object getNamedParams(Object[] args) {
// 参数名称个数
final int paramCount = names.size();
if (args == null || paramCount == 0) {
return null;
} else if (!hasParamAnnotation && paramCount == 1) {
// 如果不含有@Param注解,且只有一个参数,直接返回参数
return args[names.firstKey()];
} else {
final Map<String, Object> param = new ParamMap<>();
int i = 0;
for (Map.Entry<Integer, String> entry : names.entrySet()) {
// 首先按照类注释中提供的key,存入一遍 【参数的@Param名称 或者 参数排序:实参值】
// 注意,key和value交换了位置
param.put(entry.getValue(), args[entry.getKey()]);
// 再按照param1, param2, ...的命名方式存入一遍
// add generic param names (param1, param2, ...)
final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
// ensure not to overwrite parameter named with @Param
// 生成的参数名称不能覆盖names中已有的参数名称
if (!names.containsValue(genericParamName)) {
param.put(genericParamName, args[entry.getKey()]);
}
i++;
}
return param;
}
}
DefaultSqlSession#selectList
- 设置MappedStatement映射,见Mybatis源码之select|insert|update|delete标签解析
- 获取executor,见Mybatis源码之创建SqlSession对象
/**
* 查询结果列表
* @param <E> 返回的列表元素的类型
* @param statement SQL语句
* @param parameter 参数对象
* @param rowBounds 翻页限制条件
* @return 结果对象列表
*/
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
/**
* 获取查询语句
* 设置MappedStatement映射,见{@link XMLStatementBuilder#parseStatementNode()}
*/
MappedStatement ms = configuration.getMappedStatement(statement);
/**
* 交由执行器进行查询,由于全局配置cacheEnabled默认是打开的,因此此处的executor通常都是CachingExecutor
* 获取executor,见{@link Configuration#newExecutor(org.apache.ibatis.transaction.Transaction, org.apache.ibatis.session.ExecutorType)}
*/
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
以上便是Mybatis中MapperMethod#executeForMany方法的流程。