Mybatis一級、二級緩衝,mybatis二級緩衝
一級緩衝
首先做一個測試,建立一個mapper設定檔和mapper介面,我這裡用了最簡單的查詢來示範。
<mapper namespace="cn.elinzhou.mybatisTest.mapper.UserMapper"> <select id="findUsers" resultType="cn.elinzhou.mybatisTest.pojo.User"> SELECT * FROM user </select></mapper>
public interface UserMapper { List<User> findUsers()throws Exception;}
然後編寫一個單元測試
public class UserMapperTest { SqlSession sqlSession = null; @Before public void setUp() throws Exception { // 通過設定檔擷取資料庫連接資訊 Reader reader = Resources.getResourceAsReader("cn/elinzhou/mybatisTest/config/mybatis.xml"); // 通過配置資訊構建一個SqlSessionFactory SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); // 通過sqlSessionFactory開啟一個資料庫會話 sqlSession = sqlSessionFactory.openSession(); } @Test public void testFindUsers() throws Exception { UserMapper userMapper = sqlSession.getMapper(UserMapper.class); List<User> users = userMapper.findUsers(); System.out.println(users); }}
運行,可以看到控制台輸出(先配好log4j)為類似如日誌
日誌說明了該操作執行的sql語句已經查詢的內容,最後一行是我手動通過System.out.printf輸出的結果。
然後再加一條語句
users = userMapper.findUsers();
之前的單元測試就變成了這個樣子
也就是在執行完userMapper.findUsers();後立刻再執行一遍userMapper.findUsers(); 可以想象,其實這兩個操作執行的sql是完全相同的,而且在這期間沒有對資料庫進行過其他動作。然後執行該單元測試,發現效果跟上面執行一條的時候完全相同,也就是執行第二次userMapper.findUsers();操作的時候沒有對資料庫進行查詢,那麼得到的資料是從哪裡來的?答案是一級緩衝。
mybatis一級緩衝是指在記憶體中開闢一塊地區,用來儲存使用者對資料庫的操作資訊(sql)和資料庫返回的資料,如果下一次使用者再執行相同的請求,那麼直接從記憶體中讀數資料而不是從資料庫讀取。
其中資料的生命週期有兩個影響因素。
對sqlsession執行commit操作,也就意味著使用者執行了update、delete等操作,那麼資料庫中的資料勢必會發生變化,如果使用者請求資料仍然使用之前記憶體中的資料,那麼將讀到髒資料。所以在執行sqlsession操作後,會清除儲存資料的HashMap,使用者在發起查詢請求時就會重新讀取資料並放入一級緩衝中了。
上述測試就是在第一查詢完後執行了commit操作,再進行查詢。與之前的測試不同的是,這次測試控制台列印了兩組查詢結果,說明在commit之後mybatis對資料重新進行了查詢。
一般在mybatis整合spring時,會把SqlSessionFactory設定為單例注入到IOC容器中,不把sqlsession也設定為單例的原因是sqlsession是線程不安全的,所以不能為單例。那也就意味著其實是有關閉sqlsession的過程的。其實,對於每一個service中的sqlsession是不同的,這是通過mybatis-spring中的org.mybatis.spring.mapper.MapperScannerConfigurer建立sqlsession自動注入到service中的。
而一級緩衝的設計是每個sqlsession單獨使用一個緩衝空間,不同的sqlsession是不能互相訪問資料的。當然,在sqlsession關閉後,其中資料自然被清空。
二級緩衝
在使用二級緩衝之前,先測試之前提到過的關閉sqlsession後會清空緩衝的問題,把junit代碼修改一下
@Test public void testFindUsers() throws Exception { UserMapper userMapper = sqlSession.getMapper(UserMapper.class); List<User> users = userMapper.findUsers(); //關閉sqlsession sqlSession.close(); //通過sqlsessionFactroy建立一個新的sqlsession sqlSession = sqlSessionFactory.openSession(); //擷取mapper對象 userMapper = sqlSession.getMapper(UserMapper.class); users = userMapper.findUsers(); System.out.println(users); }
這段代碼在第一次查詢完後關閉sqlsession,然後建立新的sqlsession和mapper來重新執行一次查詢操作,可以預見,執行結果
說明關閉了sqlsession後的確把之前的快取資料清空了,之後再執行同樣的查詢操作也會再訪問一遍資料庫。為瞭解決這個問題,需要使用二級緩衝
一級緩衝的範圍僅限於一個sqlsession,但是二級緩衝的範圍是一個namespace。但並不是意味著同一個namespace建立的mapper可以互相讀取緩衝內容,這裡的原則是,如果開啟了二級緩衝,那麼在關閉sqlsession後,會把該sqlsession一級緩衝中的資料添加到namespace的二級緩衝中。
接下測試,先需要開啟二級緩衝。
1.開啟二級緩衝總開關
開啟總開關,只需要在mybatis總設定檔中加入一行設定
<settings> <!--開啟二級緩衝--> <setting name="cacheEnabled" value="true"/></settings>
2.開啟需要使用二級緩衝的mapper的開關
在需要開啟二級緩衝的mapper.xml中加入caceh標籤
<cache/>
3.POJO序列化
讓需要使用二級緩衝的POJO類實現Serializable介面,如
public class User implements Serializable {
通過之前三步操作就可以使用二級緩衝了,接下來測試。添加一個Junit方法
@Test public void testFindUsersCache() throws Exception { UserMapper userMapper = sqlSession.getMapper(UserMapper.class); List<User> users = userMapper.findUsers(); //關閉sqlsession sqlSession.close(); //通過sqlsessionFactroy建立一個新的sqlsession sqlSession = sqlSessionFactory.openSession(); //擷取mapper對象 userMapper = sqlSession.getMapper(UserMapper.class); users = userMapper.findUsers(); System.out.println(users); }
執行後可以發現,控制台值輸出了一次查詢過程,也可以證明二級緩衝開啟成功。
還有一個問題,之前說了,即使開啟了二級緩衝,不同的sqlsession之間的快取資料也不是想互訪就能互訪的,必須等到sqlsession關閉了以後,才會把其一級緩衝中的資料寫入二級緩衝。為了測試這個,把上述代碼中的
sqlSession.close();
注釋,那麼之前的代碼就變成了
再執行,發現控制太又輸出了兩次的查詢過程,所以可以印證,只有關閉了sqlsession之後,才會把其中一級快取資料寫入二級緩衝。
緩衝配置
在預設情況下,當sqlsession執行commit後會重新整理緩衝,但是也可以強制設定為不重新整理,在不需要重新整理的標籤中加入
flushCache="false"
如
<select id="findUsers" resultType="cn.elinzhou.mybatisTest.pojo.User" flushCache="false">
那麼,無論是否執行commit,緩衝都不會重新整理了。但是這樣會造成髒讀,只有在特殊情況下才使用
有些情況下,需要設定自動重新整理緩衝,那麼需要配置對應mapper中的cache標籤。
flushInterval="10000"
該屬性工作表示每隔10秒鐘自動重新整理一遍緩衝
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。