根据前文Mybatis源码分析-SqlSessionTemplate的简单分析,对于SqlSession的CURD操作都需要经过Executor接口的update/query方法,本文将分析下BaseExecutor如何解析执行sql语句
BaseExecutor-抽象类
其是Executor
接口的实现类但为抽象类,另外一个则为具体实现类为CachingExecutor
,主要是通过对象适配的设计模式在原来的executor上再附上缓存的属性,有兴趣的可自行查阅。先从构造函数看一发
protected BaseExecutor(Configuration configuration, Transaction transaction) {
//事务对象
this.transaction = transaction;
this.deferredLoads = new ConcurrentLinkedQueue();
this.localCache = new PerpetualCache("LocalCache");
this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache");
//表明exector的状态
this.closed = false;
//主文件属性,主要获取MappedStatement对象
this.configuration = configuration;
this.wrapper = this;
}
BaseExecutor#update()-SqlSession之insert/update/delete入口
具体源码如下
@Override
public int update(MappedStatement ms, Object parameter) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());
if (closed) {
throw new ExecutorException("Executor was closed.");
}
clearLocalCache();
//供子类复写执行CUD操作
return doUpdate(ms, parameter);
}
BaseExecutor#query()-SqlSession之select入口
具体源码如下
@Override
public List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
//获取绑定的sql,并将参数对象与sql语句的#{}一一对应
BoundSql boundSql = ms.getBoundSql(parameter);
//获取cacheKey供缓存,包含完整的语句、参数等
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
//比原先多传入CacheKey和BoundSql参数
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
具体的查询处理逻辑如下
@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类型的表达式进行处理,主要是针对mode=out类型的参数
//此参数主要是通过map来定义,直接从map中获取
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
//从数据库中获取并进行缓存处理,其也会调用子类需复写的doQuery()方法
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;
}
由上可知,BaseExecutor对CRUD操作均转化为对子类的doUpdate()/doQuery()方法的调用,并一般都会相应的结果进行缓存以免频繁请求数据库导致性能下降。本文则从SimpleExecutor子类来进行分析
SimpleExecutor
分别看下SimpleExecutor复写的doUpdate()和doQuery()方法,具体源码如下
doUpdate()
@Override
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Configuration configuration = ms.getConfiguration();
//创建StatementHandler来处理update()
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
//创建表达式对象Statement
Statement stmt = prepareStatement(handler, ms.getStatementLog());
return handler.update(stmt);
}
doQuery()
@Override
public List doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Configuration configuration = ms.getConfiguration();
//创建StatementHandler来处理query()
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
//创建表达式对象Statement
Statement stmt = prepareStatement(handler, ms.getStatementLog());
return handler.query(stmt, resultHandler);
}
doUpdate()/doQuery()代码的执行逻辑一致,均是先创建
StatementHandler
对象,然后通过prepareStatement()
方法创建表达式对象,供前者调用处理update/query方法
SimpleExecutor#prepareStatement()-创建预表达式对象
逻辑如下
//handler对象对应的为RoutingStatementHandler对象,其实也是个适配管理类
//可根据MappedStatement的statementType来确定表达式处理handler类,后续讲解
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
//获取连接对象,如果该日志等级为debug,则会打印相应的处理日志,采用代理实现
Connection connection = getConnection(statementLog);
//创建真实的Statement对象,比如SimpleStatement/PreparedStatement/CallableStatement
stmt = handler.prepare(connection, transaction.getTimeout());
//请求参数,常用在preparedStatement用来设置相应的请求参数
handler.parameterize(stmt);
return stmt;
}
小结
BaseExecutor抽象类提供了对CRUD操作的入口,并带有缓存效应,子类只需要复写doUpdate()和doQuery()抽象方法即可
SimpleExecutor-简单的处理实现类,即基本每次对相同的sql语句都会创建新的Statement对象;ReuseExecutor-复用处理实现类,即对相同的sql语句会缓存Statement对象;BatchExecutor-批处理实现类
最终获取Statement对象以及执行sql语句的解释权在于
StatementHandler
接口,详情看下节内容