前言
通过 MapperMethod#execute() 方法,可以得知执行 SELECT 查询语句的方法比较多:
executeWithResultHandlerexecuteForManyexecuteForMapexecuteForCursor
这些方法在内部调用了SqlSession的一些 select* 方法。这些方法的返回值类型是不同的,因此对于每种返回类型,需要有专门的处理方法。以 selectList() 方法为例,该方法的返回值类型为 List。但如果 Mapper 或 Dao 的接口方法返回值类型为数组,或者 Set,直接将 List 类型的结果返回给 Mapper/Dao 就不合 适了。execute* 等方法只是对 select* 等方法做了一层简单的封装。
selectOne
SqlSession#selectOne() 方法内部会调用 selectList()。
// SqlSession
public <T> T selectOne(String statement, Object parameter) {
List<T> list = this.selectList(statement, parameter);
if (list.size() == 1) {
return list.get(0);
} else if (list.size() > 1) {
// 常见的异常。如果查询结果大于1则会抛出
throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
} else {
return null;
}
}
selectList
查询职责最终会委托给 Executor#query() 方法。
public <E> List<E> selectList(String statement) {
// 调用重载方法
return this.selectList(statement, null);
}
public <E> List<E> selectList(String statement, Object parameter) {
// 调用重载方法
return this.selectList(statement, parameter, RowBounds.DEFAULT);
}
// selectList最终实现方法
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("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
Executor
Executor 是个接口,用于执行定义的SQL语句。其实现类关系图:
创建默认Executor
executor 是 DefaultSqlSession 中的成员变量。跟踪下 DefaultSqlSessionFactory#openSession() 方法,默认是创建 CachingExecutor 实例对象。
CachingExecutor 类是个装饰器类,用于给目标Executor增加二级缓存功能。目标Executor默认是 SimpleExecutor
// DefaultSqlSessionFactory
public SqlSession openSession() {
// ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
// Configuration对象中的默认executor type为SIMPLE
// 创建默认的 SimpleExecutor 对象
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
// DefaultSqlSessionFactory
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// 根据参数创建指定的 Executor 实例对象
final Executor executor = configuration.newExecutor(tx, execType);
// 返回 DefaultSqlSession 实例对象
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
// Configuration
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
// 默认创建 SimpleExecutor
executor = new SimpleExecutor(this, transaction);
}
// 默认为true
if (cacheEnabled) {
// 装饰器类,用于给目标Executor增加二级缓存功能
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
CachingExecutor#query
SqlSession#selectList() 方法的查询功能,最终会委托给 CachingExecutor#query() 方法。CachingExecutor 类实际作用就是 增加二级缓存功能。
// 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);
}
// 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();
// XML映射文件中未配置缓存,则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.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list);
}
return list;
}
}
// 调用被装饰器类的 query() 方法
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
最终执行查询语句的功能还是会委托给目标Executor,这就是装饰器设计模式。这里调用的 query() 方法,实际上是目标Executor的父类BaseExecutor 的 query() 方法。
BaseExecutor#query
父类的 query() 方法,是完成通用功能,从一级缓存中查询结果集,如果缓存未命中,则调用 queryFromDatabase() 方法从数据库查询。
// BaseExecutor
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();
}
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
clearLocalCache();
}
}
return list;
}
BaseExecutor#queryFromDatabase
queryFromDatabase() 方法最终还是会调用 doQuery() 方法进行查询,并将返回的结果集添加到一级缓存中。但是 doQuery() 方法在 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;
}
SimpleExecutor#doQuery
到这里整个调用过程就比较清晰了,可以看出整个查询功能是由 StatementHandler 类完成的。而且已经清楚 prepareStatement() 方法创建了 Statment 对象。这已经是属于 JDBC 的范畴了。
// 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.query(stmt, resultHandler);
} finally {
// 关闭Statement
closeStatement(stmt);
}
}
StatementHandler 是个接口,其中主要有三个实现类SimpleStatementHandler、PreparedStatementHandler、CallableStatementHandler 分别对应 statementType 的STATEMENT,PREPARED 和 CALLABLE
statementType| 可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
PreparedStatementHandler#query
到这里整个调用过程算是已经结束了。此方法就是执行sql,并将结果集进行处理。
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
// 执行sql
ps.execute();
// 处理执行结果
return resultSetHandler.handleResultSets(ps);
}