當前位置:首頁 » 硬碟大全 » mybatis緩存版
擴展閱讀
webinf下怎麼引入js 2023-08-31 21:54:13
堡壘機怎麼打開web 2023-08-31 21:54:11

mybatis緩存版

發布時間: 2023-08-09 12:20:10

Ⅰ mybatis的緩存機制是怎麼樣的

MyBatis數據緩存設計兩級結構級緩存、二級緩存:
級緩存Session級別緩存位於表示資料庫sqlSession象稱本緩存級緩存MyBatis內部實現特性用戶能配置默認情況自支持緩存用戶沒定製權利(絕通發插件進行修改);
二級緩存Application應用級別緩存命周期跟Application聲明周期說作用范圍整Application應用

Ⅱ mybatis為什麼需要分布式緩存

通常為了減輕資料庫的壓力,我們會引入緩存。在Dao查詢資料庫之前,先去緩存中找是否有要找的數據,如果有則用緩存中的數據即可,就不用查詢數
據庫了。如果沒有才去資料庫中查找。這樣就能分擔一下資料庫的壓力。另外,為了讓緩存中的數據與資料庫同步,我們應該在該數據發生變化的地方加入更新緩存
的邏輯代碼。這樣無形之中增加了工作量,同時也是一種對原有代碼的入侵。這對於有著代碼潔癖的程序員來說,無疑是一種傷害。

MyBatis框架早就考慮到了這些問題,因此MyBatis提供了自定義的二級緩存概念,方便引入我們自己的緩存機制,而不用更改原有的業務邏輯。

Ⅲ mybatis一級緩存內存佔用過大的問題

內存佔用過大可以通過flushCache="true"或者where <隨機數>=<隨機數>去除MyBatis的一級緩存來解決。

1、一級緩存是SqlSession級別的緩存 —— 它是各自獨立的。

在操作資料庫時需要構造sqlSession對象,在對象中有一個數據結構(HashMap)用於存儲緩存數據。

不同的sqlSession之間的緩存數據區域(HashMap)是互相不影響的。

2、二級緩存是mapper級別的緩存 —— 它是多個 SqlSession 共享的。

多個SqlSession去操作同一個Mapper的sql語句,多個SqlSession可以共用二級緩存,二級緩存是跨SqlSession的。

Ⅳ mybatis的緩存有幾種

1、一級緩存

MyBatis默認開啟了一級緩存,一級緩存是在SqlSession 層面進行緩存的。即,同一個SqlSession ,多次調用同一個Mapper和同一個方法的同一個參數,只會進行一次資料庫查詢,然後把數據緩存到緩沖中,以後直接先從緩存中取出數據,不會直接去查資料庫。

但是不同的SqlSession對象,因為不用的SqlSession都是相互隔離的,所以相同的Mapper、參數和方法,他還是會再次發送到SQL到資料庫去執行,返回結果。

2、二級緩存

為了克服這個問題,需要開啟二級緩存,是的緩存zaiSqlSessionFactory層面給各個SqlSession 對象共享。默認二級緩存是不開啟的,需要手動進行配置。

<cache/>

如果這樣配置的話,很多其他的配置就會被默認進行,如:

  • 映射文件所有的select 語句會被緩存

  • 映射文件的所有的insert、update和delete語句會刷新緩存

  • 緩存會使用默認的Least Recently Used(LRU,最近最少使用原則)的演算法來回收緩存空間

  • 根據時間表,比如No Flush Interval,(CNFI,沒有刷新間隔),緩存不會以任何時間順序來刷新

  • 緩存會存儲列表集合或對象(無論查詢方法返回什麼)的1024個引用

  • 緩存會被視為是read/write(可讀/可寫)的緩存,意味著對象檢索不是共享的,而且可以很安全的被調用者修改,不幹擾其他調用者或縣城所作的潛在修改

  • 可以在開啟二級緩存時候,手動配置一些屬性

  • <cache eviction="LRU" flushInterval="100000" size="1024" readOnly="true"/>

  • 各個屬性意義如下:

  • eviction:緩存回收策略
    - LRU:最少使用原則,移除最長時間不使用的對象
    - FIFO:先進先出原則,按照對象進入緩存順序進行回收
    - SOFT:軟引用,移除基於垃圾回收器狀態和軟引用規則的對象
    - WEAK:弱引用,更積極的移除移除基於垃圾回收器狀態和弱引用規則的對象

  • flushInterval:刷新時間間隔,單位為毫秒,這里配置的100毫秒。如果不配置,那麼只有在進行資料庫修改操作才會被動刷新緩存區

  • size:引用額數目,代表緩存最多可以存儲的對象個數

  • readOnly:是否只讀,如果為true,則所有相同的sql語句返回的是同一個對象(有助於提高性能,但並發操作同一條數據時,可能不安全),如果設置為false,則相同的sql,後面訪問的是cache的clone副本。

  • 可以在Mapper的具體方法下設置對二級緩存的訪問意願:

  • useCache配置

    如果一條語句每次都需要最新的數據,就意味著每次都需要從資料庫中查詢數據,可以把這個屬性設置為false,如:

  • <select id="selectAll" resultMap="BaseResultMap" useCache="false">

  • 刷新緩存(就是清空緩存)

    二級緩存默認會在insert、update、delete操作後刷新緩存,可以手動配置不更新緩存,如下:

  • <update id="updateById" parameterType="User" flushCache="false" />


  • 3、自定義緩存

    自定義緩存對象,該對象必須實現 org.apache.ibatis.cache.Cache 介面

每次查詢資料庫前,MyBatis都會先在緩存中查找是否有該緩存對象。只有當調用了commit() 方法,MyBatis才會往緩存中寫入數據,數據記錄的鍵為數字編號+Mapper名+方法名+SQL語句+參數格式,值為返回的對象值。

Ⅳ MyBatis二級緩存帶來的問題

MyBatis二級緩存使用的在某些場景下會出問題,來看一下為什麼這么說。

假設我有一條select語句(開啟了二級緩存):

selecta.col1, a.col2, a.col3, b.col1, b.col2, b.col3fromtableA a, tableB bwherea.id= b.id;

對於tableA與tableB的操作定義在兩個Mapper中,分別叫做MapperA與MapperB,即它們屬於兩個命名空間,如果此時啟用緩存:

MapperA中執行上述sql語句查詢這6個欄位

tableB更新了col1與col2兩個欄位

MapperA再次執行上述sql語句查詢這6個欄位(前提是沒有執行過任何insert、delete、update操作)

此時問題就來了,即使第(2)步tableB更新了col1與col2兩個欄位,第(3)步MapperA走二級緩存查詢到的這6個欄位依然是原來的這6個欄位的值,因為我們從CacheKey的3組條件來看:

<select>標簽所在的Mapper的Namespace+<select>標簽的id屬性

RowBounds的offset和limit屬性,RowBounds是MyBatis用於處理分頁的一個類,offset默認為0,limit默認為Integer.MAX_VALUE

<select>標簽中定義的sql語句

對於MapperA來說,其中的任何一個條件都沒有變化,自然會將原結果返回。

這個問題對於MyBatis的二級緩存來說是一個無解的問題,因此使用MyBatis二級緩存有一個前提: 必須保證所有的增刪改查都在同一個命名空間下才行 。

Ⅵ mybatis二級緩存原理

mybatis篇

一級緩存的作用域是Sqlsession級別的,也就是說不同的Sqlsession是不會走一級緩存的,那麼如果需要跨Sqlsession的緩存,就需要使用到二級緩存了。

二級緩存的話默認是關閉的,所以需要我們開啟,開啟的方式官網也有介紹,需要在mybatis-config.xml核心配置文件中開啟二級緩存功能,並且我們mapper.xml中也需要加入<cache/>標簽,二者缺一不可,後面我們看源碼就能知道為啥這兩個缺一不可。

先來看個例子

執行結果很意外,為什麼二級緩存的功能都開啟了,結果sql還是執行了2次,並沒有走緩存,其實,二級緩存還有一個要注意的點那就是必須要提交事務二級緩存才會保存記錄,因為已經是跨SqlSession共享緩存了,所以事務必須要提交,否則會讀取到因混滾導致的錯誤數據。

有個地方需要注意,二級緩存的Sqlsession中的Executor實際上是CachingExecutor

我們知道getMapper最終的執行都會走到MapperProxy類中的invoker方法,具體就來分析這個類。

最後來到了重點的地方

CacheKey我們可以認為他就是每個方法對應的一個唯一標識符。

這里我們就可以看出為什麼之前兩者必須要配置,cacheEnable開啟了才會用CachingExecutor包裝一下BaseExecutor,而<cache/>標簽只有配置了才會走緩存的邏輯

這里的tcm

到這,我們就差不多揭開了二級緩存的秘密,重要的還是<cache/>這個標簽,因為它的存在就對應著每個mapper.xml中的一個具體Cache類,而這個類在每個mapper.xml中又是同一個,所以最終的值是放入了Cache類中,key為CacheKey,value就是sql執行的結果。
至於為什麼需要事務提交才能命中二級緩存,我們看下put方法就知道

這里的putObject並沒有真正的把值存入Cache中,而是存入了待提交的Map中,所以再來看下commit做了什麼

具體看tcm.commit()

而這里可以看到此處會遍歷所有的TransactionCache並執行commit方法

真相就出來了,會遍歷待提交的Map然後把裡面的值都存入Cache中,所以後面的查詢就能直接從Cache中拿到值了。

總結
二級緩存先會把Sqlsession中的Executor包裝成包裝成CacheingExecutor,所有的sql都會經過這個類,而該類通過mapper.xml中配置的唯一<cache/>標簽生成的Cache類存放每個方法執行的結果

Ⅶ mybatis的緩存機制是怎麼樣的

Mybatis緩存處理機制
MyBatis緩存介紹
正如大多數持久層框架一樣,MyBatis 同樣提供了一級緩存和二級緩存的支持
一級緩存: 基於PerpetualCache 的 HashMap本地緩存,其存儲作用域為 Session,當 Session flush 或 close 之後,該Session中的所有 Cache 就將清空。
2. 二級緩存與一級緩存其機制相同,默認也是採用 PerpetualCache,HashMap存儲,不同在於其存儲作用域為 Mapper(Namespace),並且可自定義存儲源,如 Ehcache。
3. 對於緩存數據更新機制,當某一個作用域(一級緩存Session/二級緩存Namespaces)的進行了 C/U/D 操作後,默認該作用域下所有 select 中的緩存將被clear。

Ⅷ mybatis的緩存機制是怎麼樣的

MyBatis將數據緩存設計成兩級結構,分為一級緩存、二級緩存:
一級緩存是Session會話級別的緩存,位於表示一次資料庫會話的SqlSession對象之中,又被稱之為本地緩存。一級緩存是MyBatis內部實現的一個特性,用戶不能配置,默認情況下自動支持的緩存,用戶沒有定製它的權利(不過這也不是絕對的,可以通過開發插件對它進行修改);
二級緩存是Application應用級別的緩存,它的是生命周期很長,跟Application的聲明周期一樣,也就是說它的作用范圍是整個Application應用。

Ⅸ mybatis 二級緩存怎麼使用

深入了解MyBatis二級緩存
一、創建Cache的完整過程
我們從SqlSessionFactoryBuilder解析mybatis-config.xml配置文件開始:
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);

然後是:
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());

看parser.parse()方法:
parseConfiguration(parser.evalNode("/configuration"));

看處理Mapper.xml文件的位置:
mapperElement(root.evalNode("mappers"));

看處理Mapper.xml的XMLMapperBuilder:
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration,
resource, configuration.getSqlFragments());
mapperParser.parse();

繼續看parse方法:
configurationElement(parser.evalNode("/mapper"));

到這里:
String namespace = context.getStringAttribute("namespace");
if (namespace.equals("")) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
builderAssistant.setCurrentNamespace(namespace);
cacheRefElement(context.evalNode("cache-ref"));
cacheElement(context.evalNode("cache"));

從這里看到namespace就是xml中<mapper>元素的屬性。然後下面是先後處理的cache-ref和cache,後面的cache會覆蓋前面的cache-ref,但是如果一開始cache-ref沒有找到引用的cache,他就不會被覆蓋,會一直到最後處理完成為止,最後如果存在cache,反而會被cache-ref覆蓋。這里是不是看著有點暈、有點亂?所以千萬別同時配置這兩個,實際上也很少有人會這么做。
看看MyBatis如何處理<cache/>:
private void cacheElement(XNode context) throws Exception {
if (context != null) {
String type = context.getStringAttribute("type", "PERPETUAL");
Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
String eviction = context.getStringAttribute("eviction", "LRU");
Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);
Long flushInterval = context.getLongAttribute("flushInterval");
Integer size = context.getIntAttribute("size");
boolean readWrite = !context.getBooleanAttribute("readOnly", false);
boolean blocking = context.getBooleanAttribute("blocking", false);
Properties props = context.getChildrenAsProperties();
builderAssistant.useNewCache(typeClass, evictionClass,
flushInterval, size, readWrite, blocking, props);
}
}

從源碼可以看到MyBatis讀取了那些屬性,而且很容易可以到這些屬性的默認值。
創建Java的cache對象方法為builderAssistant.useNewCache,我們看看這段代碼:
public Cache useNewCache(Class<? extends Cache> typeClass,
Class<? extends Cache> evictionClass,
Long flushInterval,
Integer size,
boolean readWrite,
boolean blocking,
Properties props) {
typeClass = valueOrDefault(typeClass, PerpetualCache.class);
evictionClass = valueOrDefault(evictionClass, LruCache.class);
Cache cache = new CacheBuilder(currentNamespace)
.implementation(typeClass)
.addDecorator(evictionClass)
.clearInterval(flushInterval)
.size(size)
.readWrite(readWrite)
.blocking(blocking)
.properties(props)
.build();
configuration.addCache(cache);
currentCache = cache;
return cache;
}

從調用該方法的地方,我們可以看到並沒有使用返回值cache,在後面的過程中創建MappedStatement的時候使用了currentCache。
二、使用Cache過程
在系統中,使用Cache的地方在CachingExecutor中:
@Override
public <E> List<E> query(
MappedStatement ms, Object parameterObject,
RowBounds rowBounds, ResultHandler resultHandler,
CacheKey key, BoundSql boundSql) throws SQLException {
Cache cache = ms.getCache();

獲取cache後,先判斷是否有二級緩存。
只有通過<cache/>,<cache-ref/>或@CacheNamespace,@CacheNamespaceRef標記使用緩存的Mapper.xml或Mapper介面(同一個namespace,不能同時使用)才會有二級緩存。
if (cache != null) {

如果cache存在,那麼會根據sql配置(<insert>,<select>,<update>,<delete>的flushCache屬性來確定是否清空緩存。
flushCacheIfRequired(ms);

然後根據xml配置的屬性useCache來判斷是否使用緩存(resultHandler一般使用的默認值,很少會null)。
if (ms.isUseCache() && resultHandler == null) {

確保方法沒有Out類型的參數,mybatis不支持存儲過程的緩存,所以如果是存儲過程,這里就會報錯。
ensureNoOutParams(ms, parameterObject, boundSql);

沒有問題後,就會從cache中根據key來取值:
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);

如果沒有緩存,就會執行查詢,並且將查詢結果放到緩存中。
if (list == null) {
list = delegate.<E>query(ms, parameterObject,
rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list); // issue #578 and #116
}

返回結果
return list;
}
}

沒有緩存時,直接執行查詢
return delegate.<E>query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

在上面的代碼中tcm.putObject(cache, key, list);這句代碼是緩存了結果。但是實際上直到sqlsession關閉,MyBatis才以序列化的形式保存到了一個Map(默認的緩存配置)中。

三、Cache使用時的注意事項
1. 只能在【只有單表操作】的表上使用緩存
不只是要保證這個表在整個系統中只有單表操作,而且和該表有關的全部操作必須全部在一個namespace下。
2. 在可以保證查詢遠遠大於insert,update,delete操作的情況下使用緩存
這一點不需要多說,所有人都應該清楚。記住,這一點需要保證在1的前提下才可以!

四、避免使用二級緩存
可能會有很多人不理解這里,二級緩存帶來的好處遠遠比不上他所隱藏的危害。
緩存是以namespace為單位的,不同namespace下的操作互不影響。
insert,update,delete操作會清空所在namespace下的全部緩存。
通常使用MyBatis Generator生成的代碼中,都是各個表獨立的,每個表都有自己的namespace。
為什麼避免使用二級緩存
在符合【Cache使用時的注意事項】的要求時,並沒有什麼危害。
其他情況就會有很多危害了。
針對一個表的某些操作不在他獨立的namespace下進行。
例如在UserMapper.xml中有大多數針對user表的操作。但是在一個XXXMapper.xml中,還有針對user單表的操作。
這會導致user在兩個命名空間下的數據不一致。如果在UserMapper.xml中做了刷新緩存的操作,在XXXMapper.xml中緩存仍然有效,如果有針對user的單表查詢,使用緩存的結果可能會不正確。
更危險的情況是在XXXMapper.xml做了insert,update,delete操作時,會導致UserMapper.xml中的各種操作充滿未知和風險。
有關這樣單表的操作可能不常見。但是你也許想到了一種常見的情況。
多表操作一定不能使用緩存
為什麼不能?
首先不管多表操作寫到那個namespace下,都會存在某個表不在這個namespace下的情況。
例如兩個表:role和user_role,如果我想查詢出某個用戶的全部角色role,就一定會涉及到多表的操作。
<select id="selectUserRoles" resultType="UserRoleVO">
select * from user_role a,role b where a.roleid = b.roleid and a.userid = #{userid}
</select>123123

像上面這個查詢,你會寫到那個xml中呢??
不管是寫到RoleMapper.xml還是UserRoleMapper.xml,或者是一個獨立的XxxMapper.xml中。如果使用了二級緩存,都會導致上面這個查詢結果可能不正確。
如果你正好修改了這個用戶的角色,上面這個查詢使用緩存的時候結果就是錯的。
這點應該很容易理解。
在我看來,就以MyBatis目前的緩存方式來看是無解的。多表操作根本不能緩存。
如果你讓他們都使用同一個namespace(通過<cache-ref>)來避免臟數據,那就失去了緩存的意義。
看到這里,實際上就是說,二級緩存不能用。整篇文章介紹這么多也沒什麼用了。

五、挽救二級緩存?
想更高效率的使用二級緩存是解決不了了。
但是解決多表操作避免臟數據還是有法解決的。解決思路就是通過攔截器判斷執行的sql涉及到那些表(可以用jsqlparser解析),然後把相關表的緩存自動清空。但是這種方式對緩存的使用效率是很低的。
設計這樣一個插件是相當復雜的,既然我沒想著去實現,就不廢話了。
最後還是建議,放棄二級緩存,在業務層使用可控制的緩存代替更好。

Ⅹ mybatis緩存取得的數據是按條件查詢的嗎

最近在使用mybatis的過程中,發現一個問題。如果在同一個事物中,多次同一個查詢sql在mybatis的執行過程中,只會查詢一次資料庫,後幾次所返回的對象是mybatis在在內部做了緩存。
Property property = this.findByPropertyId("123");
property.setPropertyId(null);;
property = this.findByPropertyId("123");
System.out.println(property.getPropertyId());

以上的代碼,列印的結果為 null , 但是我們所期望的可能是 123 , 我不知道這是mybatis的一個bug還是故意這樣去設計的.mybatis在執行查詢語句的時候,會在本地做一份緩存信息.在BaseExecutor類中:

private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
localCache.removeObject(key);
}
localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}

可以看到在queryFromDatabase方法中,查詢資料庫返回結果之後,mybatis編制了一個cachekey的對象,作為key,返回結果作為value,放入了緩存當中 (這個地方沒有使用拷貝的函數,所以只要外部修改了值,內部緩存中的值信息也會被修改)
之後再下次查詢的時候,會依據一個判斷,是否需要執行緩存信息,同樣是在BaseExecutor類中.

@SuppressWarnings("unchecked")
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) {
(ms, key, parameter, boundSql);
} else {
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
if (queryStack == 0) {
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
deferredLoads.clear(); // issue #601
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
clearLocalCache(); // issue #482
}
}
return list;
}

看到mybatis判斷了 ms.isFlushCacheRequired() 的返回數據,如果為 true 會執行 clearLocalCache 方法,清空緩存信息。如果緩存中獲取不到的話,才會繼續去查詢資料庫。
可以從 list = resultHandler == null ? (List<E>) localCache.getObject(key) : null; 代碼中看出。
所以當第一次查詢放入緩存之後,在外部修改了任何一個值之後,mybatis內部緩存的值也會被修改,而且下次查詢不會查詢資料庫,直接返回緩存中被修改過的值
ms.isFlushCacheRequired() 這段代碼的判斷是基於了一個MappedStatement 類中的flushCacheRequired 的屬性做判斷的。flushCacheRequired 變數可以通過註解的方式和xml的方式來配置

1.註解:註解的方式是通過 @Options 註解中 flushCache 的配置
2.配置文件:xml中每一個select 都可以設置 flushCache 的屬性
flushCache 設置成true之後,本sql的每次查詢都會清空緩存後在執行。