查询语句对应的方法比较多,有如下几种:
- executeWithResultHandler
- executeForMany
- executeForMap
- executeForCursor
这些方法在内部调用了 SqlSession 中的一些 select*方法,比如 selectList、selectMap、
selectCursor 等。这些方法的返回值类型是不同的,因此对于每种返回类型,需要有专门的处
理方法。
以selectList 方法为例,该方法的返回值类型为 List。但如果我们的 Mapper 或 Dao
的接口方法返回值类型为数组,或者 Set,直接将 List 类型的结果返回给 Mapper/Dao 就不合适了。
execute_等方法只是对 select _等方法做了一层简单的封装,因此接下来我们应们应该
把目光放在这些 select * 方法上。
selectOne ⽅法分析
selectOne 在内部会调用selectList方法,selectOne和selectList方法是有联系的,同时分析selectOne 方法等同于分析selectList 方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| // -☆- DefaultSqlSession public <T> T selectOne(String statement, Object parameter) { // 调用 selectList 获取结果 List<T> list = this.<T>selectList(statement, parameter); if (list.size() == 1) { // 返回结果 return list.get(0); } else if (list.size() > 1) { // 如果查询结果大于 1 则抛出异常,这个异常也是很常见的 throw new TooManyResultsException("……"); } else { return null; } }
// -☆- DefaultSqlSession public <E> List<E> selectList(String statement, Object parameter) { // 调用重载方法 return this.selectList(statement, parameter, RowBounds.DEFAULT); }
|
来看看 selectList 方法的实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| private final Executor executor;
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { try { // 获取 MappedStatement MappedStatement ms = configuration.getMappedStatement(statement); // 调用 Executor 实现类中的 query 方法 return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); } catch (Exception e) { throw ExceptionFactory.wrapException("……"); } finally { ErrorContext.instance().reset(); } }
|
executor 变量,该变量类型为 Executor。Executor 是一个接口,它的实现类如下:
默认情况下,executor 的类型为CachingExecutor,该类是一个装饰器类,用于给目标 Executor 增加二级缓存功能。目标Executor默认情况下是 SimpleExecutor
接下来继续分析 selectOne 方法的调用栈。先来看看 CachingExecutor 的 query 方法是怎样实现的。
1 2 3 4 5 6 7 8 9 10 11
| // -☆- CachingExecutor public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { // 获取 BoundSql BoundSql boundSql = ms.getBoundSql(parameterObject); // 创建 CacheKey CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql); // 调用重载方法 return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); }
|
上面的代码用于获取 BoundSql 对象,创建 CacheKey 对象,然后再将这两个对象传给重
载方法。
重载方法
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
| // -☆- CachingExecutor 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 = null if (cache != null) { flushCacheIfRequired(ms); if (ms.isUseCache() && resultHandler == null) { ensureNoOutParams(ms, boundSql); List<E> list = (List<E>) tcm.getObject(cache, key); if (list == null) { // 若缓存未命中,则调用被装饰类的 query 方法 list = delegate.<E>query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); tcm.putObject(cache, key, list); // issue #578 and #116 } return list; } } // 调用被装饰类的 query 方法 return delegate.<E>query( ms, parameterObject, rowBounds, resultHandler, key, boundSql); }
|
以上代码涉及到了二级缓存,若二级缓存为空,或未命中,则调用被装饰类的 query 方
法。
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
| // -☆- BaseExecutor public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { 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(); } deferredLoads.clear(); if (configuration.getLocalCacheScope()==LocalCacheScope.STATEMENT) { clearLocalCache(); } } return list; }
|
主要用于从一级缓存中查找查询结果,若缓存未命中,再向数据库进行查询。DeferredLoad,这个类用于延迟加载。
queryFromDatabase 方法的实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| // -☆- BaseExecutor 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 { // 调用 doQuery 进行查询 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; }
|
最终还会调用 doQuery 进行查询
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| // -☆- SimpleExecutor 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 StatementHandler handler = configuration.newStatementHandler( wrapper, ms, parameter, rowBounds, resultHandler, boundSql); // 创建 Statement stmt = prepareStatement(handler, ms.getStatementLog()); // 执行查询操作 return handler.<E>query(stmt, resultHandler); } finally { // 关闭 Statement closeStatement(stmt); } }
|
以 PreparedStatementHandler 为例,看看它的 query 方
法是怎样实现的。如下:
1 2 3 4 5 6 7 8 9
| // -☆- PreparedStatementHandler public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; // 执行 SQL ps.execute(); // 处理执行结果 return resultSetHandler.<E>handleResultSets(ps); }
|
整个调用过程总算要结束了。查询过程涉及到了很多方法调用,不把这些调用方法搞清楚,很难对MyBatis 的查询过程有深入的理解。所以在接下来的章节中,我将会对一些重要的调用进行分析。