Java Mybatis一級(jí)緩存和二級(jí)緩存
一、什么是緩存
緩存是內(nèi)存當(dāng)中一塊存儲(chǔ)數(shù)據(jù)的區(qū)域,目的是提高查詢(xún)效率。MyBatis會(huì)將查詢(xún)結(jié)果存儲(chǔ)在緩存當(dāng)中,當(dāng)下次執(zhí)行相同的SQL時(shí)不訪問(wèn)數(shù)據(jù)庫(kù),而是直接從緩存中獲取結(jié)果,從而減少服務(wù)器的壓力。
什么是緩存?
存在于內(nèi)存中的一塊數(shù)據(jù)。
緩存有什么作用?
減少程序和數(shù)據(jù)庫(kù)的交互,提高查詢(xún)效率,降低服務(wù)器和數(shù)據(jù)庫(kù)的壓力。
什么樣的數(shù)據(jù)使用緩存?
經(jīng)常查詢(xún)但不常改變的,改變后對(duì)結(jié)果影響不大的數(shù)據(jù)。
MyBatis緩存分為哪幾類(lèi)?
一級(jí)緩存和二級(jí)緩存
如何判斷兩次Sql是相同的?
查詢(xún)的Sql語(yǔ)句相同 傳遞的參數(shù)值相同 對(duì)結(jié)果集的要求相同 預(yù)編譯的模板Id相同
二、Mabtis一級(jí)緩存
MyBatis一級(jí)緩存也叫本地緩存。SqlSession對(duì)象中包含一個(gè)Executor對(duì)象,Executor對(duì)象中包含一個(gè)PerpetualCache對(duì)象,在該對(duì)象存放一級(jí)緩存數(shù)據(jù)。
由于一級(jí)緩存是在SqlSession對(duì)象中,所以只有使用同一個(gè)SqlSession對(duì)象操作數(shù)據(jù)庫(kù)時(shí)才能共享一級(jí)緩存。
MyBatis的一級(jí)緩存是默認(rèn)開(kāi)啟的,不需要任何的配置。
如下圖所示:

(1)測(cè)試一級(jí)緩存
其實(shí)測(cè)試方法很簡(jiǎn)單,就是通過(guò)使用相同和不同的SqlSession對(duì)象進(jìn)行SQL查詢(xún),返回的對(duì)象的哈希值是否一樣就可以知道了,如果返回的哈希值一樣說(shuō)明沒(méi)有進(jìn)行SQL查詢(xún),而是直接從緩存拿到對(duì)象來(lái)返回
下面是使用相同的Session對(duì)象來(lái)執(zhí)行查詢(xún),如果觀察user1和user2的哈希值一樣則說(shuō)明確實(shí)開(kāi)啟了一級(jí)緩存,并沒(méi)有進(jìn)行查詢(xún),而是直接從緩存中拿數(shù)據(jù)。
import com.mybatisstudy.mapper.UserMapper;
import com.mybatisstudy.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import java.io.InputStream;
public class TestUserMapper3 {
// 測(cè)試使用同一個(gè)SqlSession查詢(xún)
@Test
public void testCache1() throws Exception{
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(is);
SqlSession session = factory.openSession();
// 使用同一個(gè)SqlSession查詢(xún)
UserMapper mapper1 = session.getMapper(UserMapper.class);
UserMapper mapper2 = session.getMapper(UserMapper.class);
User user1 = mapper1.findById(1);
System.out.println(user1.hashCode());
System.out.println("------------------------------------------");
User user2 = mapper2.findById(1);
System.out.println(user2.hashCode());
session.close();
}
}執(zhí)行結(jié)果

OK,確實(shí)返回的哈希值都是一樣的,并且我們可以通過(guò)控制臺(tái)輸出顯示它并沒(méi)有進(jìn)行查詢(xún)而是直接從緩存中拿到對(duì)象并返回,所以這就是一級(jí)緩存 ,提高了查詢(xún)效率。
下面使用不同的SqlSession來(lái)進(jìn)行測(cè)試一下,返回的哈希值是否一致
// 測(cè)試使用不同SqlSession查詢(xún)
@Test
public void testCache2() throws Exception{
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(is);
SqlSession session1 = factory.openSession();
SqlSession session2 = factory.openSession();
// 測(cè)試使用不同SqlSession查詢(xún)
UserMapper mapper1 = session1.getMapper(UserMapper.class);
UserMapper mapper2 = session2.getMapper(UserMapper.class);
User user1 = mapper1.findById(1);
System.out.println(user1.hashCode());
System.out.println("---------------------------------");
User user2 = mapper2.findById(1);
System.out.println(user2.hashCode());
session1.close();
session2.close();
}運(yùn)行結(jié)果

OK,可以看到,返回的哈希值不一樣,并且從控制臺(tái)輸出顯示也可以看到這里的確也進(jìn)行了一次查詢(xún),因此可以證實(shí),共享一級(jí)緩存確實(shí)是基于SqlSession對(duì)象的
(2)清空一級(jí)緩存
但是吧,如果緩存過(guò)多的話,也是會(huì)影響我們的查詢(xún)效率的,所以這時(shí)候就需要清空緩存了,就像我們時(shí)不時(shí)要清理一下手機(jī)緩存否則就會(huì)很卡,是同樣的道理,那怎么清空一級(jí)緩存呢?
進(jìn)行以下操作可以清空MyBatis一級(jí)緩存:
- SqlSession 調(diào)用 close() :操作后SqlSession對(duì)象不可用,該對(duì)象的緩存數(shù)據(jù)也不可用。
- SqlSession 調(diào)用 clearCache() / commit() :操作會(huì)清空一級(jí)緩存數(shù)據(jù)。
- SqlSession 調(diào)用增刪改方法:操作會(huì)清空一級(jí)緩存數(shù)據(jù),因?yàn)樵鰟h改后數(shù)據(jù)庫(kù)發(fā)生改變,緩存數(shù)據(jù)將不準(zhǔn)確
// 清空Mybatis一級(jí)緩存
@Test
public void testCache3() throws Exception{
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(is);
SqlSession session = factory.openSession();
UserMapper mapper1 = session.getMapper(UserMapper.class);
UserMapper mapper2 = session.getMapper(UserMapper.class);
User user1 = mapper1.findById(1);
System.out.println(user1.hashCode());
// 清空Mybatis一級(jí)緩存
session.clearCache();
System.out.println("-------------------------------");
User user2 = mapper2.findById(1);
System.out.println(user2.hashCode());
session.close();
}執(zhí)行效果

OK,返回的哈希值也確實(shí)不一樣, 但是我們有沒(méi)有觀察到這和上面使用不同的SqlSession對(duì)象來(lái)執(zhí)行查詢(xún)的時(shí)候,控制臺(tái)輸入顯示有點(diǎn)不一樣,那就是這里不用再建立JDBC連接,也有效了提高查詢(xún)效率,所以我們偶爾還是要清空一下緩存才行
三、Mybatis二級(jí)緩存
MyBatis二級(jí)緩存也叫全局緩存。數(shù)據(jù)存放在SqlSessionFactory中,只要是同一個(gè)工廠對(duì)象創(chuàng)建的SqlSession,在進(jìn)行查詢(xún)時(shí)都能共享數(shù)據(jù)。一般在項(xiàng)目中只有一個(gè)SqlSessionFactory對(duì)象,所以二級(jí)緩存的數(shù)據(jù)是全項(xiàng)目共享的。
MyBatis一級(jí)緩存存放的是對(duì)象,二級(jí)緩存存放的是對(duì)象的數(shù)據(jù)。所以要求二級(jí)緩存存放的POJO必須是可序列化的,也就是要實(shí)現(xiàn)Serializable接口。
MyBatis二級(jí)緩存默認(rèn)不開(kāi)啟,手動(dòng)開(kāi)啟后數(shù)據(jù)先存放在一級(jí)緩存中,只有一級(jí)緩存數(shù)據(jù)清空后,數(shù)據(jù)才會(huì)存到二級(jí)緩存中。
SqlSession 調(diào)用 clearCache() 無(wú)法將數(shù)據(jù)存到二級(jí)緩存中。
(1)開(kāi)啟二級(jí)緩存
1. POJO類(lèi)實(shí)現(xiàn)Serializable接口
import java.io.Serializable;
public class User implements Serializable {
private int id;
private String username;
private String sex;
private String address;
}2. 在Mybatis配置文件添加如下設(shè)置
<!-- 二級(jí)緩存打開(kāi) --> <settings> <setting name="cacheEnabled" value="true"/> </settings>
這里有個(gè)額外知識(shí),就是Mybatis配置文件的標(biāo)簽還得按照順序來(lái)放的,否則就會(huì)以下編譯錯(cuò)誤;
The content of element type "configuration" must match "(properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,
objectWrapperFactory?,reflectorFactory?,plugins?,environments?,
databaseIdProvider?,mappers?)".
同時(shí)也說(shuō)明了放置順序就得按照match里面的順序來(lái)放
3. 添加 <cache /> 標(biāo)簽
如果查詢(xún)到的集合中對(duì)象過(guò)多,二級(jí)緩存只能緩存1024個(gè)對(duì)象引用??梢酝ㄟ^(guò)
<cache /> 標(biāo)簽的size屬性修改該數(shù)量。
比如:<cache size="2048"/>
(2)測(cè)試二級(jí)緩存
那怎么測(cè)試呢,從上面我們可以知道二級(jí)緩存存放的是對(duì)象的數(shù)據(jù),并且是基于SqlSessionFactory的,因此我們可以用SqlSessionFactory獲取兩個(gè)SqlSession對(duì)象,然后讓他們分別獲取各自的mapper,然后進(jìn)行查詢(xún),返回到同一個(gè)實(shí)例化的USer對(duì)象中,如果返回的數(shù)據(jù)是一致的,但是對(duì)象的哈希值是不一樣的話,則說(shuō)明二級(jí)緩存里存放的確實(shí)對(duì)象的數(shù)據(jù)而不是對(duì)象。
// 測(cè)試二級(jí)緩存
@Test
public void testCache4() throws Exception {
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(is);
SqlSession session = factory.openSession();
UserMapper mapper1 = session.getMapper(UserMapper.class);
UserMapper mapper2 = session.getMapper(UserMapper.class);
User user1 = mapper1.findById(1);
System.out.println(user1);
System.out.println(user1.hashCode());
// 讓一級(jí)緩存失效
session.commit();
System.out.println("----------------------------");
user1 = mapper2.findById(1);
System.out.println(user1);
System.out.println(user1.hashCode());
}
運(yùn)行結(jié)果

OK,從運(yùn)行結(jié)果上我們可以知道結(jié)果集返回到同一個(gè)對(duì)象中,而他們的哈希值反而不一樣,說(shuō)明執(zhí)行第二次查詢(xún)的時(shí)候新建了一個(gè)對(duì)象并且該對(duì)象指向那個(gè)對(duì)象并且將SqlSessionFactory中的數(shù)據(jù)賦值到新建的那個(gè)對(duì)象。其實(shí)從控制臺(tái)打印的日志我們也可以得出,并沒(méi)有執(zhí)行查詢(xún)方法,因?yàn)闆](méi)有打印SQL語(yǔ)句,而且緩存也是從0.0改成了0.5,因此我們可以斷定二級(jí)緩存存放的是數(shù)據(jù)而不是對(duì)象。
到此這篇關(guān)于Java Mybatis一級(jí)緩存和二級(jí)緩存的文章就介紹到這了,更多相關(guān)Mybatis一級(jí)緩存和二級(jí)緩存內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 一文搞懂MyBatis一級(jí)緩存和二級(jí)緩存
- MyBatis一級(jí)緩存與二級(jí)緩存原理與作用分析
- mybatis一級(jí)緩存和二級(jí)緩存的區(qū)別及說(shuō)明
- Mybatis詳細(xì)對(duì)比一級(jí)緩存與二級(jí)緩存
- Mybatis?一級(jí)緩存和二級(jí)緩存原理區(qū)別
- Mybatis的一級(jí)緩存和二級(jí)緩存原理分析與使用
- 關(guān)于mybatis的一級(jí)緩存和二級(jí)緩存的那些事兒
- Mybatis 一級(jí)緩存與二級(jí)緩存的實(shí)現(xiàn)
- MyBatis 延遲加載、一級(jí)緩存、二級(jí)緩存(詳解)
- MyBatis中一級(jí)緩存和二級(jí)緩存的區(qū)別
相關(guān)文章
java設(shè)計(jì)模式-單例模式實(shí)現(xiàn)方法詳解
單例模式,屬于創(chuàng)建類(lèi)型的一種常用的軟件設(shè)計(jì)模式。通過(guò)單例模式的方法創(chuàng)建的類(lèi)在當(dāng)前進(jìn)程中只有一個(gè)實(shí)例(根據(jù)需要,也有可能一個(gè)線程中屬于單例2021-07-07
Spring Boot整合Spring Data Jpa代碼實(shí)例
這篇文章主要介紹了Spring Boot整合Spring Data Jpa代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11
基于Redisson實(shí)現(xiàn)注解式分布式鎖的示例代碼
這篇文章主要為大家詳細(xì)介紹了如何基于Redisson實(shí)現(xiàn)注解式分布式鎖,文中的示例代碼講解詳細(xì),具有一定的參考價(jià)值,需要的可以了解一下2023-07-07
Java 對(duì)HashMap進(jìn)行排序的三種常見(jiàn)方法
這篇文章主要介紹了Java 對(duì)HashMap進(jìn)行排序的三種常見(jiàn)方法,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下2020-10-10
SpringBoot中實(shí)現(xiàn)訂單30分鐘自動(dòng)取消的三種方案分享
在電商和其他涉及到在線支付的應(yīng)用中,通常需要實(shí)現(xiàn)一個(gè)功能:如果用戶(hù)在生成訂單后的一定時(shí)間內(nèi)未完成支付,系統(tǒng)將自動(dòng)取消該訂單,本文將詳細(xì)介紹基于Spring Boot框架實(shí)現(xiàn)訂單30分鐘內(nèi)未支付自動(dòng)取消的幾種方案,并提供實(shí)例代碼,需要的朋友可以參考下2023-10-10
Spring?Boot項(xiàng)目中使用OpenAI-Java的示例詳解
Spring?Boot是由Pivotal團(tuán)隊(duì)提供的全新框架,其設(shè)計(jì)目的是用來(lái)簡(jiǎn)化新Spring應(yīng)用的初始搭建以及開(kāi)發(fā)過(guò)程,這篇文章主要介紹了Spring?Boot項(xiàng)目中使用OpenAI-Java的示例詳解,需要的朋友可以參考下2023-04-04
SpringMVC響應(yīng)視圖和結(jié)果視圖詳解
這篇文章主要介紹了SpringMVC響應(yīng)視圖和結(jié)果視圖,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09

