先看一个示例:
看一下这段代码具体的执行过程:
一、SqlSession的创建:
通过SqlSessionFactory获取SqlSession的代码,sqlSessionFactory.openSession():
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);
// 创建执行器
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();
}
}
根据创建好的执行器作为构造函数参数,生成一个SqlSession(DefaultSqlSession)。
说明:个人觉得以上代码中事务对象存在的意义不是特别大,因为很少说mybatis单独使用,一般都是交给spring管理,这里仅仅说明mybatis脱离spring也可以单独管理事务。
创建执行器的代码,这里会根据传入的执行器类型来创建,如果开启了二级缓存,则使用CachingExecutor:
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 {
executor = new SimpleExecutor(this, transaction);
}
//如果开启了二级缓存
if (cacheEnabled) {
// 使用CachingExecutor装饰实际的执行器
executor = new CachingExecutor(executor);
}
// 为执行器增加拦截器,以启用各个拦截器的功能
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
二、代理对象的生成
这里接着上一篇说,在Mapper.xml解析的时候,会根据namespace的反射类型和代理工厂做了一个映射关系,如图所示:
那么通过接口获取代理实例就根据接口类型作为Key,去map中拿到代理工厂生成的jdk代理对象:
三、查询过程
代理对象的invoke方法:
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) { // 继承自Object的方法
// 直接执行原有方法
return method.invoke(this, args);
} else if (method.isDefault()) { // 默认方法
// 执行默认方法
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
// 找对应的 MapperMethod 对象
final MapperMethod mapperMethod = cachedMapperMethod(method);
// 调用 MapperMethod 中的execute方法
return mapperMethod.execute(sqlSession, args);
}
private MapperMethod cachedMapperMethod(Method method) {
return methodCache.computeIfAbsent(method, k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}
public MapperMethod(Class> mapperInterface, Method method, Configuration config) {
this.command = new SqlCommand(config, mapperInterface, method);
this.method = new MethodSignature(config, mapperInterface, method);
}
MethodSignature中封装了方法的一些信息,比如有无返回值,返回值的类型等等。
所以在mapperMethod.execute方法中,可以获取到SQL语句类型的枚举值和方法的一些信息:
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) { // 根据SQL语句类型,执行不同的操作
.....
.....
case SELECt: // 如果是查询语句
if (method.returnsVoid() && method.hasResultHandler()) { // 返回返回为void,且有结果处理器
// 使用结果处理器执行查询
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) { // 多条结果查询
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) { // map结果查询
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;
.....
}
.....
return result;
}
所以我们示例中的查询会走到多条结果查询的代码,如图所示:
这里根据Mapper.xml中namespace+标签ID从configuration中拿到MappedStatement对象,由于开启了二级缓存,所以这里的执行器是CachingExecutor,由它实现查询操作:
在上一篇中提到,如果标签中存在
1、会在ms.getBoundSql中去动态的解析这个DynamicSqlSource,把DynamicSqlSource中存在的树状的MixedSqlNode(一个List
2、生成缓存KEY
3、
执行器的继承关系
先根据KEY去缓存中查询,如果缓存中没有,再调用被包装的执行器的父类BaseExecutor中的query方法,去查询数据库,将查询出的数据放入缓存中,CachingExecutor.query方法代码如下:
@Override publicList 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) { // 该语句使用缓存且没有输出结果处理器 // 二级缓存不支持含有输出参数的CALLABLE语句,故在这里进行判断 ensureNoOutParams(ms, boundSql); @SuppressWarnings("unchecked") // 从缓存中读取结果 List list = (List ) tcm.getObject(cache, key); if (list == null) { // 缓存中没有结果 // 交给被包装的执行器执行 list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); // 缓存被包装执行器返回的结果 tcm.putObject(cache, key, list); } return list; } } // 交由被包装的实际执行器执行 return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); }
被包装的执行器(这里采用的是默认的执行器类型,即:SimpleExecutor)执行:
@Override publicList 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 list; try { queryStack++; // 尝试从一级缓存获取结果 list = resultHandler == null ? (List ) localCache.getObject(key) : null; if (list != null) { // 本地缓存中有结果,则对于CALLABLE语句还需要绑定到IN/INOUT参数上 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(); // 如果本地缓存的作用域为STATEMENT,则立刻清除本地缓存 if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) { clearLocalCache(); } } return list; }
查询数据库的代码:
privateList queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { List 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; }



