最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

一文詳解mybatis二級緩存執(zhí)行流程

 更新時間:2024年02月26日 09:30:16   作者:LixBox  
本文主要詳細介紹了mybatis二級緩存執(zhí)行流程,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考價值,感興趣的朋友們下面隨著小編來一起學(xué)習(xí)吧

mybatis二級緩存的執(zhí)行流程

1.二級緩存的生成

在mybatis啟動時會加載并解析配置文件,其中就會解析二級緩存的可選項配置,由此是否生成二級緩存,下面這段代碼就是解析xxxMapper.xml文件中的子標簽來生成二級緩存

//XMLmapperBuilder
private void configurationElement(XNode context) {
  try {
    // 獲取<mapper>標簽的namespace值,也就是命名空間
    String namespace = context.getStringAttribute("namespace");
    // 命名空間不能為空
    if (namespace == null || namespace.isEmpty()) {
      throw new BuilderException("Mapper's namespace cannot be empty");
    }
    // MapperBuilderAssistant:構(gòu)建MappedStatement對象的構(gòu)建助手,設(shè)置當(dāng)前的命名空間為namespace的值
    builderAssistant.setCurrentNamespace(namespace);
    ......
        
    // 解析<cache>子標簽,這里生成二級緩存
    cacheElement(context.evalNode("cache"));

    ......
  } catch (Exception e) {
    throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
  }
}

默認的二級緩存對象是PerpetuateCache,并把緩存封裝進MapperStatement,所以一個MapperStatement對應(yīng)一個二級緩存,一個MapperStatement就是存儲一個xxxMapper.xml文件中的信息

//XMLmapperBuilder
private void cacheElement(XNode context) {
  if (context != null) {
    // 解析<cache>標簽type屬性的值,在這可以自定義type的值,比如redisCache,如果沒有指定默認就是PERPETUAL
    String type = context.getStringAttribute("type", "PERPETUAL");
    Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
    // 獲取負責(zé)過期的eviction對象,默認策略為LRU
    String eviction = context.getStringAttribute("eviction", "LRU");
    Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);
    // 清空緩存的頻率 0代表不清空
    Long flushInterval = context.getLongAttribute("flushInterval");
    // 緩存容器的大小
    Integer size = context.getIntAttribute("size");
    // 是否只讀
    boolean readWrite = !context.getBooleanAttribute("readOnly", false);
    // 是否阻塞
    boolean blocking = context.getBooleanAttribute("blocking", false);
    // 獲得Properties屬性
    Properties props = context.getChildrenAsProperties();
    //builderAssistant是MapperBuilderAssistant,作用就是解耦建立MapperStatement,因為
    //MapperStatement對象創(chuàng)建復(fù)雜,所以用這個類來解耦創(chuàng)建
    builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);
  }
}

PerpetualCache內(nèi)部的緩存其實就是個HashMap

public class PerpetualCache implements Cache {

  private final String id;

  private final Map<Object, Object> cache = new HashMap<>();

2.二級緩存的使用

在執(zhí)行查詢時,會先走二級緩存,若二級緩存沒有才走下一步查詢,并把查詢結(jié)果存到二級緩存中,但此時只是存到tcm(TransactionalCacheManager)中的一個map中

//CachingExecutor
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
    throws SQLException {
  // 獲取二級緩存,是從MapperStatement里面獲取的
  Cache cache = ms.getCache();
  if (cache != null) {
    // 刷新tcm的緩存 (存在緩存且flushCache為true時)
    flushCacheIfRequired(ms);
    if (ms.isUseCache() && resultHandler == null) {
      ensureNoOutParams(ms, boundSql);
      @SuppressWarnings("unchecked")
      // 從二級緩存中查詢數(shù)據(jù)
      List<E> list = (List<E>) tcm.getObject(cache, key);
      // 如果二級緩存中沒有查詢到數(shù)據(jù),則查詢一級緩存及數(shù)據(jù)庫
      if (list == null) {
        // 委托給BaseExecutor執(zhí)行
        list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
        // 將查詢結(jié)果 要存到二級緩存中(注意:此處只是存到map集合中,沒有真正存到二級緩存中)
        tcm.putObject(cache, key, list); // issue #578 and #116
      }
      return list;
    }
  }
  // 如果沒有開啟二級緩存或開啟了沒查到二級緩存則委托給BaseExecutor執(zhí)行下一步查詢
  return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

TransactionalCacheManager顧名思義事務(wù)緩存管理器,只在一次事務(wù)中進行緩存管理,當(dāng)事務(wù)commit后tcm就不存在了。在一次事務(wù)commit前可以進行多次數(shù)據(jù)庫操作,例如進行2次查詢。

public class TransactionalCacheManager {

  // Cache 與 TransactionalCache 的映射關(guān)系表
  private final Map<Cache, TransactionalCache> transactionalCaches = new HashMap<>();
    
  public Object getObject(Cache cache, CacheKey key) {
    // 直接從TransactionalCache中獲取緩存
    return getTransactionalCache(cache).getObject(key);
  }

  public void putObject(Cache cache, CacheKey key, Object value) {
    // 直接存入TransactionalCache的緩存中
    getTransactionalCache(cache).putObject(key, value);
  }
    
  private TransactionalCache getTransactionalCache(Cache cache) {
    // 從映射表中獲取 TransactionalCache,下面代碼等同于
    // transactionalCaches.computeIfAbsent(cache, TransactionalCache::new);
    return MapUtil.computeIfAbsent(transactionalCaches, cache, TransactionalCache::new);
  }

上面提到查詢時先走二級緩存,二級緩存查詢從這開始,從這里查一下二級緩存是否存在,不存在再走其他查詢結(jié)果,以下的delegate就是MapperStatement的二級緩存對象

public class TransactionalCache implements Cache {
   /* 二級緩存 Cache 對象。
   */
  private final Cache delegate;
  /**
   * 提交時,清空 {@link #delegate}
   *
   * 初始時,該值為 false
   * 清理后{@link #clear()} 時,該值為 true ,表示持續(xù)處于清空狀態(tài)
   */
  private boolean clearOnCommit;
  /**
   *  // 在事務(wù)被提交前,所有從數(shù)據(jù)庫中查詢的結(jié)果將緩存在此集合中
   */
  private final Map<Object, Object> entriesToAddOnCommit;
  /**
   *   在事務(wù)被提交前,當(dāng)緩存未命中時,CacheKey 將會被存儲在此集合中
   */
  private final Set<Object> entriesMissedInCache;
  
  public Object getObject(Object key) {
    // issue #116
    // 查詢的時候是直接從delegate(就是MapperSatement的二級緩存對象)中去查詢的
    Object object = delegate.getObject(key);
    // 如果不存在,則添加到 entriesMissedInCache 中
    if (object == null) {
      // 緩存未命中,則將 key 存入到 entriesMissedInCache 中
      entriesMissedInCache.add(key);
    }
    // issue #146
    // 如果 clearOnCommit 為 true ,表示處于持續(xù)清空狀態(tài),則返回 null
    if (clearOnCommit) {
      return null;
    } else {
      // 返回 value
      return object;
    }
  }

不存在二級緩存,走其他查詢后,把查詢結(jié)果放進二級緩存中,但這里其實并沒放進二級緩存,而是放到了entriesToAddOnCommit中,畢竟一次事務(wù)之后tcm才不存在,所以在事務(wù)commit后,再放進真正二級緩存

//TransactionalCache
public void putObject(Object key, Object object) {
  // 將鍵值對存入到 entriesToAddOnCommit 這個Map中中,而非真實的緩存對象 delegate 中
  entriesToAddOnCommit.put(key, object);
}

當(dāng)一次事務(wù)commit后,會把查詢結(jié)果真正放到二級緩存中

public void commit() {
  // 如果 clearOnCommit 為 true ,則清空 delegate 緩存
  if (clearOnCommit) {
    delegate.clear();
  }
  // 將 entriesToAddOnCommit、entriesMissedInCache 刷入 delegate(cache) 中
  flushPendingEntries();
  // 重置
  reset();
}

private void flushPendingEntries() {
    // 將 entriesToAddOnCommit 中的內(nèi)容轉(zhuǎn)存到 delegate 中
    for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {
      // 在這里真正的將entriesToAddOnCommit的對象逐個添加到delegate中,只有這時,二級緩存才真正的生效
      delegate.putObject(entry.getKey(), entry.getValue());
    }
    // 將 entriesMissedInCache 刷入 delegate 中
    for (Object entry : entriesMissedInCache) {
      if (!entriesToAddOnCommit.containsKey(entry)) {
        delegate.putObject(entry, null);
      }
    }
  }

到此這篇關(guān)于一文詳解mybatis二級緩存執(zhí)行流程的文章就介紹到這了,更多相關(guān)mybatis二級緩存執(zhí)行內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • mybatis之批量添加問題

    mybatis之批量添加問題

    這篇文章主要介紹了mybatis之批量添加問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-11-11
  • JAVA 對接騰訊云直播的實現(xiàn)

    JAVA 對接騰訊云直播的實現(xiàn)

    這篇文章主要介紹了JAVA 對接騰訊云直播的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-06-06
  • Spark SerializedLambda錯誤的兩種解決方案

    Spark SerializedLambda錯誤的兩種解決方案

    這篇文章主要介紹了Spark SerializedLambda錯誤的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • Java中Set&List的迭代器實現(xiàn)步驟解析

    Java中Set&List的迭代器實現(xiàn)步驟解析

    這篇文章主要介紹了Java中Set&List的迭代器實現(xiàn)步驟解析,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-10-10
  • Spring Cloud搭建eureka過程圖解

    Spring Cloud搭建eureka過程圖解

    這篇文章主要介紹了Spring Cloud搭建eureka過程圖解,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-12-12
  • 深入探究Java?@MapperScan實現(xiàn)原理

    深入探究Java?@MapperScan實現(xiàn)原理

    之前是直接在Mapper類上面添加注解@Mapper,這種方式要求每一個mapper類都需要添加此注解,麻煩。通過使用@MapperScan可以指定要掃描的Mapper類的包的路徑,這篇文章深入探究Java?@MapperScan的實現(xiàn)原理
    2023-01-01
  • Java?18?新特性之Web服務(wù)器?jwebserver功能

    Java?18?新特性之Web服務(wù)器?jwebserver功能

    JEP?408:?Simple?Web?Server,是這次Java?18推出的一個比較獨立的全新功能點。我們可以通過命令行工具來啟動一個提供靜態(tài)資源訪問的迷你Web服務(wù)器,本文通過一個構(gòu)建HTML頁面的例子,來嘗試一下jwebserver的功能
    2022-04-04
  • 通過Java實現(xiàn)獲取表的自增主鍵值

    通過Java實現(xiàn)獲取表的自增主鍵值

    這篇文章主要為大家詳細介紹了如何通過Java實現(xiàn)獲取表的自增主鍵值,文中的示例代碼講解詳細,具有一定的學(xué)習(xí)價值,感興趣的可以了解一下
    2023-06-06
  • Mybatis?saveAndUpdate空值不更新問題及解決

    Mybatis?saveAndUpdate空值不更新問題及解決

    這篇文章主要介紹了Mybatis?saveAndUpdate空值不更新問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • Java Socket實現(xiàn)聊天室附1500行源代碼

    Java Socket實現(xiàn)聊天室附1500行源代碼

    Socket是應(yīng)用層與TCP/IP協(xié)議族通信的中間軟件抽象層,它是一組接口。本篇文章手把手帶你通過Java Socket來實現(xiàn)自己的聊天室,大家可以在過程中查缺補漏,溫故而知新
    2021-10-10

最新評論

高要市| 阿坝县| 东乡县| 泰州市| 贡觉县| 定西市| 区。| 奉节县| 东乌| 汨罗市| 柘城县| 渭南市| 项城市| 垫江县| 横山县| 昌江| 华亭县| 漯河市| 丹棱县| 安平县| 尼玛县| 和田县| 墨江| 龙泉市| 天等县| 枞阳县| 那曲县| 札达县| 乐东| 比如县| 临洮县| 泾阳县| 禹州市| 汶川县| 三门峡市| 佛坪县| 石河子市| 塔河县| 瑞丽市| 马龙县| 射洪县|