SqlSessionFactory sqlSessionFactory =
new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
从以上代码来看,SqlSession是Mybatis提供操作数据库的API,但是从SqlSession源代码来看真正执行SQL的是Executor组件。Executor接口中定义了操作shujuk的增删改查方法,其中query和queryCursor方法用于执行查询类SQL,update方法用于执行插入、删除和修改类SQL。
如上图所示,Executor接口有BaseExecutor和CachingExecutor两个直接实现类,然后SimpleExecutor、ReuseExecutor和SimpleExecutor三个类又继承自BaseExecutor抽象类。根据源代码和UML来看Executor组件使用了模板方法设计模式。
public interface Executor {
int update(MappedStatement ms, Object parameter) throws SQLException;
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
<E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;
List<BatchResult> flushStatements() throws SQLException;
void commit(boolean required) throws SQLException;
void rollback(boolean required) throws SQLException;
CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);
boolean isCached(MappedStatement ms, CacheKey key);
void clearLocalCache();
void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType);
Transaction getTransaction();
void close(boolean forceRollback);
boolean isClosed();
void setExecutorWrapper(Executor executor);
}
Executor接口定义了执行查询SQL、执行更新SQL、提交、回滚和关闭连接等操作数据库和事务的方法。
以SqlSession接口DefaultSqlSession实现类的selectList方法为切入口,首先从configuration配置类中获取当前SQL对应的MappedStatement,然后调用executor的query方法来执行SQL。
private <E> List<E> selectList(
String statement, Object parameter,
RowBounds rowBounds, ResultHandler handler) {
try {
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();
}
}
BaseExecutor抽象类实现了Executor接口中定义的方法的执行流程及通用的处理逻辑,但是其中核心的处理逻辑交给子类来实现,是典型的模板方法模式。
@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.");
}
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
queryStack++;
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
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
clearLocalCache();
}
}
return list;
}
以query()方法源代码为例,BaseExecutor抽象类实现逻辑如下:
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;
}
Mybatis提供了SimpleExecutor、ResueExecutor和BatchExecutor三种Executor实现,这些Executor都继承至BaseExecutor抽象类,并实现了其中定义的抽象方法,以完成数据库的操作。
SimpleExecutor顾名思义就是提供最基础功能的Executor,它能够完成基本的增删改查;ResueExecutor缓存了JDBC的Statement对象,以便提高执行效率;BatchExecutor会将通过同一个Mapper执行的update、insert和delete操作转换为批量执行。
Mybatis还提供了一个带有缓存功能的CachingExecutor,CachingExecutor直接实现了Executor接口,并不像SimpleExecutor、ResueExecutor和BatchExecutor那样继承自BaseExecutor抽象类,所以我们可以将CachingExecutor当做另一条线的Executor。
@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) {
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, boundSql);
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
// delegate为真正执行逻辑的executor
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);
}
从CachingExecutor类中query()方法的源代码来看,CachingExecutor在执行查询逻辑之前做了一系列的缓存操作。在从缓存中没有获取到数据的时候,才会通过调用真正的Executor来执行对应的查询操作,由此可见,CachingExecutor使用了装饰器模式。