真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

使用MyBatis如何實現(xiàn)一級緩存與二級緩存

這期內(nèi)容當中小編將會給大家?guī)碛嘘P使用MyBatis如何實現(xiàn)一級緩存與二級緩存,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

臨潁ssl適用于網(wǎng)站、小程序/APP、API接口等需要進行數(shù)據(jù)傳輸應用場景,ssl證書未來市場廣闊!成為成都創(chuàng)新互聯(lián)的ssl證書銷售渠道,可以享受市場價格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:028-86922220(備注:SSL證書合作)期待與您的合作!

MyBatis緩存

我們知道,頻繁的數(shù)據(jù)庫操作是非常耗費性能的(主要是因為對于DB而言,數(shù)據(jù)是持久化在磁盤中的,因此查詢操作需要通過IO,IO操作速度相比內(nèi)存操作速度慢了好幾個量級),尤其是對于一些相同的查詢語句,完全可以把查詢結(jié)果存儲起來,下次查詢同樣的內(nèi)容的時候直接從內(nèi)存中獲取數(shù)據(jù)即可,這樣在某些場景下可以大大提升查詢效率。

MyBatis的緩存分為兩種:

一級緩存,一級緩存是SqlSession級別的緩存,對于相同的查詢,會從緩存中返回結(jié)果而不是查詢數(shù)據(jù)庫

二級緩存,二級緩存是Mapper級別的緩存,定義在Mapper文件的標簽中并需要開啟此緩存,多個Mapper文件可以共用一個緩存,依賴標簽配置

下面來詳細看一下MyBatis的一二級緩存。

MyBatis一級緩存工作流程

接著看一下MyBatis一級緩存工作流程。前面說了,MyBatis的一級緩存是SqlSession級別的緩存,當openSession()的方法運行完畢或者主動調(diào)用了SqlSession的close方法,SqlSession就被回收了,一級緩存與此同時也一起被回收掉了。前面的文章有說過,在MyBatis中,無論selectOne還是selectList方法,最終都被轉(zhuǎn)換為了selectList方法來執(zhí)行,那么看一下SqlSession的selectList方法的實現(xiàn):

public  List selectList(String statement, Object parameter, RowBounds rowBounds) {
  try {
   MappedStatement ms = configuration.getMappedStatement(statement);
   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();
  }
}

繼續(xù)跟蹤第4行的代碼,到BaseExeccutor的query方法:

 public  List 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);
 }

 第3行構(gòu)建緩存條件CacheKey,這里涉及到怎么樣條件算是和上一次查詢是同一個條件的一個問題,因為同一個條件就可以返回上一次的結(jié)果回去,這部分代碼留在下一部分分析。

接著看第4行的query方法的實現(xiàn),代碼位于CachingExecutor中:

public  List 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, parameterObject, boundSql);
    @SuppressWarnings("unchecked")
    List list = (List) 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);
}

第3行~第16行的代碼先不管,繼續(xù)跟第17行的query方法,代碼位于BaseExecutor中:

public  List 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 list;
  try {
   queryStack++;
   list = resultHandler == null ? (List) localCache.getObject(key) : null;
   if (list != null) {
    handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
   } else {
    list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
   }
  } finally {
   queryStack--;
  }
  ...
}

看12行,query的時候會嘗試從localCache中去獲取查詢結(jié)果,如果獲取到的查詢結(jié)果為null,那么執(zhí)行16行的代碼從DB中撈數(shù)據(jù),撈完之后會把CacheKey作為key,把查詢結(jié)果作為value放到localCache中。

MyBatis一級緩存存儲流程看完了,接著我們從這段代碼中可以得到三個結(jié)論:

MyBatis的一級緩存是SqlSession級別的,但是它并不定義在SqlSessio接口的實現(xiàn)類DefaultSqlSession中,而是定義在DefaultSqlSession的成員變量Executor中,Executor是在openSession的時候被實例化出來的,它的默認實現(xiàn)為SimpleExecutor

MyBatis中的一級緩存,與有沒有配置無關,只要SqlSession存在,MyBastis一級緩存就存在,localCache的類型是PerpetualCache,它其實很簡單,一個id屬性+一個HashMap屬性而已,id是一個名為"localCache"的字符串,HashMap用于存儲數(shù)據(jù),Key為CacheKey,Value為查詢結(jié)果

MyBatis的一級緩存查詢的時候默認都是會先嘗試從一級緩存中獲取數(shù)據(jù)的,但是我們看第6行的代碼做了一個判斷,ms.isFlushCacheRequired(),即想每次查詢都走DB也行,將標簽所在的Mapper的Namespace+標簽中定義的sql語句

即只要兩次查詢滿足以上三個條件且沒有定義flushCache="true",那么第二次查詢會直接從MyBatis一級緩存PerpetualCache中返回數(shù)據(jù),而不會走DB。

MyBatis二級緩存

上面說完了MyBatis,接著看一下MyBatis二級緩存,還是從二級緩存工作流程開始。還是從DefaultSqlSession的selectList方法進去:

public  List selectList(String statement, Object parameter, RowBounds rowBounds) {
  try {
   MappedStatement ms = configuration.getMappedStatement(statement);
   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();
  }
}

執(zhí)行query方法,方法位于CachingExecutor中:

 public  List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
   BoundSql boundSql = ms.getBoundSql(parameterObject);
   CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
   return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
 }

繼續(xù)跟第4行的query方法,同樣位于CachingExecutor中:

public  List 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, parameterObject, boundSql);
    @SuppressWarnings("unchecked")
    List list = (List) 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);
}

從這里看到,執(zhí)行第17行的BaseExecutor的query方法之前,會先拿Mybatis二級緩存,而BaseExecutor的query方法會優(yōu)先讀取MyBatis一級緩存,由此可以得出一個重要結(jié)論:假如定義了MyBatis二級緩存,那么MyBatis二級緩存讀取優(yōu)先級高于MyBatis一級緩存。

而第3行~第16行的邏輯:

第5行的方法很好理解,根據(jù)flushCache=true或者flushCache=false判斷是否要清理二級緩存

第7行的方法是保證MyBatis二級緩存不會存儲存儲過程的結(jié)果

第9行的方法先嘗試從tcm中獲取查詢結(jié)果,這個tcm解釋一下,這又是一個裝飾器模式(數(shù)數(shù)MyBatis用到了多少裝飾器模式了),創(chuàng)建一個事物緩存TranactionalCache,持有Cache接口,Cache接口的實現(xiàn)類就是根據(jù)我們在Mapper文件中配置的創(chuàng)建的Cache實例

第10行~第12行,如果沒有從MyBatis二級緩存中拿到數(shù)據(jù),那么就會查一次數(shù)據(jù)庫,然后放到MyBatis二級緩存中去
至于如何判定上次查詢和這次查詢是一次查詢?由于這里的CacheKey和MyBatis一級緩存使用的是同一個CacheKey,因此它的判定條件和前文寫過的MyBatis一級緩存三個維度的判定條件是一致的。

最后再來談一點,"Cache cache = ms.getCache()"這句代碼十分重要,這意味著Cache是從MappedStatement中獲取到的,而MappedStatement又和每一個、、

<strike id="eyygk"></strike>