这是我参与8月更文挑战的第26天,活动详情查看:8月更文挑战
时序图
sequenceDiagram
participant A as DefaultSqlSession
participant B as CachingExecutor
participant C as MappedStatement
participant D as SqlSource
participant E as Executor
A ->> B : query
B ->> C : getBoundSql
C ->> D : getBoundSql
D -->> C : BoundSql
C -->> B : BoundSql
B ->> B : createCacheKey
B ->> E : createCacheKey
E -->> B : CacheKey
B ->> B : query
B ->> E : query
E -->> B : List<E>
详细步骤
DefaultSqlSession#selectList
- 添加MappedStatement到Configuration对象,见Mybatis源码之select|insert|update|delete标签解析
/**
* 查询结果列表
* @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();
}
}
CachingExecutor#query
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
// 获取BoundSql,其中包含可执行的SQL以及响应的参数映射
BoundSql boundSql = ms.getBoundSql(parameterObject);
// 使用实际Executor生成缓存键
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
// 使用实际Executor执行查询
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
MappedStatement#getBoundSql
public BoundSql getBoundSql(Object parameterObject) {
// 获取可执行的SQL
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
// 获取参数映射
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
// 判断参数映射是否为空,如果为空,则使用默认的参数映射(配置的parameterMap或parameterType)
if (parameterMappings == null || parameterMappings.isEmpty()) {
boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
}
// check for nested result maps in parameter mappings (issue #30)
// 检查内嵌结果集映射
for (ParameterMapping pm : boundSql.getParameterMappings()) {
String rmId = pm.getResultMapId();
if (rmId != null) {
ResultMap rm = configuration.getResultMap(rmId);
if (rm != null) {
hasNestedResultMaps |= rm.hasNestedResultMaps();
}
}
}
return boundSql;
}
CachingExecutor#query
- 如果有二级缓存,则通过缓存进行查询,如果缓存中不存在,则查询后将结果进行缓存。
- 二级缓存解析位置,见Mybatis源码之mappers标签解析
/**
* 查询数据库中的数据
*
* @param ms 映射语句
* @param parameterObject 参数对象
* @param rowBounds 翻页限制条件
* @param resultHandler 结果处理器
* @param key 缓存的键
* @param boundSql 查询语句
* @param <E> 结果类型
* @return 结果列表
* @throws SQLException
*/
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
// 获取MappedStatement对应的缓存,可能的结果有:该命名空间的缓存、共享的其它命名空间的缓存、无缓存
Cache cache = ms.getCache();
// 如果映射文件未设置<cache>或<cache-ref>则,此处cache变量为null
if (cache != null) { // 存在缓存
// 根据要求判断语句执行前是否要清除二级缓存,如果需要,清除二级缓存
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) { // 该语句使用缓存且没有结果处理器
// 二级缓存不支持含有输出参数的CALLABLE语句,故在这里进行判断
ensureNoOutParams(ms, boundSql);
@SuppressWarnings("unchecked")
// 从缓存中读取结果
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) { // 缓存中没有结果
// 交给被包装的执行器执行
list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
// 缓存被包装执行器返回的结果
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
// 交由被包装的实际执行器执行
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
以上便是Mybatis中DefaultSqlSession#selectList方法的流程。