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

Mybatis CachingExecutor二級(jí)緩存使用示例詳解

 更新時(shí)間:2023年09月26日 08:28:56   作者:戰(zhàn)斧  
這篇文章主要介紹了?Mybatis的CachingExecutor與二級(jí)緩存使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

前言

上次我們講Mybatis的緩存時(shí),我們提到了CachingExecutor,知道了這個(gè)帶緩存的執(zhí)行器就是二級(jí)緩存的來(lái)源,這次我們系統(tǒng)的分析下其是如何產(chǎn)生作用的

一、CachingExecutor的在邏輯定位

流程圖中的位置

我把CachingExecutor在邏輯鏈路中的位置標(biāo)出來(lái)了,就是儲(chǔ)存在會(huì)話對(duì)象中,通過會(huì)話可使用到CachingExecutor,而CachingExecutor又內(nèi)置一個(gè)SimpleExecutor,熟悉設(shè)計(jì)模式的同學(xué)應(yīng)該知道這就是所謂的委派模式。當(dāng)然,這里面會(huì)話內(nèi)置的也可能直接就是SimpleExecutor了,那樣的話,調(diào)用的就直接是SimpleExecutor執(zhí)行器了。

二、CachingExecutor的生效

既然知道了CachingExecutor在會(huì)話對(duì)象中,那毫無(wú)疑問,就是在創(chuàng)建會(huì)話的時(shí)候,把一個(gè)CachingExecutor放入到會(huì)話對(duì)象中的,我們來(lái)看看,要實(shí)現(xiàn)這個(gè)目標(biāo)要做什么

1.全局參數(shù)

首先,要想啟用CachingExecutor,我們得開啟一個(gè)全局的設(shè)置參數(shù)

mybatis.configuration.cache-enabled=true

為什么開了這個(gè)參數(shù)就有用呢?其實(shí)不難猜想,其作用的位置肯定還是在創(chuàng)建會(huì)話對(duì)象的時(shí)候,我們直接看源碼吧

SqlSessionUtils.class

// SqlSessionUtils.class
// 通過工廠對(duì)象(單例,存在容器中),開啟會(huì)話,獲得會(huì)話對(duì)象,executorType是執(zhí)行器枚舉類(SIMPLE, REUSE, BATCH),一般是SIMPLE
session = sessionFactory.openSession(executorType);

DefaultSqlSessionFactory.class

// DefaultSqlSessionFactory.class
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
  Transaction tx = null;
  try {
    final Environment environment = configuration.getEnvironment();
    final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
    tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
    // 通過configuration(Mybatis配置,單例)創(chuàng)建執(zhí)行器
    final Executor executor = configuration.newExecutor(tx, execType);
    return new DefaultSqlSession(configuration, executor, autoCommit);
  } catch (Exception e) {
    closeTransaction(tx); // may have fetched a connection so lets call close()
    throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
  } finally {
    ErrorContext.instance().reset();
  }
}

Configuration.class

// Configuration.class
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 {
    // 創(chuàng)建了一個(gè)簡(jiǎn)易執(zhí)行器
    executor = new SimpleExecutor(this, transaction);
  }
  // 如果開啟了緩存,即 mybatis.configuration.cache-enabled=true
  if (cacheEnabled) {
    // 創(chuàng)建了緩存執(zhí)行器,并且把簡(jiǎn)易執(zhí)行器作為其 delegate,通過構(gòu)造方法放入
    executor = new CachingExecutor(executor);
  }
  executor = (Executor) interceptorChain.pluginAll(executor);
  return executor;
}

如上,可以看出CachingExecutor就是由 mybatis.configuration.cache-enabled=true 開啟的,并且和會(huì)話的情況無(wú)關(guān),這是一個(gè)全局設(shè)置,一旦開啟,所有會(huì)話都會(huì)首先調(diào)用CachingExecutor

2. MappedStatement啟用Cache

是不是我們啟用了CachingExecutor就可以使用二級(jí)緩存了呢?為什么這么說,我們還是直接看CachingExecutor的源碼
CachingExecutor.class

// CachingExecutor.class
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
    throws SQLException {
  // mappedStatement 就是我們寫的mapper接口里的某個(gè)方法的所有信息,注意其描述的是一個(gè)方法,而不是一整個(gè)mapper接口的所有方法
  // 當(dāng)然,它同樣也包含某些mapper層次的設(shè)置,如對(duì)應(yīng)的xml文件位置等,此處的Cache同樣是mapper層次的設(shè)置
  Cache cache = ms.getCache();
  // 有Cache才會(huì)真正的去找二級(jí)緩存,否則直接就讓委托的執(zhí)行器去查詢數(shù)據(jù)了。
  // 注意此處的Cache并不是緩存本身,而是mapper里的緩存配置
  if (cache != null) {
    // ....
      List<E> list = (List<E>) tcm.getObject(cache, key);
    // ....
      return list;
    }
  }
  return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

可以看到,要想真正啟用緩存,還得在Mapper層級(jí)進(jìn)行一次緩存配置,也就是所謂的

聲明下Cache時(shí)可以設(shè)置一下參數(shù)
<cache eviction="FIFO" flushinterval="60000" size="512" readOnly="true"/>
當(dāng)然也可以不進(jìn)行任何參數(shù)配置,就單獨(dú)聲明下Cache,如下:
<cache/>

我們看一下此時(shí)MappedStatement里Cache的構(gòu)成,可以說是套娃巔峰,把委派模式玩到了極致

我們通過源碼看其實(shí)現(xiàn)

MapperBuilderAssistant.class

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

三、二級(jí)緩存的存取

上面我們已經(jīng)看了如何使二級(jí)緩存生效,但真正的查詢和存入還沒有細(xì)看,現(xiàn)在來(lái)看看其存儲(chǔ)的位置,及如何讀取,我們直接看下源碼

1. 緩存源碼分析

// CachingExecutor.class query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
if (cache != null) {
  // 是否清緩存,即如果Cache配置里含有flushCache=“true” 則進(jìn)行 tcm.clear();
  flushCacheIfRequired(ms);
  if (ms.isUseCache() && resultHandler == null) {
    ensureNoOutParams(ms, boundSql);
    @SuppressWarnings("unchecked")
    // tcm 是一個(gè)成員變量,是通過new TransactionalCacheManager()賦值的,此處為查詢
    List<E> list = (List<E>) tcm.getObject(cache, key);
    if (list == null) {
      list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
      // 通過 tcm 進(jìn)行結(jié)果的存儲(chǔ)
      tcm.putObject(cache, key, list); // issue #578 and #116
    }
    return list;
  }
}

在進(jìn)行下一步前,我們有必要看一下獲取緩存,為什么要傳兩個(gè)入?yún)?,即cache和key?

  • cache:mapper級(jí)別的緩存配置及二級(jí)緩存
  • key:方法的全限定名及完整的sql即sql入?yún)?/li>

接下來(lái),我們不難發(fā)現(xiàn)已經(jīng)出現(xiàn)了 存儲(chǔ)、獲取、清除這三種方法,且都圍繞著tcm(TransactionalCacheManager),因此接下來(lái),我們還得關(guān)注一下TransactionalCacheManager。

public class TransactionalCacheManager {
	private final Map<Cache, TransactionalCache> transactionalCaches = new HashMap<>();
	public Object getObject(Cache cache, CacheKey key) {
	    // 先把緩存設(shè)置作為key,查詢出一個(gè)TransactionalCache
    	return getTransactionalCache(cache).getObject(key);
  	}
  	private TransactionalCache getTransactionalCache(Cache cache) {
  	    // 作為第一次查詢,不難發(fā)現(xiàn),所謂的value居然也是依托緩存設(shè)置來(lái)構(gòu)造的
    	return transactionalCaches.computeIfAbsent(cache, TransactionalCache::new);
    }
  	// ....
}
public class TransactionalCache implements Cache {
    private final Cache delegate;
    private boolean clearOnCommit;
    // 臨時(shí)待加緩存,查詢數(shù)據(jù)庫(kù)返回的結(jié)果,首先會(huì)放在這里,等本次事務(wù)提交后,才會(huì)加入到真正的二級(jí)緩存中,即調(diào)用套娃對(duì)象,層層深入,最終存入HashMap
    // 在事務(wù)提交前,其他會(huì)話甚至本會(huì)話自己都無(wú)法看見該緩存,更無(wú)法使用該緩存
    private final Map<Object, Object> entriesToAddOnCommit;
    // 查二級(jí)緩存沒查到時(shí),會(huì)把key值存在這個(gè)miss的集合中
    private final Set<Object> entriesMissedInCache;
    public TransactionalCache(Cache delegate) {
    	this.delegate = delegate;
    	this.clearOnCommit = false;
    	this.entriesToAddOnCommit = new HashMap<>();
    	this.entriesMissedInCache = new HashSet<>();
    }
	public Object getObject(Object key) {
		// issue #116
		// 觀察后,可以知道,這里的delegate其實(shí)就是我們說的mapper緩存設(shè)置
		// 由此可見,緩存設(shè)置中也能包含緩存的值,并且以完整sql為鍵,sql結(jié)果為值以map存儲(chǔ)
		Object object = delegate.getObject(key);
		if (object == null) {
			entriesMissedInCache.add(key);
		}
		// issue #146
		if (clearOnCommit) {
			return null;
		} else {
			return object;
		}
	}
}

由于Cache的套娃十分嚴(yán)重,實(shí)際形成了鏈狀引用,而且受配置的影響很大,所以沒有辦法把每一種配置緩存間的相互調(diào)用闡述詳盡。但是我們?nèi)匀豢梢灾v出其核心思想。忽略掉中間的套娃,最終實(shí)現(xiàn)存儲(chǔ)的緩存類為PerpetualCache.class,其包含了一個(gè)HashMap,鍵就是我們上面提及的key(混合了方法信息,完整sql等),值為sql返回值的字節(jié)數(shù)組

2. 二級(jí)緩存可見性

二級(jí)緩存并不是即時(shí)生效的,我們可以關(guān)注下TransactionalCache 類,這個(gè)類看名字也知道是事務(wù)有關(guān),它有一個(gè)成員變量

private final Map<Object, Object> entriesToAddOnCommit;

這個(gè)變量我們上面源碼分析里其實(shí)說了,就算查數(shù)據(jù)庫(kù),返回了結(jié)果,也不是立即就到我們說的終極位置——PerpetualCache的HashMap里。而是在這個(gè)變量里暫存,等待事務(wù)提交了,再把這里的數(shù)據(jù)存入真正的二級(jí)緩存處

而在此之前,即使是本會(huì)話,也沒法從二級(jí)緩存中撈到東西,也就是說在一個(gè)事務(wù)里,你連續(xù)執(zhí)行兩次同樣的sql,盡管二級(jí)緩存已經(jīng)暫存了數(shù)據(jù),但第二次sql經(jīng)過CachingExecutor時(shí),它并不會(huì)把這個(gè)數(shù)據(jù)給你,那么自然,其他的會(huì)話也是無(wú)法看見這個(gè)數(shù)據(jù)的。

因此,我們說二級(jí)緩存的數(shù)據(jù),只在本事務(wù)提交后才正式可見,在此之前,其他會(huì)話甚至本會(huì)話自己,都無(wú)法使用該二級(jí)緩存

3. 一二級(jí)緩存優(yōu)先級(jí)

看上圖,不難明白,如果開啟了二級(jí)緩存,則先查的是二級(jí)緩存(mapper級(jí)別),這和我們一般的認(rèn)知相悖,因?yàn)榇蠖鄶?shù)緩存層級(jí),都是優(yōu)先查一級(jí)緩存,未命中再去查的二級(jí)緩存。除了上面因?yàn)榭梢娦詥栴}導(dǎo)致的,先查一級(jí)緩存,Mybatis里在一二級(jí)都開啟的情況下,優(yōu)先使用的是二級(jí)緩存的數(shù)據(jù),因此這里需要特別注意。

以上就是 Mybatis的CachingExecutor與二級(jí)緩存的詳細(xì)內(nèi)容,更多關(guān)于 Mybatis的CachingExecutor與二級(jí)緩存的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 淺聊java8中數(shù)值流的使用

    淺聊java8中數(shù)值流的使用

    java8為我提供的簡(jiǎn)單快捷的數(shù)值流計(jì)算API,本文就基于幾個(gè)常見的場(chǎng)景介紹一下數(shù)值流API的使用,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解下
    2023-10-10
  • Mybatis-Spring連接mysql 8.0配置步驟出錯(cuò)的解決方法

    Mybatis-Spring連接mysql 8.0配置步驟出錯(cuò)的解決方法

    這篇文章主要為大家詳細(xì)介紹了Mybatis-Spring連接mysql 8.0配置步驟出錯(cuò)的解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-06-06
  • ArrayList底層操作機(jī)制源碼解析

    ArrayList底層操作機(jī)制源碼解析

    這篇文章主要介紹了ArrayList底層操作機(jī)制源碼解析,當(dāng)創(chuàng)建ArrayList對(duì)象時(shí),如果使用的是無(wú)參構(gòu)造器,則初始elementData容量為0,第1次添加,則擴(kuò)容elementData為10,如需要再次擴(kuò)容,則擴(kuò)容elementData為1.5倍,需要的朋友可以參考下
    2023-09-09
  • 詳解SpringMVC使用MultipartFile實(shí)現(xiàn)文件的上傳

    詳解SpringMVC使用MultipartFile實(shí)現(xiàn)文件的上傳

    本篇文章主要介紹了SpringMVC使用MultipartFile實(shí)現(xiàn)文件的上傳,本地的文件上傳到資源服務(wù)器上,比較好的辦法就是通過ftp上傳。這里是結(jié)合SpringMVC+ftp的形式上傳的,有興趣的可以了解一下。
    2016-12-12
  • Java AbstractMethodError原因案例詳解

    Java AbstractMethodError原因案例詳解

    這篇文章主要介紹了Java AbstractMethodError原因案例詳解,本篇文章通過簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • Mybatis配置之<environments>配置元素詳解

    Mybatis配置之<environments>配置元素詳解

    這篇文章主要介紹了Mybatis配置之<environments>配置元素,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • Idea安裝Eslint插件提示:Plugin NativeScript was not installed的問題

    Idea安裝Eslint插件提示:Plugin NativeScript was not installed的問題

    這篇文章主要介紹了Idea安裝Eslint插件提示:Plugin NativeScript was not installed的問題,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-10-10
  • Java項(xiàng)目防止SQL注入的幾種方法總結(jié)

    Java項(xiàng)目防止SQL注入的幾種方法總結(jié)

    SQL注入是比較常見的網(wǎng)絡(luò)攻擊方式之一,在客戶端在向服務(wù)器發(fā)送請(qǐng)求的時(shí)候,sql命令通過表單提交或者url字符串拼接傳遞到后臺(tái)持久層,最終達(dá)到欺騙服務(wù)器執(zhí)行惡意的SQL命令,下面這篇文章主要給大家總結(jié)介紹了關(guān)于Java項(xiàng)目防止SQL注入的幾種方法,需要的朋友可以參考下
    2023-04-04
  • IDEA的常見的設(shè)置和優(yōu)化功能圖文詳解

    IDEA的常見的設(shè)置和優(yōu)化功能圖文詳解

    這篇文章主要介紹了IDEA的常見的設(shè)置和優(yōu)化功能,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-07-07
  • 基于Java 談回調(diào)函數(shù)

    基于Java 談回調(diào)函數(shù)

    回調(diào)函數(shù)就是一個(gè)通過函數(shù)指針調(diào)用的函數(shù)。如果你把函數(shù)的指針(地址)作為參數(shù)傳遞給另一個(gè)函數(shù),當(dāng)這個(gè)指針被用來(lái)調(diào)用其所 指向的函數(shù)時(shí),我們就說這是回調(diào)函數(shù)
    2017-05-05

最新評(píng)論