1. 二級緩存的原理
前面介紹了异袄,mybatis中的二級緩存是mapper級別的緩存,值得注意的是冤灾,不同的mapper都有一個二級緩存前域,也就是說,不同的mapper之間的二級緩存是互不影響的韵吨。為了更加清楚的描述二級緩存匿垄,先來看一個示意圖:
從圖中可以看出:
1.sqlSession1去查詢用戶id為1的用戶信息,查詢到用戶信息會將查詢數(shù)據(jù)存儲到該UserMapper的二級緩存中归粉。
2.如果SqlSession3去執(zhí)行相同 mapper下sql椿疗,執(zhí)行commit提交,則會清空該UserMapper下二級緩存區(qū)域的數(shù)據(jù)糠悼。
3.sqlSession2去查詢用戶id為1的用戶信息届榄,去緩存中找是否存在數(shù)據(jù),如果存在直接從緩存中取出數(shù)據(jù)倔喂。
緩存的執(zhí)行原理和前面提到的一級緩存是差不多的铝条,二級緩存與一級緩存區(qū)別在于二級緩存的范圍更大,多個sqlSession可以共享一個mapper中的二級緩存區(qū)域席噩。mybatis是如何區(qū)分不同mapper的二級緩存區(qū)域呢班缰?它是按照不同mapper有不同的namespace來區(qū)分的,也就是說悼枢,如果兩個mapper的namespace相同埠忘,即使是兩個mapper,那么這兩個mapper中執(zhí)行sql查詢到的數(shù)據(jù)也將存在相同的二級緩存區(qū)域中。
2. 二級緩存的使用
明白了mybatis中二級緩存的原理后莹妒,接下來就是如何使用二級緩存了假丧。在使用之前,首先得開啟二級緩存的開關(guān)动羽。
2.1 開啟二級緩存
由于mybaits的二級緩存是mapper范圍級別包帚,所以除了在SqlMapConfig.xml設(shè)置二級緩存的總開關(guān)外,還要在具體的mapper.xml中開啟二級緩存运吓。設(shè)置如下:
這是在SqlMapConfig.xml中設(shè)置的渴邦,還得在具體的mapper.xml中設(shè)置,如下:
可以看到拘哨,具體的mapper中僅僅就一個<cache>標(biāo)簽谋梭,并沒有配置啥東西,這是因為mybatis中有默認(rèn)的實現(xiàn)倦青,我們?nèi)绻慌渲梦痛玻敲淳湍J(rèn)使用那個默認(rèn)的實現(xiàn)。在mybatis的核心包里有cache的接口和這個默認(rèn)的實現(xiàn)产镐,我截個圖:
所以就明白了隘庄,為啥不用配置都可以使用,mybatis中也就只有這一個默認(rèn)實現(xiàn)類癣亚,如果不使用mybatis的默認(rèn)二級緩存的話丑掺,就需要自己實現(xiàn)cache接口,然后再在mapper.xml中配置一下了述雾,關(guān)于這個我在下面再談街州,現(xiàn)在先把二級緩存用起來!
將po類實現(xiàn)Serializable接口
開啟了二級緩存后玻孟,還需要將要緩存的pojo實現(xiàn)Serializable接口唆缴,為了將緩存數(shù)據(jù)取出執(zhí)行反序列化操作,因為二級緩存數(shù)據(jù)存儲介質(zhì)多種多樣黍翎,不一定只存在內(nèi)存中面徽,有可能存在硬盤中玩敏,如果我們要再取這個緩存的話斗忌,就需要反序列化了质礼。所以建議mybatis中的pojo都去實現(xiàn)Serializable接口眶蕉。下面以User為例截個圖:
2.3 測試mybatis的二級緩存
@Test
public void testCache2() throws Exception {
SqlSession sqlSession1 = sqlSessionFactory.openSession();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
SqlSession sqlSession3 = sqlSessionFactory.openSession();
// 創(chuàng)建代理對象
UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
// 第一次發(fā)起請求蚓让,查詢id為1的用戶
User user1 = userMapper1.findUserById(1);
System.out.println(user1);
//這里執(zhí)行關(guān)閉操作,將sqlsession中的數(shù)據(jù)寫到二級緩存區(qū)域
sqlSession1.close();
//sqlSession3用來清空緩存的锄列,如果要測試二級緩存,需要把該部分注釋掉
//使用sqlSession3執(zhí)行commit()操作
UserMapper userMapper3 = sqlSession3.getMapper(UserMapper.class);
User user = userMapper3.findUserById(1);
user.setUsername("倪升武");
userMapper3.updateUser(user);
//執(zhí)行提交,清空UserMapper下邊的二級緩存
sqlSession3.commit();
sqlSession3.close();
UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
// 第二次發(fā)起請求歇万,查詢id為1的用戶
User user2 = userMapper2.findUserById(1);
System.out.println(user2);
sqlSession2.close();
}
我們先把sqlSession3部分注釋掉來測試一下二級緩存的結(jié)果:
2.4 其他配置(useCache和flushCache)
mybatis中還可以配置userCache和flushCache等配置項泵三,userCache是用來設(shè)置是否禁用二級緩存的烫幕,在statement中設(shè)置useCache=false可以禁用當(dāng)前select語句的二級緩存冕末,即每次查詢都會發(fā)出sql去查詢,默認(rèn)情況是true嘹屯,即該sql使用二級緩存攻询。
<select id="findOrderListResultMap" resultMap="ordersUserMap" useCache="false">
這種情況是針對每次查詢都需要最新的數(shù)據(jù)sql,要設(shè)置成useCache=false抚垄,禁用二級緩存蜕窿,直接從數(shù)據(jù)庫中獲取谋逻。 在mapper的同一個namespace中呆馁,如果有其它insert桐经、update、delete操作數(shù)據(jù)后需要刷新緩存浙滤,如果不執(zhí)行刷新緩存會出現(xiàn)臟讀阴挣。 設(shè)置statement配置中的flushCache=”true” 屬性,默認(rèn)情況下為true纺腊,即刷新緩存畔咧,如果改成false則不會刷新。使用緩存時如果手動修改數(shù)據(jù)庫表中的查詢數(shù)據(jù)會出現(xiàn)臟讀揖膜。
<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User" flushCache="true">
一般下執(zhí)行完commit操作都需要刷新緩存誓沸,flushCache=true表示刷新緩存,這樣可以避免數(shù)據(jù)庫臟讀壹粟。所以我們不用設(shè)置拜隧,默認(rèn)即可,這里只是提一下.
3. MyBatis整合ehcache分布式緩存框架
3.1 問題的由來
上面的部分主要總結(jié)了一下mybatis中二級緩存的使用趁仙,但是mybatis中默認(rèn)自帶的二級緩存有個弊端洪添,即無法實現(xiàn)分布式緩存,什么意思呢雀费?就是說緩存的數(shù)據(jù)在自己的服務(wù)器上干奢,假設(shè)現(xiàn)在有兩個服務(wù)器A和B,用戶訪問的時候訪問了A服務(wù)器盏袄,查詢后的緩存就會放在A服務(wù)器上忿峻,假設(shè)現(xiàn)在有個用戶訪問的是B服務(wù)器,那么他在B服務(wù)器上就無法獲取剛剛那個緩存辕羽,如下圖所示:3.2 整合方法
上文一開始提到過停团,mybatis提供了一個cache接口旷坦,如果要實現(xiàn)自己的緩存邏輯,實現(xiàn)cache接口開發(fā)即可佑稠。mybatis本身默認(rèn)實現(xiàn)了一個秒梅,但是這個緩存的實現(xiàn)無法實現(xiàn)分布式緩存,所以我們要自己來實現(xiàn)舌胶。ehcache分布式緩存就可以捆蜀,mybatis提供了一個針對cache接口的ehcache實現(xiàn)類,這個類在mybatis和ehcache的整合包中幔嫂。所以首先我們需要導(dǎo)入整合包(點我下載)辆它。
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<diskStore path="F:\develop\ehcache"/>
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
maxElementsOnDisk="10000000"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
<persistence strategy="localTempSwap"/>
</defaultCache>
</ehcache>
4. 二級緩存的應(yīng)用場景和局限性
對于訪問多的查詢請求且用戶對查詢結(jié)果實時性要求不高扬霜,此時可采用mybatis二級緩存技術(shù)降低數(shù)據(jù)庫訪問量,提高訪問速度而涉,業(yè)務(wù)場景比如:耗時較高的統(tǒng)計分析sql著瓶、電話賬單查詢sql等。實現(xiàn)方法如下:通過設(shè)置刷新間隔時間啼县,由mybatis每隔一段時間自動清空緩存材原,根據(jù)數(shù)據(jù)變化頻率設(shè)置緩存刷新間隔flushInterval,比如設(shè)置為30分鐘季眷、60分鐘余蟹、24小時等,根據(jù)需求而定子刮。
mybatis二級緩存對細粒度的數(shù)據(jù)級別的緩存實現(xiàn)不好威酒,比如如下需求:對商品信息進行緩存,由于商品信息查詢訪問量大挺峡,但是要求用戶每次都能查詢最新的商品信息葵孤,此時如果使用mybatis的二級緩存就無法實現(xiàn)當(dāng)一個商品變化時只刷新該商品的緩存信息而不刷新其它商品的信息,因為mybaits的二級緩存區(qū)域以mapper為單位劃分的橱赠,當(dāng)一個商品信息變化會將所有商品信息的緩存數(shù)據(jù)全部清空尤仍。解決此類問題可能需要在業(yè)務(wù)層根據(jù)需求對數(shù)據(jù)有針對性緩存。