#yyds干货盘点# mybatis源码解读(cache包(缓存机制功能))

沉舟侧畔千帆进,病树前头万木春。这篇文章主要讲述#yyds干货盘点# mybatis源码解读:cache包(缓存机制功能)相关的知识,希望能为你提供帮助。
mybatis源码解读:cache包(缓存机制功能)
1.缓存机制在进行源码阅读时,通常可以以包为单位进行,因为包本身就是具有一定结构、功能的类的集合,但是,也总会有一些功能相对复杂,会横跨多个包。因此以功能为主线一次阅读多个包中的源码是必要的,能帮助我们理清功能实现。
mybatis基于cache包中提供的缓存实现了两级缓存机制。
1.一级缓存
mybatis的一级缓存又叫本地缓存,与它相关的配置项有2个。
1.在配置文件XML的setting节点,可选项有session与statement,分别对应了一次会话和一条语句,一级缓存的默认范围是session。

2.是在映射文件mapper.xml中的数据库操作节点内增加flushCache属性,默认值为false。可以设置为ture或false; 当设置为true时,mybatis会在改数据库操作执行前清空一,二级缓存。
一级缓存功能是由BaseExecutor类实现。BaseExecutor类作为实际执行器的基类,为所有实际执行器提供一些通用的基本功能,在这里增加缓存也就意味着每个实际执行器都具有着一级缓存。

public abstract class BaseExecutor implements Executor

private static final Log log = LogFactory.getLog(BaseExecutor.class);

protected Transaction transaction;
protected Executor wrapper;

protected ConcurrentLinkedQueue< DeferredLoad> deferredLoads;
// 查询操作的结果缓存
protected PerpetualCache localCache;
// Callable查询的输出参数缓存
protected PerpetualCache localOutputParameterCache;
protected Configuration configuration;

protected int queryStack;
private boolean closed;

/**
* 更新数据库数据,INSERT/UPDATE/DELETE三种操作都会调用该方法
* @param ms 映射语句
* @param parameter 参数对象
* @return 数据库操作结果
* @throws SQLException
*/
@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();
// 返回调用子类进行操作
return doUpdate(ms, parameter);


@Override
public List< BatchResult> flushStatements() throws SQLException
return flushStatements(false);


public List< BatchResult> flushStatements(boolean isRollBack) throws SQLException
if (closed)
throw new ExecutorException("Executor was closed.");

return doFlushStatements(isRollBack);


/**
* 执行查询操作
* @param ms 映射语句对象
* @param parameter 参数对象
* @param rowBounds 翻页限制
* @param resultHandler 结果处理器
* @param < E> 输出结果类型
* @return 查询结果
* @throws SQLException
*/
@Override
public < E> List< E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException
BoundSql boundSql = ms.getBoundSql(parameter);
// 生成缓存的键
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);


/**
* 查询数据库中的数据
* @param ms 映射语句
* @param parameter 参数对象
* @param rowBounds 翻页限制条件
* @param resultHandler 结果处理器
* @param key 缓存的键
* @param boundSql 查询语句
* @param < E> 结果类型
* @return 结果列表
* @throws SQLException
*/
@SuppressWarnings("unchecked")
@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)
// 本地缓存中有结果,则对于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;

因为localCache和localOutputParameterCache都是Executor的属性,而Executor归属sqlSession,因此第一级缓存的最大作用范围便是sqlSession,即一次会话。而insert,update,delete方法都对应了Executor的update方法,会引发一级缓存的更新。可见一级缓存就是BaseExcecutor中的两个PrepetualCache类型的属性(localCache和localOutputParameterCache),其作用范围有限,不支持各种装饰器的修饰,因此不能进行容量配置,清理策略设置及阻塞设置等。
2.二级缓存二级缓存的作用范围是一个命名空间(即一个mapper.xml映射文件),而且可以实现多个命名空间共享一个缓存,因此比一级缓存作用范围更广,更为灵活。


二级缓存原理:二级缓存功能由CacheingExecutor类实现,它是一个装饰器类, 能通过装饰实际执行器为它们增加二级缓存功能。mybatis会根据配置文件中的二级缓存开关配置用CacheingExecutor类装饰实际执行器。
【#yyds干货盘点# mybatis源码解读(cache包(缓存机制功能))】在Configuration类中的newExecutor方法中实现用CacheingExecutor类装饰实际执行器。


/**
* 主要内容分为以下几个部分:
* 1、大量的配置项,和与`< configuration> `标签中的配置对应
* 2、创建类型别名注册机,并向内注册了大量的类型别名
* 3、创建了大量Map,包括存储映射语句的Map,存储缓存的Map等,这些Map使用的是一种不允许覆盖的严格Map
* 4、给出了大量的处理器的创建方法,包括参数处理器、语句处理器、结果处理器、执行器。
*注意这里并没有真正创建,只是给出了方法。
*/

public class Configuration

// < environment> 节点的信息
protected Environment environment;

/**
* 创建一个执行器
* @param transaction 事务
* @param executorType 数据库操作类型
* @return 执行器
*/
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);

// 根据配置文件中settings节点cacheEnabled配置项确定是否启用缓存
if (cacheEnabled)// 如果配置启用缓存
// 使用CachingExecutor装饰实际执行器
executor = new CachingExecutor(executor);

// 为执行器增加拦截器(插件),以启用各个拦截器的功能
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;


    推荐阅读