91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

總結MyBatis緩存結構

發布時間:2021-10-21 13:59:08 來源:億速云 閱讀:158 作者:iii 欄目:編程語言

這篇文章主要介紹“總結MyBatis緩存結構”,在日常操作中,相信很多人在總結MyBatis緩存結構問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”總結MyBatis緩存結構”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

二級緩存

主要內容:

總結MyBatis緩存結構

二級緩存構建在一級緩存之上,在收到查詢請求時,MyBatis 首先會查詢二級緩存。若二級緩存未命中,再去查詢一級緩存。與一級緩存不同,二級緩存和具體的命名空間綁定,一級緩存則是和 SqlSession 綁定。

在按照 MyBatis 規范使用 SqlSession 的情況下,一級緩存不存在并發問題。二級緩存則不然,二級緩存可在多個命名空間間共享。這種情況下,會存在并發問題,因此需要針對性去處理。除了并發問題,二級緩存還存在事務問題。

二級緩存如何開啟?

配置項

<configuration>
  <settings>
    <setting name="cacheEnabled">

cacheEnabled=true表示二級緩存可用,但是要開啟話,需要在Mapper.xml內配置。

<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
或者 簡單方式
<cache/>

對配置項屬性說明:

  • flushInterval="60000",間隔60秒清空緩存,這個間隔60秒,是被動觸發的,而不是定時器輪詢的。

  • size=512,表示隊列最大512個長度,大于則移除隊列最前面的元素,這里的長度指的是CacheKey的個數,默認為1024。

  • readOnly="true",表示任何獲取對象的操作,都將返回同一實例對象。如果readOnly="false",則每次返回該對象的拷貝對象,簡單說就是序列化復制一份返回。

  • eviction:緩存會使用默認的Least Recently Used(LRU,最近最少使用的)算法來收回。FIFO:First In First Out先進先出隊列。

在Configuration類的newExecutor方法中是否開啟二級緩存

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    if (ExecutorType.BATCH == executorType) {
      executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
      executor = new ReuseExecutor(this, transaction);
    } else {
      executor = new SimpleExecutor(this, transaction);
    }
      //是否開啟二級緩存
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }

二級緩存通過CachingExecutor來實現的,原理是緩存里存在,就返回,不存在就調用Executor ,如果一級緩存未關閉,則先查一級緩存,不存在,再到數據庫中查詢。

下面使用一張圖來表示:

總結MyBatis緩存結構

下面是源碼:

@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    // 獲得 BoundSql 對象
    BoundSql boundSql = ms.getBoundSql(parameterObject);
    // 創建 CacheKey 對象
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
    // 查詢
    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
            throws SQLException {
    // 調用 MappedStatement#getCache() 方法,獲得 Cache 對象,
    //即當前 MappedStatement 對象的二級緩存。
    Cache cache = ms.getCache();
    if (cache != null) { // <2> 
        // 如果需要清空緩存,則進行清空
        flushCacheIfRequired(ms);
        //當 MappedStatement#isUseCache() 方法,返回 true 時,才使用二級緩存。默認開啟。   
        //可通過@Options(useCache = false) 或 <select useCache="false"> 方法,關閉。
        if (ms.isUseCache() && resultHandler == null) { // <2.2>
            // 暫時忽略,存儲過程相關
            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);
}

二級緩存key是如何生成的?

也是使用的是BaseExecutor類中的createCacheKey方法生成的,所以二級緩存key和一級緩存生成規則是一樣的。

二級緩存范圍

二級緩存有一個非常重要的空間劃分策略:

namespace="com.tian.mybatis.mappers.UserMapper"

namespace="com.tian.mybatis.mappers.RoleMapper"

即,按照namespace劃分,同一個namespace,同一個Cache空間,不同的namespace,不同的Cache空間。

比如:

總結MyBatis緩存結構

在這個namespace下的二級緩存是同一個。

二級緩存什么時候會被清空?

每當執行insert、update、delete,flushCache=true時,二級緩存都會被清空。

事務不提交,二級緩存不生效?

SqlSession sqlSession = sqlSessionFactory.openSession();
System.out.println("第一次查詢"); 
User user = sqlSession.selectOne("com.tian.mybatis.mapper.UserMapper.selectById", 1);
System.out.println(user);

//sqlSession.commit();

SqlSession  sqlSession1 = sqlSessionFactory.openSession();
System.out.println("第二次查詢");
User  user2 = sqlSession1.selectOne("com.tian.mybatis.mapper.UserMapper.selectById", 1);
System.out.println(user2);

因為二級緩存使用的是TransactionalCaheManager(tcm)來管理的,最后又調用了TranscatinalCache的getObject()、putObject()、commit方法。

TransactionalCache里面又持有真正的Cache對象,比如:經過層層裝飾的PrepetualCache。

在putObject的時候,只是添加到entriesToAddOnCommit里面。

//TransactionalCache類中 
@Override
public void putObject(Object key, Object object) {
    // 暫存 KV 到 entriesToAddOnCommit 中
    entriesToAddOnCommit.put(key, object);
}

只有conmit方法被調用的時候,才會調用flushPendingEntries方法,真正寫入到緩存里。DefaultSqlSession調用commit方法的時候就會調到這個commit方法。

//TransactionalCache類中   
public void commit() {
    //如果 clearOnCommit 為 true ,則清空 delegate 緩存
    if (clearOnCommit) {
      delegate.clear();
    }
    // 將 entriesToAddOnCommit、entriesMissedInCache 刷入 delegate 中
    flushPendingEntries();
    // 重置
    reset();
  }
private void flushPendingEntries() {
    // 將 entriesToAddOnCommit 刷入 delegate 中
    for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {
      delegate.putObject(entry.getKey(), entry.getValue());
    }
    // 將 entriesMissedInCache 刷入 delegate 中
    for (Object entry : entriesMissedInCache) {
      if (!entriesToAddOnCommit.containsKey(entry)) {
        delegate.putObject(entry, null);
      }
    }
}
private void reset() {
    // 重置 clearOnCommit 為 false
    clearOnCommit = false;
    // 清空 entriesToAddOnCommit、entriesMissedInCache
    entriesToAddOnCommit.clear();
    entriesMissedInCache.clear();
}

為什么增刪該操作會清空二級緩存呢?

因為在CachingExecutor的update方法中

@Override
public int update(MappedStatement ms, Object parameterObject) throws SQLException {
  flushCacheIfRequired(ms);
  return delegate.update(ms, parameterObject);
}
private void flushCacheIfRequired(MappedStatement ms) {
    Cache cache = ms.getCache();
    // 是否需要清空緩存
    //通過 @Options(flushCache = Options.FlushCachePolicy.TRUE) 或 <select flushCache="true"> 方式,
    //開啟需要清空緩存。
    if (cache != null && ms.isFlushCacheRequired()) {
        //調用 TransactionalCache#clear() 方法,清空緩存。
        //注意,此時清空的僅僅,當前事務中查詢數據產生的緩存。
        //而真正的清空,在事務的提交時。這是為什么呢?
        //還是因為二級緩存是跨 Session 共享緩存,在事務尚未結束時,不能對二級緩存做任何修改。
        tcm.clear(cache);
    }
}

如何實現多個namespace的緩存共享?

關于多個namespace的緩存共享的問題,可以使用來解決。

比如:

<cache-ref namespace="com.tian.mybatis.mapper.RoleMapper"

cache-ref代表引用別名的命名空間的Cache配置,兩個命名空間的操作使用的是同一個Cache。在關聯的表比較少或者按照業務可以對表進行分組的時候可以使用。

「注意」:在這種情況下,多個mapper的操作都會引起緩存刷新,所以這里的緩存的意義已經不是很大了。

如果將第三方緩存作為二級緩存?

Mybatis除了自帶的二級換以外,我們還可以通過是想Cache接口來自定義二級緩存。

添加依賴

     <dependency>
         <groupId>org.mybatis.caches</groupId>
         <artifactId>mybatis-redis</artifactId>
         <version>1.0.0-beta2</version>
    </dependency>

redis基礎配置項

    host=127.0.0.1
    port=6379
    connectionTimeOut=5000
    soTimeout=5000
    datebase=0

在我們的UserMapper.xml中添加

<cache type="org.mybatis.caches.redis.RedisCache"
       eviction="FIFO">

RedisCache類圖,Cache就是Mybatis中緩存的頂層接口。

總結MyBatis緩存結構

二級緩存應用場景

對于訪問多的查詢請求且用戶對查詢結果實時性要求不高,此時可采用mybatis二級緩存技術降低數據庫訪問量,提高訪問速度,業務場景比如:耗時較高的統計分析sql、電話賬單查詢sql等。

緩存查詢順序

先查二級緩存,不存在則堅持一級緩存是否關閉,沒關閉,則再查一級緩存,還不存在,最后查詢數據庫。

總結MyBatis緩存結構

二級緩存總結

二級緩存開啟方式有兩步:

第一步:在全局配置中添加配置

<settings>
    <setting name="cacheEnabled">

第二步,在Mapper中添加配置

<cache type="org.mybatis.caches.redis.RedisCache"
           eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>

二級換是默認開啟的,但是針對每一個Mapper的二級緩存是需要手動開啟的。

二級緩存的key和一級緩存的key是一樣的。

每當執行insert、update、delete,flushCache=true時,二級緩存都會被清空。

我們可以繼承第三方緩存來作為Mybatis的二級緩存。

到此,關于“總結MyBatis緩存結構”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

周口市| 张家港市| 呼图壁县| 金昌市| 筠连县| 天柱县| 遂平县| 精河县| 宁强县| 于都县| 兴和县| 井研县| 宁安市| 壤塘县| 武山县| 乌海市| 田东县| 潞西市| 云梦县| 象州县| 铁力市| 常宁市| 福州市| 陇川县| 雷州市| 岳普湖县| 青神县| 突泉县| 团风县| 萨迦县| 壶关县| 屯留县| 漾濞| 榆林市| 南昌市| 太仓市| 探索| 金沙县| 金乡县| 吕梁市| 衡南县|