Mybatis 源码之查询数据库

主要研究 DefaultSqlSession 对象的查询方法

流程图

https://www.processon.com/view/link/655092a17b996f7b645b648d

多行查询

定位: org.apache.ibatis.session.defaults.DefaultSqlSession#selectList

作用: 根据条件查询, 返回 list 集合

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
39
40
41
42
43
44
45
46
47
 @Override
public <E> List<E> selectList(String statement, Object parameter) {
return this.selectList(statement, parameter, RowBounds.DEFAULT);
}

@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
return selectList(statement, parameter, rowBounds, Executor.NO_RESULT_HANDLER);
}

// 核心selectList
private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
try {
// 根据 statement id 找到对应的 MappedStatement
MappedStatement ms = configuration.getMappedStatement(statement);
// 转用执行器来查询结果, 注意这里传入的 ResultHandler 是 null
// 如果存在插件, 即(executor 被代理), 则会执行 org.apache.ibatis.plugin.Plugin#invoke
return executor.query(ms, wrapCollection(parameter), rowBounds, handler);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
// 清空错误上下文
ErrorContext.instance().reset();
}
}
// 若参数为集合或数组, 则转换成 Map 对象, 否则返回原对象
public static Object wrapToMapIfCollection(Object object, String actualParamName) {
if (object instanceof Collection) {
// 参数若是 Collection 型,做 collection 标记
ParamMap<Object> map = new ParamMap<>();
map.put("collection", object);
if (object instanceof List) {
// 参数若是 List 型,做 list 标记
map.put("list", object);
}
Optional.ofNullable(actualParamName).ifPresent(name -> map.put(name, object));
return map;
} else if (object != null && object.getClass().isArray()) {
// 参数若是数组型,做 array 标记
ParamMap<Object> map = new ParamMap<>();
map.put("array", object);
Optional.ofNullable(actualParamName).ifPresent(name -> map.put(name, object));
return map;
}
// 参数若不是集合型,直接返回原来值
return object;
}

插件和拦截器

定位: org.apache.ibatis.plugin.Plugin

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
// org.apache.ibatis.plugin.Plugin#invoke
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
// 获取当前方法所在类或接口中,可被当前Interceptor拦截的方式
Set<Method> methods = signatureMap.get(method.getDeclaringClass());
// 如果当前调用的方式需要被拦截,则调用intercept方法进行拦截处理
if (methods != null && methods.contains(method)) {
//调用Interceptor.intercept,也即插入了我们自己的逻辑
return interceptor.intercept(new Invocation(target, method, args));
}
// 如果当前调用的方法不能被拦截,则调用target对象的相应方法
return method.invoke(target, args);
} catch (Exception e) {
throw ExceptionUtil.unwrapThrowable(e);
}
}
public static Object wrap(Object target, Interceptor interceptor) {
// 获取用户自定义Interceptor中@Signature注解的信息,getSignatureMap方法负责处理@Signture注解
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
// 获取目标类型
Class<?> type = target.getClass();
// 获取目标类型实现的接口,拦截器可以拦截的 4 类对象都实现了相应的接口,这也是能使用 JDK 动态代理的方式创建代理对象的基础
// 支持被拦截的接口有: Executor, StatementHandler, ParameterHandler, ResultSetHandler
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
if (interfaces.length > 0) {
// 使用 JDK 动态代理的方式创建代理对象
return Proxy.newProxyInstance(
type.getClassLoader(),
interfaces,
// 使用 InvocationHandler 对象就是 Plugin 对象
new Plugin(target, interceptor, signatureMap));
}
return target;
}
// 取得签名 Map
private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
// 取 Intercepts 注解
Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);
// issue #251
// 必须得有 Intercepts 注解,没有报错
if (interceptsAnnotation == null) {
throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());
}
// value 是数组型,Signature 的数组
Signature[] sigs = interceptsAnnotation.value();
// 每个 class 里有多个 Method 需要被拦截,所以这么定义
Map<Class<?>, Set<Method>> signatureMap = new HashMap<>();
for (Signature sig : sigs) {
Set<Method> methods = MapUtil.computeIfAbsent(signatureMap, sig.type(), k -> new HashSet<>());
try {
Method method = sig.type().getMethod(sig.method(), sig.args());
methods.add(method);
} catch (NoSuchMethodException e) {
throw new PluginException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e);
}
}
return signatureMap;
}

自定义拦截器

1
2
3
4
5
6
7
8
9
10
11
// 自定义实现拦截器
@Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
public class MyInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("-------" + invocation + "================");
Object result = invocation.proceed();
System.out.println("-------" + result + "================");
return result;
}
}

CachingExecutor 查询

定位: org.apache.ibatis.executor.CachingExecutor

作用: CachingExecutor 作为二级缓存执行器, 添加了缓存机制, 其实际查询逻辑仍然由 BaseExecutor#query 的实现类(即delegate)来执行

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
@Override
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);
}

// 被 ResultLoader.selectList 调用
@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) {
// 根据 select 节点的配置,决定是否需要清空二级缓存
flushCacheIfRequired(ms);
// 检测 SQL 节点的 useCache 配置以及是否使用了 resultHandler 配置
if (ms.isUseCache() && resultHandler == null) {
// 二级缓存不能保存输出类型的参数,如果查询操作调用了包含输出参数的存储过程,则报错
ensureNoOutParams(ms, boundSql);
@SuppressWarnings("unchecked")
// 查询二级缓存
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
// 二级缓存没有相应的结果集,调用封装的 Executor 对象的 query 方法
list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
// 将查询结果保存到 TransactionalCache.entriesToAddOnCommit 集合中
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
// 没有启动二级缓存,直接调用底层Executor执行数据库查询操作
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

BaseExecutor 查询

查询: org.apache.ibatis.executor.BaseExecutor#query

作用: BaseExecutor 的实现类有: SimpleExecutor(简单执行器), ReuseExecutor(重用执行器), BatchExecutor(批处理执行器)

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
@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());
// 检测当前 Executor 是否已经关闭
if (closed) {
throw new ExecutorException("Executor was closed.");
}
if (queryStack == 0 && ms.isFlushCacheRequired()) {
// 非嵌套查询,并且 select 节点配置的 flushCache 属性为 true 时,才会清空一级缓存, flushCache 配置项是影响一级缓存中结果对象存活时长的第一个方面
clearLocalCache();
}
List<E> list;
try {
// 增加查询层数
queryStack++;
// 查询一级缓存
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
// 针对存储过程调用的处理,在一级缓存命中时,获取缓存中保存的输出类型参数,并设置到用户传入的实参对象中
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
// 调用 doQuery 方法完成数据库查询,并得到映射后的结果对象
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
// 当前查询完成,查询层数减少
queryStack--;
}
if (queryStack == 0) {
// 在最外层的查询结束时,所有嵌套查询也已经完成,相关缓存项也已经完全记载,所以在此处触发DeferredLoad 加载一级缓存中记录的嵌套查询的结果对象
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
// issue #601
// 加载完成后,清空deferredLoads集合
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
// 根据 LocalCacheScope 配置决定是否清空一级缓存
clearLocalCache();
}
}
return list;
}
// 从数据库查
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;
}
// 创建缓存Key
@Override
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
// 检测当前 executor 是否已经关闭
if (closed) {
throw new ExecutorException("Executor was closed.");
}
// 创建 CacheKey 对象
CacheKey cacheKey = new CacheKey();
// 将 MappedStatemen t的 id 添加到 CacheKey 对象中
cacheKey.update(ms.getId());
// 将 offset 添加到 CacheKey 对象中
cacheKey.update(rowBounds.getOffset());
// 将 limit 添加到 CacheKey 对象中
cacheKey.update(rowBounds.getLimit());
// 将 sql 添加到 CacheKey 对象中
cacheKey.update(boundSql.getSql());
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
// 获取用户传入的实参,并添加到 CacheKey 对象中
for (ParameterMapping parameterMapping : parameterMappings) {
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) {
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
// 将实参添加到 CacheKey 对象中
cacheKey.update(value);
}
}
// 如果 environment 的 id 不为空,则将其添加到 CacheKey 中
if (configuration.getEnvironment() != null) {
// issue #176
cacheKey.update(configuration.getEnvironment().getId());
}
return cacheKey;
}

SimpleExecutor 查询

定位: org.apache.ibatis.executor.SimpleExecutor#doQuery

作用: 默认实现的查询

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
@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对象,实际返回的是RoutingStatementHandler对象
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
// 完成Statement的创建和初始化
stmt = prepareStatement(handler, ms.getStatementLog());
// 调用query方法执行sql语句,并通过ResultSetHandler完成结果集的映射
return handler.query(stmt, resultHandler);
} finally {
// 关闭Statement对象
closeStatement(stmt);
}
}
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
// 获取数据库连接
Connection connection = getConnection(statementLog);
// 创建Statement对象
stmt = handler.prepare(connection, transaction.getTimeout());
// 处理占位符
handler.parameterize(stmt);
return stmt;
}
// org.apache.ibatis.session.Configuration#newStatementHandler
// 创建语句处理器
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
// 创建路由选择语句处理器
// 根据 MappedStatement 的配置,生成一个对应的 StatementHandler 对象
// SimpleStatementHandler, PreparedStatementHandler, CallableStatementHandler
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
// 引入插件
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}

PreparedStatementHandler 查询

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
// 准备语句
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
// 实例化 Statement
statement = instantiateStatement(connection);
// 设置超时
setStatementTimeout(statement, transactionTimeout);
// 设置读取条数
setFetchSize(statement);
return statement;
} catch (SQLException e) {
closeStatement(statement);
throw e;
} catch (Exception e) {
closeStatement(statement);
throw new ExecutorException("Error preparing statement. Cause: " + e, e);
}
}
// 执行数据库查询
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
// 执行 jdbc 的查询
ps.execute();
// 解析返回结果
return resultSetHandler.handleResultSets(ps);
}

多行查询(map)

定位: org.apache.ibatis.session.defaults.DefaultSqlSession#selectMap

作用: 根据条件查询, 返回 map 对象, key 为 @MapKey 指定列, value 为查询列对象; 与多行查询的不同点在于, 返回结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Override
public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey) {
return this.selectMap(statement, parameter, mapKey, RowBounds.DEFAULT);
}
// 核心selectMap
@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) {
// 循环用 DefaultMapResultHandler 处理每条记录
context.nextResultObject(o);
mapResultHandler.handleResult(context);
}
// 注意这个 DefaultMapResultHandler 里面存了所有已处理的记录(内部实现可能就是一个Map),最后再返回一个 Map
return mapResultHandler.getMappedResults();
}

游标查询

定位: org.apache.ibatis.session.defaults.DefaultSqlSession#selectCursor

作用: 根据条件查询, 返回游标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Override
public <T> Cursor<T> selectCursor(String statement, Object parameter) {
return selectCursor(statement, parameter, RowBounds.DEFAULT);
}
@Override
public <T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);
Cursor<T> cursor = executor.queryCursor(ms, wrapCollection(parameter), rowBounds);
registerCursor(cursor);
return cursor;
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}

CachingExecutor 游标查询

定位: org.apache.ibatis.executor.CachingExecutor#queryCursor

1
2
3
4
5
6
@Override
public <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException {
flushCacheIfRequired(ms);
// 没有二级缓存
return delegate.queryCursor(ms, parameter, rowBounds);
}

BaseExecutor 游标查询

定位: org.apache.ibatis.executor.BaseExecutor#queryCursor

1
2
3
4
5
@Override
public <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameter);
return doQueryCursor(ms, parameter, rowBounds, boundSql);
}

SimpleExecutor 游标查询

定位: org.apache.ibatis.executor.SimpleExecutor#doQueryCursor

1
2
3
4
5
6
7
8
9
@Override
protected <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql) throws SQLException {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, null, boundSql);
Statement stmt = prepareStatement(handler, ms.getStatementLog());
Cursor<E> cursor = handler.queryCursor(stmt);
stmt.closeOnCompletion();
return cursor;
}

PreparedStatementHandler 游标查询

定位: org.apache.ibatis.executor.statement.PreparedStatementHandler#queryCursor

1
2
3
4
5
6
@Override
public <E> Cursor<E> queryCursor(Statement statement) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.handleCursorResultSets(ps);
}