mybatis提供查詢緩存,用於減輕數據壓力,提高數據庫性能。
mybaits提供一級緩存,和二級緩存。
一級緩存是SqlSession級別的緩存。在操作數據庫時需要構造 sqlSession對象,在對象中有一個(內存區域)數據結構(HashMap)用於存儲緩存數據。不同的sqlSession之間的緩存數據區域(HashMap)是互相不影響的。
一級緩存的作用域是同一個SqlSession,在同一個sqlSession中兩次執行相同的sql語句,第一次執行完畢會將數據庫中查詢的數據寫到緩存(內存),第二次會從緩存中獲取數據將不再從數據庫查詢,從而提高查詢效率。當一個sqlSession結束後該sqlSession中的一級緩存也就不存在了。Mybatis默認開啓一級緩存。
二級緩存是mapper級別的緩存,多個SqlSession去操作同一個Mapper的sql語句,多個SqlSession去操作數據庫得到數據會存在二級緩存區域,多個SqlSession可以共用二級緩存,二級緩存是跨SqlSession的。
二級緩存是多個SqlSession共享的,其作用域是mapper的同一個namespace,不同的sqlSession兩次執行相同namespace下的sql語句且向sql中傳遞參數也相同即最終執行相同的sql語句,第一次執行完畢會將數據庫中查詢的數據寫到緩存(內存),第二次會從緩存中獲取數據將不再從數據庫查詢,從而提高查詢效率。Mybatis默認沒有開啓二級緩存需要在setting全局參數中配置開啓二級緩存。
如果緩存中有數據就不用從數據庫中獲取,大大提高系統性能。
一、一級緩存
通過id查詢學生信息
@Test
public void testcache(){
StudentMapper mapper = session.getMapper(StudentMapper.class);
Student stu = mapper.getStuById(1);
System.out.println(stu);
Student stu1 = mapper.getStuById(1);
System.out.println(stu1);
}
打印的日誌信息
第一次查詢的時候發送一條SQL語句到數據庫,第二次查詢沒有去發送SQL語句。
每次查詢的時候都會去緩存裏查看一下,如果命中緩存就直接從緩存中拿出數據,沒有命中緩存就發送SQL語句到數據庫進行查詢
第一次查詢的時候去查詢緩存,緩存沒有數據,發送SQL語句到數據看。
第二次命中緩存,直接從緩存中拿出數據。所以只發送一次SQL到數據庫
如果sqlSession去執行commit操作(執行插入、更新、刪除),清空SqlSession中的一級緩存,這樣做的目的爲了讓緩存中存儲的是最新的信息,避免髒讀。
@Test
public void testcache(){
StudentMapper mapper = session.getMapper(StudentMapper.class);
Student stu = mapper.getStuById(3);
System.out.println(stu);
stu.setName("jerry");
mapper.updateStu(stu);
session.commit();
Student stu1 = mapper.getStuById(3);
System.out.println(stu1);
}
第一次查詢數據後將數據放到緩存裏面,然後執行更新操作後刪除了緩存,所以第二次查詢緩存中沒有數據,發送了一條SQL語句到數據庫
二、二級緩存
二級緩存默認是關閉的,需要手動的開啓
1.全局配置文件
<setting name="cacheEnabled" value="true" />
2.Mpper.xml 文件
<cache eviction="FIFO"
flushInterval="60000"
size="512"
readOnly = "true"/>
元素 | 作用 |
---|---|
eviction | 緩存淘汰策略 |
flushInterval | 自動刷新時間 |
size | 緩存對象引用的數量 |
readOnly | 只讀 |
eviction 元素有四種取值
- LRU:最近最少使用,優先數量長時間未使用的對象
- FIFO:先進先出 按照對象的進入緩存的順序進行先進先淘汰
- SOFT:軟引用
- WEAK:虛引用
@Test
public void testcache2() throws Exception{
Mybatisutils mybatisutils = new Mybatisutils();
SqlSession session,session1,session2,session3;
String resource = "mybatis-conf.xml";
//通過Resource 獲取mybatis 流
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
session = sqlSessionFactory.openSession();
System.out.println("第一次查詢開始");
StudentMapper mapper = session.getMapper(StudentMapper.class);
Student stu = mapper.getStuById(1);
session.close();
System.out.println("第一次查詢結束");
System.out.println("第二次查詢開始");
session1 = sqlSessionFactory.openSession();
StudentMapper mapper1 = session1.getMapper(StudentMapper.class);
Student stu1 = mapper1.getStuById(1);
session1.close();
System.out.println("第二次查詢結束");
session2 = sqlSessionFactory.openSession();
StudentMapper mapper2 = session2.getMapper(StudentMapper.class);
stu1.setName("lihua");
mapper2.updateStu(stu1);
session2.commit();
System.out.println("第三次查詢開始");
session3 = sqlSessionFactory.openSession();
StudentMapper mapper3 = session3.getMapper(StudentMapper.class);
Student stu3 = mapper3.getStuById(1);
System.out.println("第三次查詢結束");
}
第一次查詢完緩存裏有了數據,第二次查詢的時候就直接在緩存裏面拿,然後進行了一個更新,更新操作會清空緩存的數據(爲了避免拿到髒數據),所以第三次就沒有命中緩存。
說一下我在這兒犯的錯誤:
剛開始我自己寫了一個Mybatis 工具類,就是下面這段代碼,返回了一個Sqlsession,getSession() 方法每次新建一個SqlSessionFactory 實例來創建SqlSession,所以二級緩存一直不能命中。
後來通過查閱資料,發現這寫SqlSession 必須是由同一個SqlSessioonFactory 創建的,具體爲什麼我還沒有找到原因,這個後面會更新下。
public SqlSession getsession() throws IOException {
String resource = "mybatis-conf.xml";
//通過Resource 獲取mybatis 流
InputStream inputStream = Resources.getResourceAsStream(resource);
//獲取SqlSessionFactory 實例
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//我們就可以從中獲得 SqlSession 的實例了。
// SqlSession 完全包含了面向數據庫執行 SQL 命令所需的所有方法。
// 你可以通過 SqlSession 實例來直接執行已映射的 SQL 語句。
SqlSession session = sqlSessionFactory.openSession();
return session;
}
}
-
在mapper的同一個namespace中,如果有其它insert、update、delete操作數據後需要刷新緩存,如果不執行刷新緩存會出現髒讀。
-
設置statement配置中的flushCache=‘true’ 屬性,默認情況下爲true即刷新緩存,如果改成false則不會刷新。使用緩存時如果手動修改數據庫表中的查詢數據會出現髒讀
<select id="getStuById" resultType="com.tulun.bean.Student" flushCache="true">
select * from student where id = #{id}
</select>
- 在配置完二級緩存的基礎上某個方法不用二級緩存,則以用useCache參數
<select id="getStuById" resultType="com.tulun.bean.Student" useCache="false">
select * from student where id = #{id}
</select>
總結:一般下執行完commit操作都需要刷新緩存,flushCache=true表示刷新緩存默認情況下爲true,我們不用去設置它,這樣可以避免數據庫髒讀。
二級緩存的侷限性
mybatis二級緩存對細粒度的數據級別的緩存實現不好,對同時緩存較多條數據的緩存,比如如下需求:對商品信息進行緩存,由於商品信息查詢訪問量大,但是要求用戶每次都能查詢最新的商品信息,此時如果使用mybatis的二級緩存就無法實現當一個商品變化時只刷新該商品的緩存信息而不刷新其它商品的信息,因爲mybaits的二級緩存區域以mapper爲單位劃分,當一個商品信息變化會將所有商品信息的緩存數據全部清空。解決此類問題需要在業務層根據需求對數據有針對性緩存。需要使用三級緩存