Mybatis源码分析之(七)Mybatis一级缓存和二级缓存的实现
文章目錄
- 一級(jí)緩存
- 二級(jí)緩存
- 總結(jié)
對(duì)于一名程序員,緩存真的很重要,而且緩存真的是老生常談的一個(gè)話(huà)題拉。因?yàn)樗谖覀兊拈_(kāi)發(fā)過(guò)程中真的是無(wú)處不在。今天LZ帶大家來(lái)看一下。Mybatis是怎么實(shí)現(xiàn)一級(jí)緩存和二級(jí)緩存的。(自帶的緩存機(jī)制)
一級(jí)緩存
存在BaseExecutor中,是全局的緩存,每次查詢(xún)后將值存入BaseExecutor的localCache中。key是由ms,parameter,rowBounds和boundSql一起生成的一個(gè)值。value就是查詢(xún)出來(lái)的結(jié)果。一旦有任何更新變動(dòng),就刪除整個(gè)localCache。
@Override//生成一級(jí)緩存的key的函數(shù),有興趣的看看,LZ不詳細(xì)解釋了,不是重點(diǎn)。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<ParameterMapping> parameterMappings = boundSql.getParameterMappings();TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();// mimic DefaultParameterHandler logicfor (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 #176cacheKey.update(configuration.getEnvironment().getId());}return cacheKey;} //查詢(xún)數(shù)據(jù)庫(kù)并存入一級(jí)緩存的語(yǔ)句private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {List<E> list;//先放一個(gè)值占位localCache.putObject(key, EXECUTION_PLACEHOLDER);try {//查詢(xún)數(shù)據(jù)list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);} finally {//刪除占位的localCache.removeObject(key);}//將查詢(xún)出來(lái)的結(jié)果存入一級(jí)緩存localCache.putObject(key, list);if (ms.getStatementType() == StatementType.CALLABLE) {//如果是存儲(chǔ)過(guò)程把參數(shù)存入localOutputParameterCachelocalOutputParameterCache.putObject(key, parameter);}return list;}@Override//更新變動(dòng)等語(yǔ)句刪除緩存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.");}//刪除一級(jí)緩存clearLocalCache();//執(zhí)行語(yǔ)句return doUpdate(ms, parameter);}@Override//刪除一級(jí)緩存public void clearLocalCache() {if (!closed) {localCache.clear();localOutputParameterCache.clear();}}所以一級(jí)緩存的作用級(jí)別是SESSION級(jí)別的,因?yàn)橐粋€(gè)session中存放一個(gè)Executor。而一級(jí)緩存放在Executor。
二級(jí)緩存
如果開(kāi)啟了二級(jí)緩存的話(huà),你的Executor將會(huì)被裝飾成CachingExecutor,二級(jí)緩存是MapperStatement級(jí)的緩存,也就是一個(gè)namespace就會(huì)有一個(gè)緩存,緩存是通過(guò)CachingExecutor來(lái)操作的。查詢(xún)出來(lái)的結(jié)果會(huì)存在statement中的cache中,若有更新,刪除類(lèi)的操作默認(rèn)就會(huì)清空該MapperStatement的cache(也可以通過(guò)修改xml中的屬性,讓它不執(zhí)行),不會(huì)影響其他的MapperStatement
@Overridepublic <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)throws SQLException {//獲得該MappedStatement的cacheCache cache = ms.getCache();if (cache != null) {//看是否需要清除cache(在xml中可以配置flushCache屬性決定何時(shí)清空cache)flushCacheIfRequired(ms);if (ms.isUseCache() && resultHandler == null) {//若開(kāi)啟了cache且resultHandler 為空ensureNoOutParams(ms, parameterObject, boundSql);@SuppressWarnings("unchecked")//從TransactionalCacheManager中取cacheList<E> list = (List<E>) tcm.getObject(cache, key);//若沒(méi)值if (list == null) {//訪問(wèn)數(shù)據(jù)庫(kù)list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);//存入cachetcm.putObject(cache, key, list); // issue #578 and #116}return list;}}return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);}@Overridepublic int update(MappedStatement ms, Object parameterObject) throws SQLException {//看是否需要清除cache(在xml中可以配置flushCache屬性決定何時(shí)清空cache)flushCacheIfRequired(ms);return delegate.update(ms, parameterObject);}private void flushCacheIfRequired(MappedStatement ms) {//獲得cacheCache cache = ms.getCache();if (cache != null && ms.isFlushCacheRequired()) { //若需要清除,則清除cachetcm.clear(cache);}}因同一個(gè)namespace下的MappedStatement的cache是同一個(gè),而TransactionalCacheManager中統(tǒng)一管理cache是里面的屬性transactionalCaches,該屬性以MappedStatement中的Cache為key,TransactionalCache對(duì)象為Value。即一個(gè)namespace對(duì)應(yīng)一個(gè)TransactionalCache。
public class TransactionalCacheManager {private Map<Cache, TransactionalCache> transactionalCaches = new HashMap<Cache, TransactionalCache>();…… } public class TransactionalCache implements Cache {private static final Log log = LogFactory.getLog(TransactionalCache.class);//就是對(duì)應(yīng)的namespace中的cacheprivate Cache delegate;//提交的時(shí)候清除cache的標(biāo)志位private boolean clearOnCommit;//待提交的集合private Map<Object, Object> entriesToAddOnCommit;//未查到的key存放的集合private Set<Object> entriesMissedInCache; }總結(jié)
一級(jí)緩存是sqlSession級(jí)別的緩存,存放在BaseExecutor中的localCache中。查詢(xún)就將結(jié)果緩存進(jìn)去,一旦有更新,刪除,插入類(lèi)的操作就清空緩存。不同的sqlSession之間的緩存是互相不影響的。
二級(jí)緩存是namespace級(jí)別的,可以理解為一個(gè)mapper.xml文件對(duì)應(yīng)一個(gè)二級(jí)緩存(不同的sqlSession之間的緩存是共享的),然后對(duì)緩存的操作是在.xml文件中的標(biāo)簽文件進(jìn)行控制的。比如下面的代碼
總結(jié)
以上是生活随笔為你收集整理的Mybatis源码分析之(七)Mybatis一级缓存和二级缓存的实现的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 学习Spring Boot:(十四)sp
- 下一篇: 天涯共此式 有AI月更圆