在我们上一篇的分析中分析了Mybatis一次插入执行流程分析,因为我们所有的插入执行都会转换为update,所以这一次我们执行一次我们的查询,看看他的过程是怎么样的?代码如下
Student student = studentMapper.selectStudentWithAddress(1);
这段代码跟踪,因为他是一个代理我们直接走进代理,和上一篇分析的一样在excute中有一个select的判断:
else if (SqlCommandType.SELECT == command.getType()) { if (method.returnsVoid() && method.hasResultHandler()) { executeWithResultHandler(sqlSession, args); result = null; } else if (method.returnsMany()) { result = executeForMany(sqlSession, args); } else if (method.returnsMap()) { result = executeForMap(sqlSession, args); } else { Object param = method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(command.getName(), param); }
我们选择进入一个
result = executeForMany(sqlSession, args);
进入executeForMany的代码中:
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) { List<E> result;//结果集 Object param = method.convertArgsToSqlCommandParam(args);//转换参数 if (method.hasRowBounds()) {//是否有行数限制 RowBounds rowBounds = method.extractRowBounds(args); result = sqlSession.<E>selectList(command.getName(), param, rowBounds); } else { result = sqlSession.<E>selectList(command.getName(), param); }
// 对结果集进行处理
if (!method.getReturnType().isAssignableFrom(result.getClass())) { if (method.getReturnType().isArray()) { return convertToArray(result); } else { return convertToDeclaredCollection(sqlSession.getConfiguration(), result); } } return result;
}
上面的代码归纳起来:
1。转换参数
2。判断是否有行数限制,来执行不同的查询。
3。对返回的结果进行一些处理,如果需要数组转换为数组,否则转化为申明的结果集。
我们这里跟踪
result = sqlSession.<E>selectList(command.getName(), param, rowBounds);
继续进入我们的sqlSession的代码:
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);//获取我们的映射Statement
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();
}
}
executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);这句是我们的核心执行,和以前分析的一样我们要记住我们所有对的真正执行过程都是在executor中执行的。
简单说明一下wrapCollection(parameter),把我们的参数包装为一个map.接下来进入query:
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameter);//得到真正的sql
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);//得到我们之前的缓存Key
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
ms.getBoundSql(parameter)这里是我们解析sql的地方上一篇已经说过,这里不便多谈。接下来得到我们的缓存key如果在同一次SqlSession中我们就会缓存查询数据:
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
if (closed) {
throw new ExecutorException("Executor was closed.");
}
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
queryStack++;
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
if (queryStack == 0) {
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
// issue #601
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
}
过程简述为
1。上面的代码有一个int类型的查询栈用来标记当前是否是第一次查询,如果是就清除当前的缓存。
2。在执行查询之前首先查询栈自加1。
3。判断结果集处理器是否为空,如果为空则从缓存中获取,如果不为空则赋值为null。
4。进入queryFromDatabase查询。
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
localCache.putObject(key, EXECUTION_PLACEHOLDER);//向缓存添加
try {
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
localCache.removeObject(key);
}
localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
核心执行:
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
模板方法,我们进入默认的SimpleExecutor.class的doQuery:
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
和插入一样,这里的Handler是个代理,在执行真正的查询之前我们可以在这里进行分页拦截,后面我也会详细分析如何写一个mybati的分页拦截器。