免费在线a视频-免费在线观看a视频-免费在线观看大片影视大全-免费在线观看的视频-色播丁香-色播基地

mybatis一級二級緩存原理一次擼到底

:2019年10月16日 小白學java
分享到:

我們都知道mybatis有一級,二級緩存之說。我們對他們的了解大部分都是這樣:1,一級緩存對應SqlSession,二級緩存對應mapper。2,一級緩存默認開啟,二級緩存需要手動開啟3,當查詢的時候,在二級緩存開...

我們都知道mybatis有一級,二級緩存之說。我們對他們的了解大部分都是這樣:

1,一級緩存對應SqlSession,二級緩存對應mapper。

2,一級緩存默認開啟,二級緩存需要手動開啟

3,當查詢的時候,在二級緩存開啟的情況下查二級緩存,先查二級緩存,如果二級緩存也沒有,然后查一級緩存,如果一級緩存再沒有,則查數據庫。查完數據庫之后把數據放到緩存中。

那么,今天我們通過源碼的方式來了解一下mybatis的緩存原理。

1,當我們走mapper1.selectOne(map);這行代碼的時候:

會通過代理的方式會走到下面第5代碼executor.query()。

public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {  try {    MappedStatement ms = configuration.getMappedStatement(statement);    //會先調用CachingExecutor子類的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();  }}

我們就以這個executor作為切入點進入觀察,跟進斷點可知:這個executor會先使用他的子類CachingExecutor調用query(),這個類一看名字就知道跟緩存相關的。

我們來看這個CachingExecutor的query()方法:

@Overridepublic <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {  //處理sql語句  BoundSql boundSql = ms.getBoundSql(parameterObject);  //創建緩存key  CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);  //通過這個key作為緩存進行調用query方法  return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);}

這里其中如何創建這個CacheKey等會再說,我們先看看query()

@Overridepublic <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)    throws SQLException {  //  ms.getCache()拿到緩存對象 開啟二級緩存菜回進入if里面  Cache cache = ms.getCache();  if (cache != null) {    flushCacheIfRequired(ms);    //如果標簽中設置useCache=false,那么不使用二級緩存    if (ms.isUseCache() && resultHandler == null) {      ensureNoOutParams(ms, boundSql);      @SuppressWarnings("unchecked")      List<E> list = (List<E>) tcm.getObject(cache, key);      if (list == null) {        list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);        tcm.putObject(cache, key, list); // issue #578 and #116      }      return list;    }  }  //如果不開啟直接走下面這條查詢,其實這條查詢跟13行是一樣的。  return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);}

這個MappedStatement.getCache()就是我們說的二級緩存。同時它會判斷我們的二級緩存是否開啟ms.isUseCache()。

那么它是如何初始化二級緩存并且如何設置isUseCache的呢?

這就回到我們解析配置文件說起(之前寫過一次mybatis的執行流程,不清楚可以去看看,這里只是略講):

1,在解析的時候會先后調用兩個方法,如圖:

在settingsElement方法中,默認對mybatis配置文件是否啟用二級緩存功能設置true。有人就奇怪了,二級緩存不是默認不開啟嗎,怎么默認為true?不要著急。。

然后是mapperElement方法中,看調用的mapperParser.parse()的方法

我們在點進去看configurationElement這個方法:

這個cacheElement()方法就是解析我們映射文件是否配置了<cache/>

如果不配置,那么什么也不做。也就是整個mapper中不開啟二級緩存!

這我們就說明了Cache cache = ms.getCache();的由來

那么還有一個問題就是這個判斷ms.isUseCache(),這個有什么用呢?也就是mapper雖然開啟了二級緩存,它還要看看,select標簽中是否禁用了二級緩存,默認如果不配置,那么為true,以下圖中代碼。

而如果配置為false了,將不會走

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

這個分支的代碼,也就是不使用二級緩存。

講到這里,大家應該明白為何二級緩存需要手動開啟了吧?默認情況下,mybatis配置文件開啟二級緩存,而mapper映射文件默認是關閉二級緩存的,簡單的說配置文件開啟了,可是我映射文件沒有開啟,那么還是不開啟,所以我們說的開啟二級緩存其實就是在映射文件中開啟。當我們在映射文件中開啟了二級緩存,那么映射文件中的select標簽也是默認開啟二級緩存的,我們可以通過這種機制來使某個select禁用二級緩存(在select標簽中加入useCache=false即可)。

2)繼續說CachingExecutor.query()方法。

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) {    flushCacheIfRequired(ms);    if (ms.isUseCache() && resultHandler == null) {      ensureNoOutParams(ms, boundSql);      @SuppressWarnings("unchecked")      List<E> list = (List<E>) tcm.getObject(cache, key);      if (list == null) {        list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);        tcm.putObject(cache, key, list); // issue #578 and #116      }      return list;    }  }  return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);}

如果開啟二級緩存,則直接當我們進入二級緩存的邏輯之中,tcm.getObject(cache, key)這個就是根據key從緩存中拿,如果拿不到,則調用這個delegate.query()方法拿。

if (ms.isUseCache() && resultHandler == null) {  ensureNoOutParams(ms, boundSql);  @SuppressWarnings("unchecked")  //從緩存中拿數據   List<E> list = (List<E>) tcm.getObject(cache, key);  //判斷是否拿到,如果拿不到則delegate.query(  if (list == null) {    list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);    tcm.putObject(cache, key, list); // issue #578 and #116  }  return list;}

以上就是mybatis二級緩存中獲取數據的邏輯

我們再來看看delegate.query()這個方法:

通過代碼我們發現,如果沒有開啟二級緩存,或者二級緩存中沒有數據

那么最后都是執行delegate.query()方法。

這個方法由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());  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) {      //處理本地緩存的輸出參數      handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);    } else {      //如果緩存拿不到則查詢數據庫      list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);    }  } finally {    queryStack--;  }  //....   return list;}

這個代碼就很好了,跟之前差不多,13行就是從緩存中拿,如果拿不到,那么就直接從18行中從數據庫中拿。這就是從一級緩存中拿數據的流程。

這就是mybatis查詢語句從緩存獲取數據的流程。

那么這個緩存對象到底是什么??

我們可以開啟二級緩存,然后通過CachingExecutor的query()打斷點進入看看Cache cache = ms.getCache();這個是什么。

這樣,我們就能找到LruCache這個Cache了。

這個LruCache有個成員變量:private final Cache delegate;我們可以通過之前看出他就是一個PerpetualCache類,

到這里就不難看出,不管是一級緩存還是二級緩存,他們的緩存設計思想都是Map。

既然我們懂了緩存其實就是個map,那么如何確定key呢??

這就是之前沒有說的CacheKey,也就是下面代碼的第5行

@Overridepublic <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {  BoundSql boundSql = ms.getBoundSql(parameterObject); // 確定key,如何確定?  CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);  return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);}

createCacheKey同樣是由BaseExecutor進行調用createCacheKey()方法。

我們最后再看看它的實現邏輯:

它就是通過一系列的update語句來確認CacheKey,具體如下:

public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {  if (closed) {    throw new ExecutorException("Executor was closed.");  }  CacheKey cacheKey = new CacheKey();  //確定cacheKey 的邏輯如下  cacheKey.update(ms.getId());//通過mapper中的select標簽id  cacheKey.update(rowBounds.getOffset());//跟分頁有關,不用理  cacheKey.update(rowBounds.getLimit());//跟分頁有關,不用理  cacheKey.update(boundSql.getSql());//通過sql語句  //省略代碼  return cacheKey;}

那么update方法中到底如何實現?

public void update(Object object) {  int baseHashCode = object == null ? 1 : ArrayUtil.hashCode(object);  count++;  checksum += baseHashCode;  baseHashCode *= count;  hashcode = multiplier * hashcode + baseHashCode;  updateList.add(object);}

看到HashCode就明白了update就是通過哈希算法來確認CacheKey的。

我們明白了select的過程以及使用緩存的原理,那么update呢?其實好多人都知道mybatis在執行update,insert,delete之后,都會清空緩存。

因為在執行update,insert,delete的過程中,最后都是BaseExecutor調用update方法,所以我們只要看這個代碼

這個就是清空緩存的代碼。

以上就是原理,最后我們通過select執行打印的sql的例子來看看效果:

1,關閉二級緩存:

效果:兩次一樣的查詢只發出一條sql

2,開啟二級緩存:

結果:同樣只發出一條sql

總結:

1,二級緩存需要手動開啟,其實就是只用在映射文件中加入<cache/>。因為mybatis配置文件已經默認開啟了二級緩存。

2,發出select語句之后,如果開啟二級緩存,先在二級緩存中查數據。如果查不到數據,則查一級緩存,如果查不到,則最后查數據庫。

3,insert,update,delete執行后,會清空緩存,不管是一級還是二級緩存

4,緩存對象Cache其實就是Map來實現的

5,緩存的key分別由select標簽id,分頁信息,sql通過一系列哈希算法來確定

最后附上Mybatis緩存執行流程:

本人水平有限,難免有錯誤或遺漏之處,望大家指正和諒解,提出寶貴意見,愿與之交流。

[我要糾錯]
文:王振袢&發表于江蘇
關鍵詞: 我們 知道 mybatis 一級 二級

來源:本文內容搜集或轉自各大網絡平臺,并已注明來源、出處,如果轉載侵犯您的版權或非授權發布,請聯系小編,我們會及時審核處理。
聲明:江蘇教育黃頁對文中觀點保持中立,對所包含內容的準確性、可靠性或者完整性不提供任何明示或暗示的保證,不對文章觀點負責,僅作分享之用,文章版權及插圖屬于原作者。

點個贊
0
踩一腳
0

您在閱讀:mybatis一級二級緩存原理一次擼到底

Copyright?2013-2024 JSedu114 All Rights Reserved. 江蘇教育信息綜合發布查詢平臺保留所有權利

蘇公網安備32010402000125 蘇ICP備14051488號-3技術支持:南京博盛藍睿網絡科技有限公司

南京思必達教育科技有限公司版權所有   百度統計

主站蜘蛛池模板: 久草高清在线 | 欧美一级视频精品观看 | 99视频在线永久免费观看 | 一级片+国产 | 午夜视频色| 老司机成人午夜精品福利视频 | 97午夜理伦影院在线观看 | 成人小视频在线观看免费 | 在线观看欧美亚洲 | 国产午夜小视频 | 亚洲精品第一 | 在线观看免费亚洲 | 欧美日韩高清不卡一区二区三区 | 国产一级一级一级国产片 | 人人澡人人射 | 伊人网综合在线观看 | 天天摸天天揉天天碰天天弄 | 国产无限免费观看黄网站 | 一个人看的www日本高清视频 | 国产一级淫 | 欧美国产成人免费观看永久视频 | 中文国产欧美在线观看 | 16欧美freesex呦交hd | 色综合色狠狠天天久久婷婷基地 | 亚洲欧美在线观看视频 | 成人91视频 | 99视频国产在线 | 欧美理伦 | 欧美日韩国产网站 | 一级一级特黄女人精品毛片视频 | 国产精品青草久久 | 成人美女黄网站色大色费 | 久久99综合国产精品亚洲首页 | 国产精品午夜久久 | 最近最好看2019年中文字幕 | 日本人亚洲人成人 | 91短视频在线高清hd | 最近最新2019中文字幕1 | 日韩色综合 | 亚洲香蕉一区二区三区在线观看 | 国产麻豆成人传媒免费观看 |
最熱文章
最新文章
  • 阿里云上云鉅惠,云產品享最低成本,有需要聯系,
  • 卡爾蔡司鏡片優惠店,鏡片價格低
  • 蘋果原裝手機殼