1. 一級緩存和二級緩存
- 在mybatis中依痊,為了節(jié)省資源,會將之前查詢的數據緩存到SqlSession的對象中(一級緩存)怎披;
當再次查詢的時候胸嘁,現在緩存中查詢是否有相同的sql語句,如果有相同的語句钳枕,那么就直接從緩存中讀出數據缴渊。 - 一級緩存有個不足的地方就是,在一個項目中鱼炒,不可能只有一個SqlSession,會有多個SqlSession存在衔沼;
當多個SqlSession訪問一個mapper的一個查詢操作,并且sql語句相同時昔瞧,那么每個SqlSession都會在數據庫中查詢一次指蚁,就比較消耗資源了;
所以二級緩存就來了自晰,二級緩存是基于mapper的凝化,當有SqlSession來調用mapper時,會在SqlSessionFactory緩存中查找與mapper名空間對應的緩存區(qū)酬荞,查看緩存中是否有這個SqlSession要使用的sql語句相同的搓劫,如果有就直接從緩存中讀取瞧哟。 - 但是一級緩存和二級緩存在執(zhí)行了commit操作后,也就是增刪改的提交操作后枪向,會將緩存刷新勤揩;
當一個SqlSession被關閉后秘蛔,才會將這個SqlSession的一級緩存刷入二級緩存中。
2. 一級緩存的案例
因為一級緩存是默認的深员,我們只需要在mybatis中用同一條sql兩次查詢,就可以通過跟進日志了解一級緩存的執(zhí)行信息倦畅。
下面是測試的部分代碼遮糖,即用同一條sql執(zhí)行兩次查詢,用不同的對象名來獲取返回值:
//綁定
TeacherDao td = sqlSession.getMapper(TeacherDao.class);
//一級緩存滔迈,基于mapper
Teacher t= td.findTeacherByTname("htc");
System.out.println("t:"+t);
Teacher tt = td.findTeacherByTname("htc");
System.out.println("tt:"+tt);
控制臺打印信息:
Created connection 559670971.
==> Preparing: select t.t_id t_id,t.t_name t_name from teacher t where t.t_name =?
==> Parameters: htc(String)
<== Columns: t_id, t_name
<== Row: 1, htc
<== Total: 1
Cache Hit Ratio [demo.cyj.dao.TeacherDao]: 0.0
==> Preparing: select s.id id,s.stu_name stu_name,s.stu_age age,s.t_id t_id from student s left join teacher t on t.t_id = s.t_id where t.t_name=?
==> Parameters: htc(String)
<== Columns: id, stu_name, age, t_id
<== Row: 8, 黑拐, 43, 1
<== Row: 9, 拐, 50, 1
<== Row: 10, 拐棍, 50, 1
<== Row: 11, 大黑拐, 30, 1
<== Row: 12, 小黑拐, 12, 1
<== Row: 13, 拐哥, 29, 1
<== Row: 14, 胡黑拐, 123, 1
<== Total: 7
t:demo.cyj.pojo.Teacher_$$_jvst65e_0@5ae63ade
Cache Hit Ratio [demo.cyj.dao.TeacherDao]: 0.0
tt:demo.cyj.pojo.Teacher_$$_jvst65e_0@5ae63ade
- 可以看到只創(chuàng)建了一次connection燎悍;
- 并且只sql語句沒有重復(控制臺信息中之所以有兩條sql敬惦,是因為我在之前配置過懶加載,什么是懶加載谈山,為什么會有兩條sql俄删,可以點擊并參考這篇文章);
- 對象 t 和 tt 打印出來的hashcode也都是5ae63ade畴椰,也就說他們是相同的存儲地址鸽粉,的確是從緩存里拿出來的
3. 二級緩存案例
一級緩存是默認開啟的,但二級緩存需要手動開啟触机,所以會稍微做一些配置。配置如下:
在mybatis的總配置文件config.xml
中的<settings>
下添加一句
<settings>
<!-- 開啟二級緩存 -->
<setting name="cacheEnabled" value="true"></setting>
</settings>
然后在需要用到二級緩存的mapper文件下的<mapper>
標簽下添加一句
<cache/>
或
<cache><cache/>
還有一個初次接觸這個的人容易踩的一個坑就是片任,每個用到二級緩存的類都要序列化一次,也就是要實現serializable
接口对供, 點擊查看這個坑長什么樣氛濒,建議不管用不用序列化鹅髓,也將每個實體類都序列化了京景。
好了,配置做完了醋粟,就是測試了:
public class Test {
public static void main(String[] args) throws IOException {
String src = "config.xml";
InputStream inputStream = Resources.getResourceAsStream(src);
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//第一次查詢
SqlSession sqlSession = sessionFactory.openSession(true);
TeacherDao td = sqlSession.getMapper(TeacherDao.class);
Teacher t= td.findTeacherByTname("htc");
System.out.println("t:"+t);
sqlSession.close();
//第二次查詢
SqlSession sqlSession2 = sessionFactory.openSession(true);
TeacherDao td2 = sqlSession2.getMapper(TeacherDao.class);
Teacher t2 = td2.findTeacherByTname("htc");
System.out.println("t2:"+t2);
sqlSession2.close();
}
}
運行后重归,控制臺打印日志:
Created connection 559670971.
==> Preparing: select t.t_id t_id,t.t_name t_name from teacher t where t.t_name =?
==> Parameters: htc(String)
<== Columns: t_id, t_name
<== Row: 1, htc
<== Total: 1
Cache Hit Ratio [demo.cyj.dao.TeacherDao]: 0.0
==> Preparing: select s.id id,s.stu_name stu_name,s.stu_age age,s.t_id t_id from student s left join teacher t on t.t_id = s.t_id where t.t_name=?
==> Parameters: htc(String)
<== Columns: id, stu_name, age, t_id
<== Row: 8, 黑拐, 43, 1
<== Row: 9, 拐, 50, 1
<== Row: 10, 拐棍, 50, 1
<== Row: 11, 大黑拐, 30, 1
<== Row: 12, 小黑拐, 12, 1
<== Row: 13, 拐哥, 29, 1
<== Row: 14, 胡黑拐, 123, 1
<== Total: 7
t:demo.cyj.pojo.Teacher_$$_jvst65e_0@5ae63ade
Closing JDBC Connection [com.mysql.jdbc.Connection@215be6bb]
Returned connection 559670971 to pool.
Cache Hit Ratio [demo.cyj.dao.TeacherDao]: 0.3333333333333333
t2:demo.cyj.pojo.Teacher@26aa12dd
- 可以看到只create了一次connection鼻吮,sql語句也沒有重復,(為什么有兩條sql參考 2.一級緩存 中的解釋)椎木,但是hashcode有變化應該是從sqlSession關閉后,將數據刷到二級緩存中導致的香椎,但是也的確只做了一次查詢。
總結
mybatis中關于一二級緩存的代碼已經封裝的很好了馍惹,我們要用的話需要做的并不多玛界,設置起來也很簡單,唯一的細節(jié)之處就是關于序列化的問題