解析基于Mybatis 3.4.4 Release,近几个版本应该都适用。
Mybatis一级缓存默认是开启的,缓存数据时存储在Executor中,而Executor则被SqlSession持有。
我们先来看生成SqlSession和Executor实例的过程:
public class DefaultSqlSessionFactory implements SqlSessionFactory {
//通过连接池生成SqlSession对象
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);
//生成执行器Executor
final Executor executor = configuration.newExecutor(tx, execType);
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();
}
}
}
public class Configuration {
// 若未显式指定,defaultExecutorType 默认值为 ExecutorType.SIMPLE
protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
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;
}
}
ReuseExecutor,SimpleExecutor 都直接继承自BaseExecutor,他们在实例化时都会调用BaseExecutor的唯一构造方法:
public abstract class BaseExecutor implements Executor {
......
protected Transaction transaction;
protected Executor wrapper;
protected PerpetualCache localCache;
protected Configuration configuration;
protected BaseExecutor(Configuration configuration, Transaction transaction) {
this.transaction = transaction;
this.deferredLoads = new ConcurrentLinkedQueue();
// 初始化localCache 属性,生成一个PerpetualCache对象
this.localCache = new PerpetualCache("LocalCache");
this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache");
this.closed = false;
this.configuration = configuration;
this.wrapper = this;
}
}
public class PerpetualCache implements Cache {
private String id;
private Map
PerpetualCache 持有一个HashMap类型的属性,可作为一个缓存实现。
接下来我们看DefaultSqlSession在增删改查时的缓存操作。
public class DefaultSqlSession implements SqlSession {
public List selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);
// 查询操作借助executor对象来执行query实现
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
}
public abstract class BaseExecutor implements Executor {
public List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameter);
// 创建一个CacheKey对象,用于缓存的Key
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
// 创建CacheKey,将查询时的参数,SQL字符串,分页数据等都填充进去
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
if (closed) {
throw new ExecutorException("Executor was closed.");
}
CacheKey cacheKey = new CacheKey();
cacheKey.update(ms.getId());
cacheKey.update(rowBounds.getOffset());
cacheKey.update(rowBounds.getLimit());
cacheKey.update(boundSql.getSql());
List 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;
}
}
CacheKey 创建时会关联Mapper的id,也就是方法名称,Offset,Limit,SQL语句和SQL的参数。
CacheKey 重写了equals和hashCode方法,当两个CacheKey 这些数据完全相同时,就能 以 后一个CacheKey 为Key获取到前一个CacheKey 存入的Value。
public abstract class BaseExecutor implements Executor {
public List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
......
List list;
try {
queryStack++;
// 查询前先勇CacheKey 从localCache获取Value,如果有值就不用再查数据库了
list = resultHandler == null ? (List) localCache.getObject(key) : null;
if (list != null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
......
return list;
}
}
当DefaultSqlSession Insert,Update,Delete时,会调用Eexcutor的Update方法:
public abstract class BaseExecutor implements Executor {
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();
return doUpdate(ms, parameter);
}
public void clearLocalCache() {
if (!closed) {
// 清空Cache
localCache.clear();
localOutputParameterCache.clear();
}
}
}
当DefaultSqlSession Commit或者Rollback时,会调用Executor相应的Commit和Rollback方法:
public abstract class BaseExecutor implements Executor {
@Override
public void commit(boolean required) throws SQLException {
if (closed) {
throw new ExecutorException("Cannot commit, transaction is already closed");
}
clearLocalCache(); // 清空Cache
flushStatements();
if (required) {
transaction.commit();
}
}
@Override
public void rollback(boolean required) throws SQLException {
if (!closed) {
try {
clearLocalCache(); // 清空Cache
flushStatements(true);
} finally {
if (required) {
transaction.rollback();
}
}
}
}
}
他们都会清空缓存。
总结: