先看一个示例:
看一下这段代码具体的执行过程:
一、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
public List 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
public List 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;
}
查询数据库的代码:
private List 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;
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)