
一、什么是緩存?
緩存,合理使用緩存是優(yōu)化中最常見的,將從數(shù)據(jù)庫中查詢出來的數(shù)據(jù)放入緩存中,下次使用時不必從數(shù)據(jù)庫查詢,而是直接從緩存中讀取,避免頻繁操作數(shù)據(jù)庫,減輕數(shù)據(jù)庫的壓力,同時提高系統(tǒng)性能。

一級緩存:是 SQlSession 級別的緩存。在操作數(shù)據(jù)庫時需要構(gòu)造 SqlSession 對象,在對象中有一個數(shù)據(jù)結(jié)構(gòu)(HashMap)用于存儲緩存數(shù)據(jù)。不同的 SqlSession 之間的緩存數(shù)據(jù)區(qū)域(HashMap)是互相不影響的。
二級緩存:是 mapper 級別的緩存,多個 SqlSession 去操作同一個mapper的sql語句,多個 SqlSession 可以共用二級緩存,二級緩存是跨 SqlSession 的。
二、具體介紹
1.使用一級緩存
我們先看一個使用緩存例子:這里我們是根據(jù)傳入的 id 獲取 Employee 對象的值,我們先使用同一個 SqlSession 對象,并且查詢 id 相同的對象
@Test
public void testFirstCache() throws IOException {
SqlSessionFactory sqlSessionFactory = Utils.getSqlSessionFactoty();
SqlSession openSession = sqlSessionFactory.openSession();
try {
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Employee employee = mapper.getEmployee(1);
System.out.println(employee);
Employee employee2 = mapper.getEmployee(1);
System.out.println(employee2);
System.out.println(employee==employee2);
} finally {
openSession.close();
}
}
他的運行結(jié)果是:
從結(jié)果中可以看出,第一次查詢結(jié)束之后,就把 id 為 1 的數(shù)據(jù)放入到緩存中(本質(zhì)上是放入 Map 對象中),第二次如果還是使用相同的 SqlSession 對象,則先會去一級緩存中找是否有 id 為 1 的員工的信息(Map 對象中是否有該對象),如果有,則直接從緩存中取出員工信息,如果沒有,則會去數(shù)據(jù)庫中查詢相關(guān)信息
我們再來看一個一級緩存失效了的例子:假設(shè)這個時候我們查詢的不是同一個 id,我們看看結(jié)果是怎樣。在上面的代碼中,我們將 Employee employee2 = mapper.getEmployee(1) 改為 Employee employee2 = mapper.getEmployee(2),即不是查詢同一個 id,結(jié)果如下:

一級緩存失效的情況
- SqlSession 對象不同, 導(dǎo)致每次使用的都是新的 SqlSession 對象
- SqlSession 相同, 查詢條件不同, 此時一級緩存中沒有數(shù)據(jù), 因為兩次查詢的內(nèi)容不一樣
- SqlSession 相同, 兩次查詢直接執(zhí)行了增刪改查操作, 因為這次操作可能會對當(dāng)前數(shù)據(jù)庫有影響
- SqlSession 相同, 手動清除了一級緩存(
openSession.clearCache())
這些情況就不一一舉例了,以后用到關(guān)注一下即可
2. 使用二級緩存
EmployeeMapper 有一個二級緩存區(qū)域(按 namespace 分),其它 Mapper 也有自己的二級緩存區(qū)域(按 namespace 分)。每一個 namespace 的 mapper 都有一個二級緩存區(qū)域,兩個 mapper 的 namespace 如果相同,這兩個 mapper 執(zhí)行 sql 查詢到數(shù)據(jù)將存在相同的二級緩存區(qū)域中。

2.1 工作機(jī)制
- 一個會話, 查詢一條數(shù)據(jù), 這個數(shù)據(jù)就會被保存在當(dāng)前會話的一次緩存(SqlSession)中
- 如果關(guān)閉會話, 那么一次緩存中的數(shù)據(jù)就會被保存到二級緩存(namespace)中,新的會話查詢的內(nèi)容, 就可以參照二級緩存
- 一個 xxxMapper 對應(yīng)一個 namespace, 不同的 namespace 查出的數(shù)據(jù)會保存在自己對應(yīng)的二級緩存中
-
查出的數(shù)據(jù)會先被保存在一級緩存中, 只有會話關(guān)閉或者提交之后, 以及緩存中的數(shù)據(jù)才會被轉(zhuǎn)移到二級緩存
2.2 開啟二級緩存步驟
- 開啟全局二級緩存配置
- 去 mapper.xml 中配置使用二級緩存
- 我們的 POJO 需要實現(xiàn)序列化接口
在全局文件中開啟二級緩存
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
在 sql 映射文件中配置使用二級緩存,具體參數(shù)可以去官方文檔查詢
<mapper namespace="edu.just.mybatis.dao.EmployeeMapper">
<cache eviction="FIFO" flushInterval="60000" readOnly="false" size="1024"></cache>
...
</mapper>
實現(xiàn)序列化
public class Employee implements Serializable{
private static final long serialVersionUID = 1L;
private Integer id;
private String lastName;
private String email;
private Integer gender;
private Department department;
...
}
2.3 二級緩存測試
這里我們通過兩個不同的 SqlSession 對象創(chuàng)建兩個 EmployeeMapper,這兩個 EmployeeMapper 屬于同一個 namespace
@Test
public void testSecondCache() throws IOException {
SqlSessionFactory sqlSessionFactory = Utils.getSqlSessionFactoty();
SqlSession openSession = sqlSessionFactory.openSession();
SqlSession openSession2 = sqlSessionFactory.openSession();
try {
//1.創(chuàng)建兩個不同的 EmployeeMapper 對象
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
EmployeeMapper mapper2 = openSession2.getMapper(EmployeeMapper.class);
Employee employee = mapper.getEmployee(1);
openSession.close(); //關(guān)閉第第一個 SqlSession 會話
//2.第二次查詢是從二級緩存中拿到的, 并沒有發(fā)送新的 sql
// 此時是從 EmployeeMapper 的二級緩存中獲取的數(shù)據(jù)
Employee employee2 = mapper2.getEmployee(1);
System.out.println(employee2);
openSession.close();
} finally {
openSession.close();
}
}
結(jié)果如下:

2.4 其他配置
①. cacheEnabled 設(shè)置的是二級緩存,一級緩存也一直可以使用
②. 每個 select 標(biāo)簽都有 useCache 標(biāo)簽,如果設(shè)置 useCache="false",那么一級緩存依舊使用,二級緩存則不會使用
③. 每個增刪改查操作的 flushCache="true", 表示增刪改查操作完成之后, 會自動清除緩存, 此時一級緩存和二級緩存都會被清空