二級緩存區域是根據mapper的namespace划分的,相同namespace的mapper查詢數據放在同一個區域,如果使用mapper代理方法每個mapper的namespace都不同,此時可以理解為二級緩存區域是根據mapper划分,也就是根據命名空間來划分的,如果兩個mapper文件的命名空間一樣,那樣,他們就可以共享一個mapper緩存。
每次查詢會先從緩存區域找,如果找不到從數據庫查詢,查詢到數據將數據寫入緩存。
Mybatis內部存儲緩存使用一個HashMap,key為hashCode+sqlId+Sql語句。value為從查詢出來映射生成的Java對象
sqlSession執行insert、update、delete等操作commit提交后會清空緩存區域。
開啟緩存:
在這特別提醒一下,Mybatis的二級緩存是需要配置來開啟的,我們需要在Mybatis的核心配置文件SqlMapConfig.xml中加入:
- <span style="font-size:18px;"><setting name="cacheEnabled" value="true"/></span>
然后還要在Mapper映射文件中添加一行:
- <span style="font-size:18px;"><cache/> <!--<span >表示此mapper開啟二級緩存--></span></span>
假如說,已開啟二級緩存的Mapper中有個statement要求禁用怎么辦,那也不難,只需要在statement中設置useCache=false就可以禁用當前select語句的二級緩存,也就是每次都會生成sql去查詢,ps:默認情況下默認是true,也就是默認使用二級緩存
- <span style="font-size:18px;"><select id="findOrderListResultMap" resultMap="ordersUserMap" useCache="false"></span>
刷新緩存:
在mapper的同一個namespace中,如果有其他insert、update、delete操作后都需要執行刷新緩存操作,來避免臟讀。這時我們只需要設置statement配置中的flushCache=“true“屬性,就會默認刷新緩存,相反如果是false就不會了。當然,不管開不開緩存刷新功能,你要是手動更改數據庫表,那都肯定不能避免臟讀的發生,那就屬於手賤了。
- <span style="font-size:18px;"><insert id="insertUser" parameterType="cn.ssm.mybatis.po.User" flushCache="true"></span>
那既然能夠刷新緩存,能定時刷新嗎?也就是設置時間間隔來刷新緩存,答案是肯定的。我們在mapper映射文件中添加<cache/>來表示開啟緩存,那接下來,只需要我們在配置flushinterval(刷新間隔)就哦了:
- <span style="font-size:18px;"><cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/></span>
flushInterval(刷新間隔)可以被設置為任意的正整數,而且它們代表一個合理的毫秒形式的時間段。默認情況是不設置,也就是沒有刷新間隔,緩存僅僅調用語句時刷新。 size(引用數目)可以被設置為任意正整數,要記住你緩存的對象數目和你運行環境的可用內存資源數目。默認值是1024。 readOnly(只讀)屬性可以被設置為true或false。只讀的緩存會給所有調用者返回緩存對象的相同實例。因此這些對象不能被修改。這提供了很重要的性能優勢。可讀寫的緩存會返回緩存對象的拷貝(通過序列化)。這會慢一些,但是安全,因此默認是false。
而這個例子更高級的配置創建了一個 FIFO 緩存,並每隔 60 秒刷新,存數結果對象或列表的 512 個引用,而且返回的對象被認為是只讀的,因此在不同線程中的調用者之間修改它們會導致沖突。可用的收回策略有, 默認的是 LRU:
1. LRU – 最近最少使用的:移除最長時間不被使用的對象。
2. FIFO – 先進先出:按對象進入緩存的順序來移除它們。
3. SOFT – 軟引用:移除基於垃圾回收器狀態和軟引用規則的對象。
4. WEAK – 弱引用:更積極地移除基於垃圾收集器狀態和弱引用規則的對象。
這就是我們軟考擅長的東西了,不用解釋了吧!
說了這么多,擺個例子來結束本篇博文:
- //獲取session1
- SqlSession session1 = sqlSessionFactory.openSession();
- UserMapper userMapper = session1.getMapper(UserMapper.class);
- //使用session1執行第一次查詢
- User user1 = userMapper.findUserById(1);
- System.out.println(user1);
- //關閉session1
- session1.close();
- //獲取session2
- SqlSession session2 = sqlSessionFactory.openSession();
- UserMapper userMapper2 = session2.getMapper(UserMapper.class);
- //使用session2執行第二次查詢,由於開啟了二級緩存這里從緩存中獲取數據不再向數據庫發出sql
- User user2 = userMapper2.findUserById(1);
- System.out.println(user2);
- //關閉session2
- session2.close();
運行效果:
而如果我們在1、2之間執行一次commit操作,就變成了:
由此可見,Mybatis的二級緩存是跨Session的,每個Mapper享有同一個二級緩存域,同樣,每次執行commit操作之后,同樣會清空緩存。
Mybatis這么好,如何應用呢?
因為這是一種緩存機制嘛,只有相對於實時性要求不高的需求才會使用緩存機制,它也一樣。對於訪問多的查詢請求且用戶對查詢結果實時性要求不高,此時可采用mybatis二級緩存技術降低數據庫訪問量,提高訪問速度,業務場景比如:耗時較高的統計分析sql、電話賬單查詢sql等。 實現方法如下:通過設置刷新間隔時間,由mybatis每隔一段時間自動清空緩存,根據數據變化頻率設置緩存刷新間隔flushInterval,比如設置為30分鍾、60分鍾、24小時等,根據需求而定。
可是,好歸好,Mybatis也有它一定的局限性。那就是Mybatis對於細粒度的數據級別的緩存實現的不是太好,也就是如果同Mapper下的商品類別繁多的話,他不能實現只刷新某固定商品的信息,而只能全盤刷新。當時將這塊的時候我想過讓Mapper水平分區不就行了,可是后來說到Mybatis的二級緩存是以命名空間划分的或者說是以Mapper划分,不管我們怎么水平划分,只要命名空間一樣,那就只共享一個二級緩存域,當刷新的時候還是會全Mapper更新一遍。
轉載:https://www.cnblogs.com/DoubleEggs/p/6243223.html