一、MyBatis 緩存中的常用概念
MyBatis 緩存:它用來優化 SQL 資料庫查詢的,但是可能會産生髒資料。
SqlSession:代表和資料庫的一次會話,向使用者提供了操作資料庫的方法。
MappedStatement:代表要發往資料庫執行的指令,可以了解為是 SQL 的抽象表示。
Executor: 代表用來和資料庫互動的執行器,接受 MappedStatment 作為參數。
namespace:每個 Mapper 檔案隻能配置一個 namespace,用來做 Mapper 檔案級别的緩存共享。
映射接口:定義了一個接口,然後裡面的接口方法對應要執行 SQL 的操作,具體要執行的 SQL 語句是寫在映射檔案中。
映射檔案:MyBatis 編寫的 XML 檔案,裡面有一個或多個 SQL 語句,不同的語句用來映射不同的接口方法。通常來說,每一張單表都對應着一個映射檔案。
二、MyBatis 一級緩存
2.1 一級緩存原理
在一次 SqlSession 中(資料庫會話),程式執行多次查詢,且查詢條件完全相同,多次查詢之間程式沒有其他增删改操作,則第二次及後面的查詢可以從緩存中擷取資料,避免走資料庫。
每個SqlSession中持有了Executor,每個Executor中有一個LocalCache。當使用者發起查詢時,MyBatis根據目前執行的語句生成
MappedStatement
,在Local Cache進行查詢,如果緩存命中的話,直接傳回結果給使用者,如果緩存沒有命中的話,查詢資料庫,結果寫入
Local Cache
,最後傳回結果給使用者。
Local Cache 其實是一個 hashmap 的結構:
private Map<Object, Object> cache = new HashMap<Object, Object>();
如下圖所示,有兩個 SqlSession,分别為 SqlSession1 和 SqlSession2,每個 SqlSession 中都有自己的緩存,緩存是 hashmap 結構,存放的鍵值對。
鍵是 SQL 語句組成的 Key :
Statement Id + Offset + Limmit + Sql + Params
值是 SQL 查詢的結果:
2.2 一級緩存配置
在 mybatis-config.xml 檔案配置,
name=localCacheScope
,value有兩種值:
SESSION
和
STATEMENT
<configuration>
<settings>
<setting name="localCacheScope" value="SESSION"/>
</settings>
<configuration>
SESSION:開啟一級緩存功能
STATEMENT:緩存隻對目前執行的這一個 SQL 語句有效,也就是沒有用到一級緩存功能。
首先我們通過幾個考題來體驗下 MyBatis 一級緩存。
2.3 一級緩存考題
考題(1)隻開啟了一級緩存,下面的代碼調用了三次查詢操作 getStudentById,請判斷,下列說法正确的是?
// 打開一個 SqlSession
SqlSession sqlSession = factory.openSession(true);
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
// 根據 id=1 查詢學生資訊
System.out.println(studentMapper.getStudentById(1));
// 根據 id=1 查詢學生資訊
System.out.println(studentMapper.getStudentById(1));
// 根據 id=1 查詢學生資訊
System.out.println(studentMapper.getStudentById(1));
答案:第一次從資料庫查詢到的資料,第二次和第二次從 MyBatis 一級緩存查詢的資料。
解答:第一次從資料庫查詢後,後續查詢走 MyBatis 一級緩存
考題(2)隻開啟了一級緩存,下面代碼示例中,開啟了一個 SqlSession 會話,調用了一次查詢,然後對資料進行了更改,又調用了一次查詢,下列關于兩次查詢的說法,正确的是?
// 打開一個 SqlSession
SqlSession sqlSession = factory.openSession(true);
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
// 根據 id=1 查詢學生資訊
System.out.println(studentMapper.getStudentById(1));
// 插入了一條學生資料,改變了資料庫
System.out.println("增加了" + studentMapper.addStudent(buildStudent()) + "個學生");
// 根據 id=1 查詢學生資訊
System.out.println(studentMapper.getStudentById(1));
sqlSession.close();
答案:第一次從資料庫查詢到的資料,第二次從資料庫查詢的資料
解答:第一次從資料庫查詢後,後續更新(包括增删改)資料庫中的資料後,這條 SQL 語句的緩存失效了,後續查詢需要重新從資料庫擷取資料。
考題(3)當開啟了一級緩存,下面的代碼中,開啟了兩個 SqlSession,第一個 SqlSession 查詢了兩次學生 A 的姓名,第二次 SqlSession 更新了一次學生 A 的姓名,請判斷哪個選項符合最後的查詢結果。
SqlSession sqlSession1 = factory.openSession(true);
SqlSession sqlSession2 = factory.openSession(true);
StudentMapper studentMapper = sqlSession1.getMapper(StudentMapper.class);
StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class); studentMapper2.updateStudentName("B",1);
System.out.println(studentMapper.getStudentById(1));
System.out.println(studentMapper2.getStudentById(1));
答案:
A
B
解答:隻開啟一級緩存的情況下,SqlSession 級别是不共享的。代碼示例中,分别建立了兩個 SqlSession,在第一個 SqlSession 中查詢學生 A 的姓名,第二個 SqlSession 中修改了學生 A 的姓名為 B,SqlSession2 更新了資料後,不會影響 SqlSession1,是以 SqlSession1 查到的資料還是 A。
2.4 MyBatis 一級緩存失效的場景
- 不同的SqlSession對應不同的一級緩存
- 同一個SqlSession但是查詢條件不同
- 同一個SqlSession兩次查詢期間執行了任何一次增删改操作
- 同一個SqlSession兩次查詢期間手動清空了緩存