欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

深入了解MyBatis二級(jí)緩存

 更新時(shí)間:2018年12月24日 14:36:39   作者:isea533  
今天小編就為大家分享一篇關(guān)于深入了解MyBatis二級(jí)緩存,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧

一、創(chuàng)建Cache的完整過(guò)程

我們從SqlSessionFactoryBuilder解析mybatis-config.xml配置文件開(kāi)始:

Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);

然后是:

XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());

看parser.parse()方法:

parseConfiguration(parser.evalNode("/configuration"));

看處理Mapper.xml文件的位置:

mapperElement(root.evalNode("mappers"));

看處理Mapper.xml的XMLMapperBuilder:

XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, 
   resource, configuration.getSqlFragments());
mapperParser.parse();

繼續(xù)看parse方法:

configurationElement(parser.evalNode("/mapper"));

到這里:

String namespace = context.getStringAttribute("namespace");
if (namespace.equals("")) {
 throw new BuilderException("Mapper's namespace cannot be empty");
}
builderAssistant.setCurrentNamespace(namespace);
cacheRefElement(context.evalNode("cache-ref"));
cacheElement(context.evalNode("cache"));

從這里看到namespace就是xml中<mapper>元素的屬性。然后下面是先后處理的cache-ref和cache,后面的cache會(huì)覆蓋前面的cache-ref,但是如果一開(kāi)始cache-ref沒(méi)有找到引用的cache,他就不會(huì)被覆蓋,會(huì)一直到最后處理完成為止,最后如果存在cache,反而會(huì)被cache-ref覆蓋。這里是不是看著有點(diǎn)暈、有點(diǎn)亂?所以千萬(wàn)別同時(shí)配置這兩個(gè),實(shí)際上也很少有人會(huì)這么做。

看看MyBatis如何處理<cache/>:

private void cacheElement(XNode context) throws Exception {
  if (context != null) {
    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);
  }
}

從源碼可以看到MyBatis讀取了那些屬性,而且很容易可以到這些屬性的默認(rèn)值。

創(chuàng)建Java的cache對(duì)象方法為builderAssistant.useNewCache,我們看看這段代碼:

public Cache useNewCache(Class<? extends Cache> typeClass,
             Class<? extends Cache> evictionClass,
             Long flushInterval,
             Integer size,
             boolean readWrite,
             boolean blocking,
             Properties props) {
  typeClass = valueOrDefault(typeClass, PerpetualCache.class);
  evictionClass = valueOrDefault(evictionClass, LruCache.class);
  Cache cache = new CacheBuilder(currentNamespace)
      .implementation(typeClass)
      .addDecorator(evictionClass)
      .clearInterval(flushInterval)
      .size(size)
      .readWrite(readWrite)
      .blocking(blocking)
      .properties(props)
      .build();
  configuration.addCache(cache);
  currentCache = cache;
  return cache;
}

從調(diào)用該方法的地方,我們可以看到并沒(méi)有使用返回值cache,在后面的過(guò)程中創(chuàng)建MappedStatement的時(shí)候使用了currentCache。

二、使用Cache過(guò)程

在系統(tǒng)中,使用Cache的地方在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();

獲取cache后,先判斷是否有二級(jí)緩存。

只有通過(guò)<cache/>,<cache-ref/>或@CacheNamespace,@CacheNamespaceRef標(biāo)記使用緩存的Mapper.xml或Mapper接口(同一個(gè)namespace,不能同時(shí)使用)才會(huì)有二級(jí)緩存。

 if (cache != null) {

如果cache存在,那么會(huì)根據(jù)sql配置(<insert>,<select>,<update>,<delete>的flushCache屬性來(lái)確定是否清空緩存。

  flushCacheIfRequired(ms);

然后根據(jù)xml配置的屬性u(píng)seCache來(lái)判斷是否使用緩存(resultHandler一般使用的默認(rèn)值,很少會(huì)null)。

  if (ms.isUseCache() && resultHandler == null) {

確保方法沒(méi)有Out類(lèi)型的參數(shù),mybatis不支持存儲(chǔ)過(guò)程的緩存,所以如果是存儲(chǔ)過(guò)程,這里就會(huì)報(bào)錯(cuò)。

   ensureNoOutParams(ms, parameterObject, boundSql);

沒(méi)有問(wèn)題后,就會(huì)從cache中根據(jù)key來(lái)取值:

   @SuppressWarnings("unchecked")
   List<E> list = (List<E>) tcm.getObject(cache, key);

如果沒(méi)有緩存,就會(huì)執(zhí)行查詢,并且將查詢結(jié)果放到緩存中。

   if (list == null) {
    list = delegate.<E>query(ms, parameterObject, 
      rowBounds, resultHandler, key, boundSql);
    tcm.putObject(cache, key, list); // issue #578 and #116
   }

返回結(jié)果

   return list;
  }
 }

沒(méi)有緩存時(shí),直接執(zhí)行查詢

 return delegate.<E>query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

在上面的代碼中tcm.putObject(cache, key, list);這句代碼是緩存了結(jié)果。但是實(shí)際上直到sqlsession關(guān)閉,MyBatis才以序列化的形式保存到了一個(gè)Map(默認(rèn)的緩存配置)中。

三、Cache使用時(shí)的注意事項(xiàng)

1. 只能在【只有單表操作】的表上使用緩存

不只是要保證這個(gè)表在整個(gè)系統(tǒng)中只有單表操作,而且和該表有關(guān)的全部操作必須全部在一個(gè)namespace下。

2. 在可以保證查詢遠(yuǎn)遠(yuǎn)大于insert,update,delete操作的情況下使用緩存

這一點(diǎn)不需要多說(shuō),所有人都應(yīng)該清楚。記住,這一點(diǎn)需要保證在1的前提下才可以!

四、避免使用二級(jí)緩存

可能會(huì)有很多人不理解這里,二級(jí)緩存帶來(lái)的好處遠(yuǎn)遠(yuǎn)比不上他所隱藏的危害。

  • 緩存是以namespace為單位的,不同namespace下的操作互不影響。
  • insert,update,delete操作會(huì)清空所在namespace下的全部緩存。
  • 通常使用MyBatis Generator生成的代碼中,都是各個(gè)表獨(dú)立的,每個(gè)表都有自己的namespace。

為什么避免使用二級(jí)緩存

在符合【Cache使用時(shí)的注意事項(xiàng)】的要求時(shí),并沒(méi)有什么危害。

其他情況就會(huì)有很多危害了。

針對(duì)一個(gè)表的某些操作不在他獨(dú)立的namespace下進(jìn)行。

例如在UserMapper.xml中有大多數(shù)針對(duì)user表的操作。但是在一個(gè)XXXMapper.xml中,還有針對(duì)user單表的操作。

這會(huì)導(dǎo)致user在兩個(gè)命名空間下的數(shù)據(jù)不一致。如果在UserMapper.xml中做了刷新緩存的操作,在XXXMapper.xml中緩存仍然有效,如果有針對(duì)user的單表查詢,使用緩存的結(jié)果可能會(huì)不正確。

更危險(xiǎn)的情況是在XXXMapper.xml做了insert,update,delete操作時(shí),會(huì)導(dǎo)致UserMapper.xml中的各種操作充滿未知和風(fēng)險(xiǎn)。

有關(guān)這樣單表的操作可能不常見(jiàn)。但是你也許想到了一種常見(jiàn)的情況。

多表操作一定不能使用緩存

為什么不能?

首先不管多表操作寫(xiě)到那個(gè)namespace下,都會(huì)存在某個(gè)表不在這個(gè)namespace下的情況。

例如兩個(gè)表:role和user_role,如果我想查詢出某個(gè)用戶的全部角色role,就一定會(huì)涉及到多表的操作。

<select id="selectUserRoles" resultType="UserRoleVO">
 select * from user_role a,role b where a.roleid = b.roleid and a.userid = #{userid}
</select>

像上面這個(gè)查詢,你會(huì)寫(xiě)到那個(gè)xml中呢??

不管是寫(xiě)到RoleMapper.xml還是UserRoleMapper.xml,或者是一個(gè)獨(dú)立的XxxMapper.xml中。如果使用了二級(jí)緩存,都會(huì)導(dǎo)致上面這個(gè)查詢結(jié)果可能不正確。

如果你正好修改了這個(gè)用戶的角色,上面這個(gè)查詢使用緩存的時(shí)候結(jié)果就是錯(cuò)的。

這點(diǎn)應(yīng)該很容易理解。

在我看來(lái),就以MyBatis目前的緩存方式來(lái)看是無(wú)解的。多表操作根本不能緩存。

如果你讓他們都使用同一個(gè)namespace(通過(guò)<cache-ref>)來(lái)避免臟數(shù)據(jù),那就失去了緩存的意義。

看到這里,實(shí)際上就是說(shuō),二級(jí)緩存不能用。整篇文章介紹這么多也沒(méi)什么用了。

五、挽救二級(jí)緩存?

想更高效率的使用二級(jí)緩存是解決不了了。

但是解決多表操作避免臟數(shù)據(jù)還是有法解決的。解決思路就是通過(guò)攔截器判斷執(zhí)行的sql涉及到那些表(可以用jsqlparser解析),然后把相關(guān)表的緩存自動(dòng)清空。但是這種方式對(duì)緩存的使用效率是很低的。

設(shè)計(jì)這樣一個(gè)插件是相當(dāng)復(fù)雜的,既然我沒(méi)想著去實(shí)現(xiàn),就不廢話了。

最后還是建議,放棄二級(jí)緩存,在業(yè)務(wù)層使用可控制的緩存代替更好。

推薦:集成 Spring Redis 緩存
http://www.dbjr.com.cn/article/153343.htm

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對(duì)腳本之家的支持。如果你想了解更多相關(guān)內(nèi)容請(qǐng)查看下面相關(guān)鏈接

相關(guān)文章

  • 淺談Java中hashCode的正確求值方法

    淺談Java中hashCode的正確求值方法

    這篇文章主要介紹了淺談Java中hashCode的正確求值方法,分享了相關(guān)代碼示例,小編覺(jué)得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下
    2018-02-02
  • Java實(shí)現(xiàn)樹(shù)形菜單的方法總結(jié)

    Java實(shí)現(xiàn)樹(shù)形菜單的方法總結(jié)

    當(dāng)我們想要展示層級(jí)結(jié)構(gòu),如文件目錄、組織結(jié)構(gòu)或分類(lèi)目錄時(shí),樹(shù)形菜單是一個(gè)直觀且有效的解決方案,本文為大家整理了java中幾種常見(jiàn)方法,希望對(duì)大家有所幫助
    2023-08-08
  • springboot做代理分發(fā)服務(wù)+代理鑒權(quán)的實(shí)現(xiàn)過(guò)程

    springboot做代理分發(fā)服務(wù)+代理鑒權(quán)的實(shí)現(xiàn)過(guò)程

    這篇文章主要介紹了springboot做代理分發(fā)服務(wù)+代理鑒權(quán)的實(shí)現(xiàn)過(guò)程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-01-01
  • Spring:如何使用枚舉參數(shù)

    Spring:如何使用枚舉參數(shù)

    這篇文章主要介紹了springboot枚舉類(lèi)型傳遞的步驟,幫助大家更好的理解和學(xué)習(xí)使用springboot,感興趣的朋友可以了解下,希望能給你帶來(lái)幫助
    2021-08-08
  • java根據(jù)List內(nèi)對(duì)象的屬性排序方法

    java根據(jù)List內(nèi)對(duì)象的屬性排序方法

    下面小編就為大家分享一篇java根據(jù)List內(nèi)對(duì)象的屬性排序方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-01-01
  • java中的forkjoin框架的使用

    java中的forkjoin框架的使用

    這篇文章主要介紹了java中的fork join框架的使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-03-03
  • java設(shè)計(jì)模式之實(shí)現(xiàn)對(duì)象池模式示例分享

    java設(shè)計(jì)模式之實(shí)現(xiàn)對(duì)象池模式示例分享

    對(duì)象池模式經(jīng)常用在頻繁創(chuàng)建、銷(xiāo)毀對(duì)象(并且對(duì)象創(chuàng)建、銷(xiāo)毀開(kāi)銷(xiāo)很大)的場(chǎng)景,比如數(shù)據(jù)庫(kù)連接池、線程池、任務(wù)隊(duì)列池等。本代碼簡(jiǎn)單,沒(méi)有限制對(duì)象池大小
    2014-02-02
  • Java的ArrayList擴(kuò)容源碼解析

    Java的ArrayList擴(kuò)容源碼解析

    這篇文章主要介紹了Java的ArrayList擴(kuò)容源碼解析,通過(guò)動(dòng)態(tài)擴(kuò)容,ArrayList能夠在添加元素時(shí)保持高效的性能,擴(kuò)容操作是有一定開(kāi)銷(xiāo)的,但由于擴(kuò)容的時(shí)間復(fù)雜度為O(n),其中n是當(dāng)前元素個(gè)數(shù),所以平均情況下,每次添加元素的時(shí)間復(fù)雜度仍然是O(1),需要的朋友可以參考下
    2024-01-01
  • SpringBoot實(shí)現(xiàn)返回值數(shù)據(jù)脫敏的步驟詳解

    SpringBoot實(shí)現(xiàn)返回值數(shù)據(jù)脫敏的步驟詳解

    這篇文章主要給大家介紹一下SpringBoot實(shí)現(xiàn)返回值數(shù)據(jù)脫敏的步驟,文章通過(guò)代碼示例介紹的非常詳細(xì),具有一定的參考價(jià)值,需要的朋友可以參考下
    2023-07-07
  • springboot項(xiàng)目打成war包部署到tomcat遇到的一些問(wèn)題

    springboot項(xiàng)目打成war包部署到tomcat遇到的一些問(wèn)題

    這篇文章主要介紹了springboot項(xiàng)目打成war包部署到tomcat遇到的一些問(wèn)題,需要的朋友可以參考下
    2017-06-06

最新評(píng)論