詳解Mybatis的緩存
Mybatis的緩存
mybatis是一個查詢數(shù)據(jù)庫的封裝框架,主要是封裝提供靈活的增刪改sql,開發(fā)中,service層能夠通過mybatis組件查詢和修改數(shù)據(jù)庫中表的數(shù)據(jù);作為查詢工具,mybatis有使用緩存,這里講一下mybatis的緩存相關(guān)源碼。
緩存
在計算機(jī)里面,任何信息都有源頭,緩存一般指源頭信息讀取后,放在內(nèi)存或者其他讀取較快的地方,下次讀取相同信息不去源頭查詢而是直接從內(nèi)存(或者能快速存取的硬件)讀取。這樣可以減少硬件使用,提高讀取速度。
mybatis也是這樣,查詢數(shù)據(jù)庫的數(shù)據(jù)之后,mybatis可以把查詢結(jié)果緩存到內(nèi)存,下次查詢?nèi)绻樵冋Z句相同,并且查詢相關(guān)的表的數(shù)據(jù)沒被修改過,就可以直接返回緩存中的結(jié)果,而不用去查詢數(shù)據(jù)庫的語句,有效節(jié)省了時間。
簡單看一下mybatis一級緩存和二級緩存相關(guān)源碼,學(xué)習(xí)使用
一級緩存
通過查看源碼可知,一級緩存是綁定sqSsession中的,所以每次查詢sqlSession不同就失效,相同的sqlSession可以使用一級緩存。
mybatis默認(rèn)sqlsession:org.apache.ibatis.session.defaults.DefaultSqlSession
構(gòu)造方法中傳入executor(查詢執(zhí)行對象)
public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
this.configuration = configuration;
this.executor = executor;
this.dirty = false;
this.autoCommit = autoCommit;
}
executor中攜帶一級緩存成員:
protected BaseExecutor(Configuration configuration, Transaction transaction) {
this.transaction = transaction;
this.deferredLoads = new ConcurrentLinkedQueue<>();
this.localCache = new PerpetualCache("LocalCache"); //默認(rèn)一級緩存
this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache");
this.closed = false;
this.configuration = configuration;
this.wrapper = this;
}
查詢使用一級緩存邏輯
org.apache.ibatis.executor.BaseExecutor.query()
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());
List<E> list;
try {
queryStack++;
//localCache 一級緩存
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
//先從一級緩存中獲取,key是通過sql語句生成
if (list != null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
// 如果緩存中沒有 才從數(shù)據(jù)庫查詢
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
return list;
}
//從數(shù)據(jù)庫讀取數(shù)據(jù)
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);//返回查詢結(jié)果之前,先放入一級緩存 刷新
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
二級緩存
二級緩存mapper中的,默認(rèn)是開啟的,但需要在映射文件mapper.xml中添加<cache/>標(biāo)簽
<mapper namespace="userMapper"> <cache/><!-- 添加cache標(biāo)簽表示此mapper使用二級緩存 --> </mapper>
配置false可以關(guān)閉二級緩存
二級緩存的解析
org.apache.ibatis.builder.xml.XMLMapperBuilder
private void configurationElement(XNode context) {
try {
//...
cacheElement(context.evalNode("cache")); //解析cache標(biāo)簽
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
}
}
private void cacheElement(XNode context) {
if (context != null) { // if hava cache tag 如果有cache標(biāo)簽才執(zhí)行下面的邏輯
String type = context.getStringAttribute("type", "PERPETUAL");
Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
String eviction = context.getStringAttribute("eviction", "LRU");
Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);
Long flushInterval = context.getLongAttribute("flushInterval");
Integer size = context.getIntAttribute("size");
boolean readWrite = !context.getBooleanAttribute("readOnly", false);
boolean blocking = context.getBooleanAttribute("blocking", false);
Properties props = context.getChildrenAsProperties();
builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);//建立二級緩存
}
}
org.apache.ibatis.builder.MapperBuilderAssistant.useNewCache():
public Cache useNewCache(Class<? extends Cache> typeClass,
Class<? extends Cache> evictionClass,
Long flushInterval,
Integer size,
boolean readWrite,
boolean blocking,
Properties props) {
Cache cache = new CacheBuilder(currentNamespace)
.implementation(valueOrDefault(typeClass, PerpetualCache.class))
.addDecorator(valueOrDefault(evictionClass, LruCache.class))
.clearInterval(flushInterval)
.size(size)
.readWrite(readWrite)
.blocking(blocking)
.properties(props)
.build();
configuration.addCache(cache);//二級緩存賦值,如果cache標(biāo)簽為空,不會執(zhí)行此方法,currentCache為空
currentCache = cache;
return cache;
}
在映射文件mapper中如果沒有cache標(biāo)簽,不會執(zhí)行上面的useNewCache方法,cache為null,就不會使用二級緩存(相當(dāng)于失效)。
查詢使用二級緩存邏輯
org.apache.ibatis.executor.CachingExecutor :
@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) {//如果二級緩存對象不為空 嘗試在二級緩存中獲?。]有cache標(biāo)簽此對象就是空)
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, boundSql);
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key); //從二級緩存中獲取數(shù)據(jù)
if (list == null) {
list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); //如果為空,使用delegate查詢(BaseExecutor)
tcm.putObject(cache, key, list); // 查詢結(jié)果保存到二級緩存
}
return list;
}
}
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
二級緩存和一級緩存不用想,數(shù)據(jù)庫的數(shù)據(jù)被修改是要清空緩存的,不然數(shù)據(jù)有誤,至于怎么清空,是另一套邏輯了,mapper中的cache標(biāo)簽可以配置一些參數(shù),比如緩存定期清空。
一級二級緩存先后順序
mybatis默認(rèn)是先查詢二級緩存,沒有,再查看一級緩存,都為空,最后查詢數(shù)據(jù)庫
以上就是詳解Mybatis的緩存的詳細(xì)內(nèi)容,更多關(guān)于Mybatis的緩存的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
舉例講解Java設(shè)計模式編程中Decorator裝飾者模式的運(yùn)用
這篇文章主要介紹了Java設(shè)計模式編程中Decorator裝飾者模式的運(yùn)用,裝飾者模式就是給一個對象動態(tài)的添加新的功能,裝飾者和被裝飾者實現(xiàn)同一個接口,裝飾者持有被裝飾者的實例,需要的朋友可以參考下2016-05-05
SpringBoot程序打包失敗(.jar中沒有主清單屬性)
在學(xué)習(xí)SpringBoot,打包SpringBoot程序后,在cmd運(yùn)行出現(xiàn)了 某某某.jar中沒有注清單屬性,本文就來介紹一下原因以及解決方法,感興趣的可以了解一下2023-06-06
通過xml配置SpringMVC注解DispatcherServlet初始化過程解析
這篇文章主要為大家介紹了通過xml配置SpringMVC注解DispatcherServlet初始化過程解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10
Java+element實現(xiàn)excel的導(dǎo)入和導(dǎo)出
本文主要介紹了Java+element實現(xiàn)excel的導(dǎo)入和導(dǎo)出,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04
SpringMVC如何自定義響應(yīng)的HTTP狀態(tài)碼
這篇文章主要介紹了SpringMVC如何自定義響應(yīng)的HTTP狀態(tài)碼,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-11-11

