1.查询操作
SqlSession中定义的查询有以下这些:
<T> T selectOne(String statement);
<T> T selectOne(String statement, Object parameter);
<E> List<E> selectList(String statement);
<E> List<E> selectList(String statement, Object parameter);
<E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds);
<K, V> Map<K, V> selectMap(String statement, String mapKey);
<K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey);
<K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds);
<T> Cursor<T> selectCursor(String statement);
<T> Cursor<T> selectCursor(String statement, Object parameter);
<T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds);
void select(String statement, Object parameter, ResultHandler handler);
void select(String statement, ResultHandler handler);
void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler);
其中最关键的方法就是selectList,观察其方法实现,其实不管是selectOne还是selectMap,其方法都会调用selectList方法进行查询数据,如下:
/**
* 查询一个
*/
@Override
public <T> T selectOne(String statement, Object parameter) {
//调用selectList方法查询数据
List<T> list = this.selectList(statement, parameter);
if (list.size() == 1) { //若只有一个数据
return list.get(0); //则返回该数据
} else if (list.size() > 1) { //若大于一个数据则抛出异常
throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
} else { //没有数据返回null
return null;
}
}
@Override
public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {
//调用selectList进行查询
final List<? extends V> list = selectList(statement, parameter, rowBounds);
//后面是组装结果
final DefaultMapResultHandler<K, V> mapResultHandler = new DefaultMapResultHandler<>(mapKey,
configuration.getObjectFactory(), configuration.getObjectWrapperFactory(), configuration.getReflectorFactory());
final DefaultResultContext<V> context = new DefaultResultContext<>();
for (V o : list) {
context.nextResultObject(o);
mapResultHandler.handleResult(context);
}
return mapResultHandler.getMappedResults();
}
2. selectList解析
selectList方法的源码如下,我们这里查看的是默认的SqlSession,也就是DefaultSqlSession:
/**
* 查询list
*/
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
//根据statement获取对应的MappedStatement
MappedStatement ms = configuration.getMappedStatement(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();
}
}
其中MappedStatement是我们在第一步读取配置文件时,根据配置生成的,是根据Mapper文件中的标签生成的,最后放到configuration中,这里就是根据id从configuration中获取到对应的MappedStatement,这里会直接调用执行器executor的query进行查询并返回,这里再看一眼执行器的生成(上一节内容):
/**
* 创建一个执行器
* @param transaction 事务
* @param executorType 执行器类型
*/
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
//若executorType为null 则设置成defaultExecutorType也就是ExecutorType.SIMPLE
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
//根据类型生成对应的执行器对象
if (ExecutorType.BATCH == executorType) { //BATCH类型
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) { //REUSE类型
executor = new ReuseExecutor(this, transaction);
} else { //默认的SIMPLE类型
executor = new SimpleExecutor(this, transaction);
}
if (cacheEnabled) { //是否开启缓存
//通过CachingExecutor对生成的执行器executor进行包装
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor; //返回执行器
}
根据执行器生成执行器后,会检查是否开启二级缓存,若开启,则会将执行器包装成CachingExecutor类型,这里我们也直接重CachingExecutor类中继续查看。
/**
* 查询数据
*/
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
//根据参数以及MappedStatement获取对应BoundSql
BoundSql boundSql = ms.getBoundSql(parameterObject);
//生成CacheKey
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
//调用query方法查询数据
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
/**
* 查询数据
*/
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
//获取缓存
Cache cache = ms.getCache();
//若缓存存在
if (cache != null) {
flushCacheIfRequired(ms);
//检查是否开启二级缓存
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, boundSql);
//直接从缓存中获取数据
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
//若缓存中未找到对应数据
if (list == null) {
//调用delegate的query方法进行查询
list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
//将查询结果加入缓存中
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
//缓存不存在或未开启二级缓存,这里会直接调用delegate的query方法进行查询数据并将结果返回
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
这里我们可以看到CachingExecutor并未直接进行查询,而是先检查二级缓存中是否有对应数据,如果有,直接返回,若是没有,则调用属性delegate的方法进行查询,并将结果缓存起来,若是未开启缓存,或未开启二级缓存,这里就直接属性delegate的方法进行查询,CachingExecutor本身只是进行二级缓存相关逻辑,下面我们继续看以下delegate中的query方法,这里delegate我们默认的是SimpleExecutor执行器,其query方法是在父类BaseExecutor中定义的,代码如下:
/**
* 查询方法
*/
@SuppressWarnings("unchecked")
@Override
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;
}
这里还是有一个缓存处理的逻辑,这里是一级缓存处理的逻辑,跟二级缓存处理类似,缓存中找不到的时候会调用queryFromDatabase方法从数据库中获取数据,我们看一下这个方法:
/**
* 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;
}
这里有一个查询到数据加入到缓存中,具体的查询方法是调用的doQuery方法,这个方法在BaseExecutor中并未实现,是在具体的执行器中实现的,我们去看一下SimpleExecutor中的doQuery方法
/**
* 查询数据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 configuration = ms.getConfiguration();
//创建一个StatementHandler,这里是创建一个RoutingStatementHandler的实例
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
//生成Statement
stmt = prepareStatement(handler, ms.getStatementLog());
//调用handler的query方法进行查询
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
RoutingStatementHandler构造方法如下
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
//根据StatementType生成对应的StatementHandler赋值给delegate属性
switch (ms.getStatementType()) {
case STATEMENT:
delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case PREPARED:
delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case CALLABLE:
delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
default:
throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
}
}
这个类似与CachingExecutor与具体执行器的关系,这里我们以SimpleStatementHandler为例,看一下其查询方法:
/**
* 查询
*/
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
//获取sql
String sql = boundSql.getSql();
//执行sql
statement.execute(sql);
//结果处理
return resultSetHandler.handleResultSets(statement);
}
这就是查询数据整个的过程。。。