mybatis的源代码相对于spring的来说简单了很多,对于初学者,可以先了解了mybatis的源码后再去了解spring的源码,本文主要来分析下Executor的内容
Executor介绍
Executor是mybatis的一个核心接口,所有的Mapper语句的执行都是通过Executor进行的。类结构图如下
1.Executor(顶层接口)
父接口,在此接口中定义了各种处理方法。具体如下:
代码语言:javascript复制public interface Executor {
ResultHandler NO_RESULT_HANDLER = null;
// 更新
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;
// 刷新statements
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 对象
Transaction getTransaction();
// 关闭
void close(boolean forceRollback);
// 是否关闭
boolean isClosed();
// 设置执行器增强器
void setExecutorWrapper(Executor executor);
}
2.BaseExecutor
BaseExecutor是一个抽象类,采用模板方法的设计模式。它实现了Executor接口,实现了执行器的基本功能。具体使用哪一个Executor则是可以在 mybatis 的 config.xml 中进行配置的。默认为SimpleExecutor;
代码语言:javascript复制<settings>
<!--SIMPLE、REUSE、BATCH-->
<setting name="defaultExecutorType" value="SIMPLE"/>
</settings>
2.1构造方法
子类的构造方法会调用 BaseExecutor 的构造方法。默认都支持一级缓存;
代码语言:javascript复制 protected BaseExecutor(Configuration configuration, Transaction transaction) {
this.transaction = transaction;
this.deferredLoads = new ConcurrentLinkedQueue<DeferredLoad>();
// 一级缓存
this.localCache = new PerpetualCache("LocalCache");
this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache");
this.closed = false;
this.configuration = configuration;
this.wrapper = this;
}
2.2 update方法
BaseExecutor中实现了update方法,改方法执行DML操作的时候都会执行,通过源码我们会发现,执行DML操作前会清空一级缓存
代码语言:javascript复制 @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();
// 调用doUpdate方法执行
return doUpdate(ms, parameter);
}
2.3 query方法
查询操作会先在缓存中查询,缓存命中失败后再去数据中查询。
2.4 createCacheKey
查看缓存key的生成。了解cacheKey的组成。一级缓存通过 HashMap 实现,它的键对象根据SQL的ID,参数,SQL本身,分页参数以及JDBC的参数信息构成。
代码语言:javascript复制@Override
// 创建CacheKey对象
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
if (closed) {
throw new ExecutorException("Executor was closed.");
}
CacheKey cacheKey = new CacheKey();
// MappedStatement的id
cacheKey.update(ms.getId());
// 分页参数的offset
cacheKey.update(rowBounds.getOffset());
// 分页参数的limit
cacheKey.update(rowBounds.getLimit());
// SQL语句本身
cacheKey.update(boundSql.getSql());
// 传递给jdbc的参数
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
// mimic DefaultParameterHandler logic
for (ParameterMapping parameterMapping : parameterMappings) {
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) {
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
cacheKey.update(value);
}
}
if (configuration.getEnvironment() != null) {
// issue #176
cacheKey.update(configuration.getEnvironment().getId());
}
return cacheKey;
}
2.5 定义的抽象方法
定义的抽象方法有如下几个,交给实现类来实现。
代码语言:javascript复制// 定义的四个抽象方法,在去掉 do 前缀的相应方法中被调用
protected abstract int doUpdate(MappedStatement ms, Object parameter)
throws SQLException;
protected abstract List<BatchResult> doFlushStatements(boolean isRollback)
throws SQLException;
protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
throws SQLException;
protected abstract <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql)
throws SQLException;
BaseExecutor的实现类是在Configuration中创建出来的
代码语言:javascript复制 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) {
executor = new CachingExecutor(executor);
}
// 如果有拦截器 则会返回对应的代理类
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
3.SimpleExecutor
最简单的执行器,根据对应的sql直接执行即可,不会做一些额外的操作;拼接完SQL之后,直接交给 StatementHandler去执行。 每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。(可以是Statement或PrepareStatement对象) 我们选择doUpdate()方法来看下
代码语言:javascript复制 @Override
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
// 获取Statement处理器对象
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
// 获取具体的Statement对象
stmt = prepareStatement(handler, ms.getStatementLog());
// 处理器执行更新操作
return handler.update(stmt);
} finally {
closeStatement(stmt);
}
}
prepareStatement方法
代码语言:javascript复制 private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
// 获取连接通道
Connection connection = getConnection(statementLog);
// 创建Statement对象
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt);
return stmt;
}
prepare方法
所以BaseExecutor中具体的Statement可以是这三种情况。
4.BatchExecutor
执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理的;可以是Statement或PrepareStatement对象.
5.ReuseExecutor
可重用的执行器,重用的对象是Statement,也就是说该执行器会缓存同一个sql的Statement,省去Statement的重新创建,优化性能。内部的实现是通过一个HashMap来维护Statement对象的。由于当前Map只在该session中有效,所以使用完成后记得调用flushStatements来清除Map。
代码语言:javascript复制public class ReuseExecutor extends BaseExecutor {
private final Map<String, Statement> statementMap = new HashMap<String, Statement>();
// 调用父类构造器
public ReuseExecutor(Configuration configuration, Transaction transaction) {
super(configuration, transaction);
}
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
BoundSql boundSql = handler.getBoundSql();
String sql = boundSql.getSql();
if (hasStatementFor(sql)) {
// 如果缓存了该SQL,则返回其Statement对象
stmt = getStatement(sql);
applyTransactionTimeout(stmt);
} else {
// 如果没有缓存该SQL,则创建SQL的Statement,并加入缓存
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection, transaction.getTimeout());
putStatement(sql, stmt);
}
handler.parameterize(stmt);
return stmt;
}
// 是否缓存了这个 sql
private boolean hasStatementFor(String sql) {
try {
return statementMap.keySet().contains(sql) && !statementMap.get(sql).getConnection().isClosed();
} catch (SQLException e) {
return false;
}
}
// 返回指定sql的 Statement
private Statement getStatement(String s) {
return statementMap.get(s);
}
// 添加SQL和Statement
private void putStatement(String sql, Statement stmt) {
statementMap.put(sql, stmt);
}
}
6.CachingExecutor
启用于二级缓存时的执行器;采用静态代理;代理一个 Executor 对象。执行 update 方法前判断是否清空二级缓存;执行 query 方法前先在二级缓存中查询,命中失败再通过被代理类查询。
代码语言:javascript复制public class CachingExecutor implements Executor {
// 持有的 Executor,最终的操作都由该对象实现
private final Executor delegate;
private final TransactionalCacheManager tcm = new TransactionalCacheManager();
public CachingExecutor(Executor delegate) {
this.delegate = delegate;
delegate.setExecutorWrapper(this);
}
public int update(MappedStatement ms, Object parameterObject) throws SQLException {
this.flushCacheIfRequired(ms);
return this.delegate.update(ms, parameterObject);
}
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) {
this.flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
this.ensureNoOutParams(ms, boundSql);
List<E> list = (List)this.tcm.getObject(cache, key);
if (list == null) {
list = this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
this.tcm.putObject(cache, key, list);
}
return list;
}
}
return this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
// 是否清空二级缓存
private void flushCacheIfRequired(MappedStatement ms) {
Cache cache = ms.getCache();
if (cache != null && ms.isFlushCacheRequired()) {
this.tcm.clear(cache);
}
}
}