Mybatis 詳解--- 一級緩存、二級緩存

Mybatis 爲咱們提供了一級緩存和二級緩存,能夠經過下圖來理解:spring

①、一級緩存是SqlSession級別的緩存。在操做數據庫時須要構造sqlSession對象,在對象中有一個數據結構(HashMap)用於存儲緩存數據。不一樣的sqlSession之間的緩存數據區域(HashMap)是互相不影響的。sql

②、二級緩存是mapper級別的緩存,多個SqlSession去操做同一個Mapper的sql語句,多個SqlSession能夠共用二級緩存,二級緩存是跨SqlSession的。數據庫

一、一級緩存

①、咱們在一個 sqlSession 中,對 User 表根據id進行兩次查詢,查看他們發出sql語句的狀況。

@Test
public void testSelectOrderAndUserByOrderId(){
	//根據 sqlSessionFactory 產生 session
	SqlSession sqlSession = sessionFactory.openSession();
	String statement = "one.to.one.mapper.OrdersMapper.selectOrderAndUserByOrderID";
	UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
	//第一次查詢,發出sql語句,並將查詢的結果放入緩存中
	User u1 = userMapper.selectUserByUserId(1);
	System.out.println(u1);
	
	//第二次查詢,因爲是同一個sqlSession,會在緩存中查找查詢結果
	//若是有,則直接從緩存中取出來,不和數據庫進行交互
	User u2 = userMapper.selectUserByUserId(1);
	System.out.println(u2);
	
	sqlSession.close();
}

查看控制檯打印狀況:apache

②、 一樣是對user表進行兩次查詢,只不過兩次查詢之間進行了一次update操做。

@Test
public void testSelectOrderAndUserByOrderId(){
	//根據 sqlSessionFactory 產生 session
	SqlSession sqlSession = sessionFactory.openSession();
	String statement = "one.to.one.mapper.OrdersMapper.selectOrderAndUserByOrderID";
	UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
	//第一次查詢,發出sql語句,並將查詢的結果放入緩存中
	User u1 = userMapper.selectUserByUserId(1);
	System.out.println(u1);
	
	//第二步進行了一次更新操做,sqlSession.commit()
	u1.setSex("女");
	userMapper.updateUserByUserId(u1);
	sqlSession.commit();
	
	//第二次查詢,因爲是同一個sqlSession.commit(),會清空緩存信息
	//則這次查詢也會發出 sql 語句
	User u2 = userMapper.selectUserByUserId(1);
	System.out.println(u2);
	
	sqlSession.close();
}

控制檯打印狀況:緩存

③、總結

一、第一次發起查詢用戶id爲1的用戶信息,先去找緩存中是否有id爲1的用戶信息,若是沒有,從數據庫查詢用戶信息。獲得用戶信息,將用戶信息存儲到一級緩存中。性能優化

二、若是中間sqlSession去執行commit操做(執行插入、更新、刪除),則會清空SqlSession中的一級緩存,這樣作的目的爲了讓緩存中存儲的是最新的信息,避免髒讀。session

三、第二次發起查詢用戶id爲1的用戶信息,先去找緩存中是否有id爲1的用戶信息,緩存中有,直接從緩存中獲取用戶信息。數據結構

二、二級緩存

二級緩存的原理和一級緩存原理同樣,第一次查詢,會將數據放入緩存中,而後第二次查詢則會直接去緩存中取。可是一級緩存是基於 sqlSession 的,而 二級緩存是基於 mapper文件的namespace的,也就是說多個sqlSession能夠共享一個mapper中的二級緩存區域,而且若是兩個mapper的namespace相同,即便是兩個mapper,那麼這兩個mapper中執行sql查詢到的數據也將存在相同的二級緩存區域中。mybatis

那麼二級緩存是如何使用的呢?架構

①、開啓二級緩存

和一級緩存默認開啓不同,二級緩存須要咱們手動開啓

首先在全局配置文件 mybatis-configuration.xml 文件中加入以下代碼:

<!--開啓二級緩存  -->
<settings>
	<setting name="cacheEnabled" value="true"/>
</settings>

其次在 UserMapper.xml 文件中開啓緩存

<!-- 開啓二級緩存 -->
<cache></cache>

咱們能夠看到 mapper.xml 文件中就這麼一個空標籤<cache/>,其實這裏能夠配置<cache type="org.apache.ibatis.cache.impl.PerpetualCache"/>,PerpetualCache這個類是mybatis默認實現緩存功能的類。咱們不寫type就使用mybatis默認的緩存,也能夠去實現 Cache 接口來自定義緩存。

咱們能夠看到 二級緩存 底層仍是 HashMap 架構。

②、po 類實現  Serializable  序列化接口

開啓了二級緩存後,還須要將要緩存的pojo實現Serializable接口,爲了將緩存數據取出執行反序列化操做,由於二級緩存數據存儲介質多種多樣,不必定只存在內存中,有可能存在硬盤中,若是咱們要再取這個緩存的話,就須要反序列化了。因此mybatis中的pojo都去實現Serializable接口。

③、測試

1、測試二級緩存和sqlSession 無關

@Test
public void testTwoCache(){
	//根據 sqlSessionFactory 產生 session
	SqlSession sqlSession1 = sessionFactory.openSession();
	SqlSession sqlSession2 = sessionFactory.openSession();
	
	String statement = "com.ys.twocache.UserMapper.selectUserByUserId";
	UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
	UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
	//第一次查詢,發出sql語句,並將查詢的結果放入緩存中
	User u1 = userMapper1.selectUserByUserId(1);
	System.out.println(u1);
	sqlSession1.close();//第一次查詢完後關閉sqlSession
	
	//第二次查詢,即便sqlSession1已經關閉了,此次查詢依然不發出sql語句
	User u2 = userMapper2.selectUserByUserId(1);
	System.out.println(u2);
	sqlSession2.close();
}

能夠看出上面兩個不一樣的sqlSession,第一個關閉了,第二次查詢依然不發出sql查詢語句。

2、測試執行 commit() 操做,二級緩存數據清空

@Test
public void testTwoCache(){
	//根據 sqlSessionFactory 產生 session
	SqlSession sqlSession1 = sessionFactory.openSession();
	SqlSession sqlSession2 = sessionFactory.openSession();
	SqlSession sqlSession3 = sessionFactory.openSession();
	
	String statement = "com.ys.twocache.UserMapper.selectUserByUserId";
	UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
	UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
	UserMapper userMapper3 = sqlSession2.getMapper(UserMapper.class);
	//第一次查詢,發出sql語句,並將查詢的結果放入緩存中
	User u1 = userMapper1.selectUserByUserId(1);
	System.out.println(u1);
	sqlSession1.close();//第一次查詢完後關閉sqlSession
	
	//執行更新操做,commit()
	u1.setUsername("aaa");
	userMapper3.updateUserByUserId(u1);
	sqlSession3.commit();
	
	//第二次查詢,因爲上次更新操做,緩存數據已經清空(防止數據髒讀),這裏必須再次發出sql語句
	User u2 = userMapper2.selectUserByUserId(1);
	System.out.println(u2);
	sqlSession2.close();
}

查看控制檯狀況:

④、useCache和flushCache

mybatis中還能夠配置userCache和flushCache等配置項,userCache是用來設置是否禁用二級緩存的,在statement中設置useCache=false能夠禁用當前select語句的二級緩存,即每次查詢都會發出sql去查詢,默認狀況是true,即該sql使用二級緩存。

<select id="selectUserByUserId" useCache="false" resultType="com.ys.twocache.User" parameterType="int">
	select * from user where id=#{id}
</select>

這種狀況是針對每次查詢都須要最新的數據sql,要設置成useCache=false,禁用二級緩存,直接從數據庫中獲取。

在mapper的同一個namespace中,若是有其它insert、update、delete操做數據後須要刷新緩存,若是不執行刷新緩存會出現髒讀。

設置statement配置中的flushCache=」true」 屬性,默認狀況下爲true,即刷新緩存,若是改爲false則不會刷新。使用緩存時若是手動修改數據庫表中的查詢數據會出現髒讀。

<select id="selectUserByUserId" flushCache="true" useCache="false" resultType="com.ys.twocache.User" parameterType="int">
	select * from user where id=#{id}
</select>

通常下執行完commit操做都須要刷新緩存,flushCache=true表示刷新緩存,這樣能夠避免數據庫髒讀。因此咱們不用設置,默認便可。

歡迎工做一到五年的Java工程師朋友們加入Java高級交流:698581634。羣內提供免費的Java架構學習資料(Spring,MyBatis,Netty源碼分析,高併發、高性能、分佈式、微服務架構的原理,JVM性能優化等...)這些成爲架構師必備的知識體系。 合理利用本身每一分每一秒的時間來學習提高本身,不要再用"沒有時間「來掩飾本身思想上的懶惰!趁年輕,使勁拼,給將來的本身一個交代!

相關文章
相關標籤/搜索