2.8 第二个 invoke
紧接上文...............
PlainMethodInvoker.invoke实际调用的是内部成员mapperMethod的execute(sqlSession, args)方法。
2.9 判断操作类型
MapperMethod中的execute方法执行操作,会根据不同的操作类型执行不同的方法。
/**
* 执行增删改查
*
* @param sqlSession SqlSession
* @param args 实际参数 UserQuery(userId=null, userName=null, userName_like=null, loginName=zhangwei, phone=null, email=null)
* @return
*/
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
// 获取操作类型
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
// 查询操作
case SELECT:
// 根据不同的返回类型 ,执行不同方法
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 if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
if (method.returnsOptional()
&& (result == null || !method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
// 返回的结果集
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
2.10 查询多个结果集(executeForMany)
因为我们查询的是list,所有会进入executeForMany方法进行查询操作。
/**
* 查询多个结果集
*
* @param sqlSession SqlSession
* @param args args 参数集合
*/
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
List<E> result;
// 将args转为SqlCommandParam
Object param = method.convertArgsToSqlCommandParam(args);
// RowBounds 分页查询
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
result = sqlSession.selectList(command.getName(), param, rowBounds);
} else {
// 调用sqlSession查询方法
result = sqlSession.selectList(command.getName(), param);
}
// issue #510 Collections & arrays support
// 处理返回数组 当返回的List 和方法返回类型不太匹配时
if (!method.getReturnType().isAssignableFrom(result.getClass())) {
if (method.getReturnType().isArray()) {
return convertToArray(result);
} else {
return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
}
}
return result;
}
2.11 调用 sqlSession
第2.10步中,调用了 sqlSession.selectList方法,进行最终的查询操作。
/**
* 查询列表
*
* @param statement MappedStatement
* @param parameter 参数
* @param rowBounds 分页
* @param handler 结果处理器
* @return List
*/
private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
try {
// 获取MappedStatement
MappedStatement ms = configuration.getMappedStatement(statement);
// 执行器查询
return executor.query(ms, wrapCollection(parameter), rowBounds, handler);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
2.12 进入执行器
之前说过,最终的查询是由执行器去进行操作了的,selectLis方法实际调用的是执行器的query方法。因为我们开启了缓存,所以执行器是CachingExecutor。
/**
* 缓存执行器查询
*/
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
// 获取BoundSql
BoundSql boundSql = ms.getBoundSql(parameter);
// 获取缓存的Key
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
// 查询
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
2.13 获取 BoundSql 对象
执行器进行query查询时,第一步是通过MappedStatement获取了BoundSql 对象。
/**
* 获取 BoundSql
*/
public BoundSql getBoundSql(Object parameterObject) {
// 通过SqlSource获取BoundSql对象
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
// 参数映射
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings == null || parameterMappings.isEmpty()) {
boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
}
// 检查参数映射中的嵌套结果映射
for (ParameterMapping pm : boundSql.getParameterMappings()) {
String rmId = pm.getResultMapId();
if (rmId != null) {
ResultMap rm = configuration.getResultMap(rmId);
if (rm != null) {
hasNestedResultMaps |= rm.hasNestedResultMaps();
}
}
}
return boundSql;
}
BoundSql 对象是通过MappedStatement中的sqlSource对象去获取的。SqlSource是一个接口,我们这里是动态查询,所以会调用DynamicSqlSource。
// 通过SqlSource获取BoundSql对象
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
//得到绑定的SQL
@Override
public BoundSql getBoundSql(Object parameterObject) {
//生成一个动态上下文
DynamicContext context = new DynamicContext(configuration, parameterObject);
//这里SqlNode.apply只是将${}这种参数替换掉,并没有替换#{}这种参数
rootSqlNode.apply(context);
//调用SqlSourceBuilder
SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
//SqlSourceBuilder.parse,注意这里返回的是StaticSqlSource,解析完了就把那些参数都替换成?了,也就是最基本的JDBC的SQL写法
SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
//看似是又去递归调用SqlSource.getBoundSql,其实因为是StaticSqlSource,所以没问题,不是递归调用
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
for (Map.Entry<String, Object> entry : context.getBindings().entrySet()) {
boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());
}
return boundSql;
}
最后返回BoundSql对象:
BoundSql 中就是对解析后的sql描述,包括对动态标签的解析,并且将 #{} 解析为占位符 ? ,还包含参数的描述信息。这个类没有什么复杂操作,可以看作是解析后的sql描述对象。
/**
* An actual SQL String got from an {@link SqlSource} after having processed any dynamic content.
* The SQL may have SQL placeholders "?" and an list (ordered) of an parameter mappings
* with the additional information for each parameter (at least the property name of the input object to read
* the value from).
* <p>
* Can also have additional parameters that are created by the dynamic language (for loops, bind...).
*
* 经过处理一些动态sql的部分获取到的真实sql,这个sql可能还有占位符?和一个参数映射的有序集合,并且还有每个参数的额外信息
* @author Clinton Begin
*/
public class BoundSql {
// 最终解析的sql,Mybatis将#{}和${}解析后的sql,其中#{}会被解析为?
private final String sql;
// 参数映射
private final List<ParameterMapping> parameterMappings;
// 参数对象
private final Object parameterObject;
// 额外的参数
private final Map<String, Object> additionalParameters;
// 元数据参数
private final MetaObject metaParameters;
public BoundSql(Configuration configuration, String sql, List<ParameterMapping> parameterMappings, Object parameterObject) {
this.sql = sql;
this.parameterMappings = parameterMappings;
this.parameterObject = parameterObject;
this.additionalParameters = new HashMap<>();
this.metaParameters = configuration.newMetaObject(additionalParameters);
}
public String getSql() {
return sql;
}
public List<ParameterMapping> getParameterMappings() {
return parameterMappings;
}
public Object getParameterObject() {
return parameterObject;
}
public boolean hasAdditionalParameter(String name) {
String paramName = new PropertyTokenizer(name).getName();
return additionalParameters.containsKey(paramName);
}
public void setAdditionalParameter(String name, Object value) {
metaParameters.setValue(name, value);
}
public Object getAdditionalParameter(String name) {
return metaParameters.getValue(name);
}
}
2.14 获取缓存的 Key
获取BoundSql 对象后,下一步就是获取缓存的Key,在cache中唯一确定一个缓存项需要使用缓存项的key,Mybatis中因为涉及到动态SQL等多方面因素,其缓存项的key不等仅仅通过一个String表示,所以MyBatis 提供了CacheKey类来表示缓存项的key,在一个CacheKey对象中可以封装多个影响缓存项的因素。
CacheKey key = this.createCacheKey(ms, parameterObject, rowBounds, boundSql);
Key:
87892607:-776139895:org.pearl.mybatis.demo.dao.UserMapper.selectDynamicUserList:0:2147483647:SELECT
*
FROM
base_user
WHERE base_user.login_name = ?:zhangwei:development
2.15 执行查询
获取了key后,最终执行器进行query查询:
/**
* 查询
*/
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
// 获取缓存对象
Cache cache = ms.getCache();
// /查CacheKey,查不到再委托给实际的执行器去查
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) {
// 查询
list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
继续调用BaseExecutor的query:
@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.");
}
//先清局部缓存,再查询.但仅查询堆栈为0,才清。为了处理递归调用
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
//加一,这样递归调用到上面的时候就不会再清局部缓存了
queryStack++;
//先根据cachekey从localCache去查
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
//若查到localCache缓存,处理localOutputParameterCache
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
//如果是STATEMENT,清本地缓存
clearLocalCache();
}
}
return list;
}
2.16 数据库查询
先去查询二级缓存,二级没有,查询本地缓存,本地缓存中还没有,才会从数据库进行查询。
//从数据库查
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);
//如果是存储过程,OUT参数也加入缓存
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
继续调用SimpleExecutor的doQuery方法。Statement 是java.sql中的接口,所以底层还是用的原生的JDBC,newStatementHandler会创建StatementHandler,是四大组件之一。StatementHandler创建时也会使用拦截器进行包装。
//select
@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
//这里看到ResultHandler传入了
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
//准备语句
stmt = prepareStatement(handler, ms.getStatementLog());
//StatementHandler.query
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
之后通过PreparedStatement的execute查询:
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement)statement;
ps.execute();
return this.resultSetHandler.handleResultSets(ps);
}
2.17 结果处理器
Statement 进行操作后,结果处理器会对Statement 进行处理,封装结果集,然后返回,整个流程就走的差不多了。
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(this.mappedStatement.getId());
List<Object> multipleResults = new ArrayList();
int resultSetCount = 0;
ResultSetWrapper rsw = this.getFirstResultSet(stmt);
List<ResultMap> resultMaps = this.mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
this.validateResultMapsCount(rsw, resultMapCount);
while(rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = (ResultMap)resultMaps.get(resultSetCount);
this.handleResultSet(rsw, resultMap, multipleResults, (ResultMapping)null);
rsw = this.getNextResultSet(stmt);
this.cleanUpAfterHandlingResultSet();
++resultSetCount;
}
String[] resultSets = this.mappedStatement.getResultSets();
if (resultSets != null) {
while(rsw != null && resultSetCount < resultSets.length) {
ResultMapping parentMapping = (ResultMapping)this.nextResultMaps.get(resultSets[resultSetCount]);
if (parentMapping != null) {
String nestedResultMapId = parentMapping.getNestedResultMapId();
ResultMap resultMap = this.configuration.getResultMap(nestedResultMapId);
this.handleResultSet(rsw, resultMap, (List)null, parentMapping);
}
rsw = this.getNextResultSet(stmt);
this.cleanUpAfterHandlingResultSet();
++resultSetCount;
}
}
return this.collapseSingleResultList(multipleResults);
}
注意: 这里面的细节后续介绍(主要是四大核心组件).....因为实在太多了.....有参数处理器,结果处理器,类型处理器等.....