簡介
- 什麼是緩存?
- 緩存是存在於內存中的臨時數據。
- 爲什麼使用緩存?
- 緩存能夠減少和數據庫的交互次數,提高執行效率。
- 什麼樣的數據適用於緩存,什麼樣的數據不適用於緩存?
- 適用於緩存:
- 經常查詢,並且不經常改變的數據。
- 數據的正確與否對最終結果的影響不大。
- 不適用於緩存:
- 經常改變的數據。
- 數據的正確與否對最終結果的影響非常大。
- 適用於緩存:
- 像大多數的持久化框架一樣,Mybatis 也提供了緩存策略,通過緩存策略來減少數據庫的查詢次數,從而提
高性能。Mybatis 中緩存分爲一級緩存,二級緩存。- 一級緩存
- 當我們執行查詢操作之後,Mybatis 框架會將在數據庫中的查詢的結果自動存儲到 SqlSession 中爲一級緩存劃分的一塊區域中。(該區域的低層結構是 Map集合)
- 當我們再次執行同樣的查詢操作之後,Mybatis 框架會先從 SqlSession 中的一級緩存區域查詢。如果存在數據,就直接拿出來;如果不存在數據,纔會去數據庫中進行查詢。
- 因爲一級緩存區域被設置在 SqlSession 中,所以當 SqlSession 消失時,一級緩存也就消失。
- 二級緩存
- 二級緩存被存儲在 SqlSessionFactory 中,同一個 SqlSessionFactory 創建的 SqlSession 共享二級緩存。
- 二級緩存被存儲在 SqlSessionFactory 中,同一個 SqlSessionFactory 創建的 SqlSession 共享二級緩存。
- 一級緩存
目錄結構
src
- main
- java
- cn.water.dao
- UserDao.java(持久層接口)
- cn.water.domain
- User.java(實體類)
- cn.water.dao
- resources
- cn.water.dao
- UserDao.xml(映射配置文件)
- SqlMapConfig.xml(MyBatis主配置文件)
- jdbcConfig.properties(數據庫連接信息文件)
- cn.water.dao
- java
- test
- java.cn.water
- MybatisTest.java(測試類)
- java.cn.water
MyBatis配置文件
jdbcConfig.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=root
SqlMapConfig.xml
- 開啓 二級緩存
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!-- mybatis的主配置文件 -->
<configuration>
<!-- 外部配置 -->
<properties resource="jdbcConfig.properties"></properties>
<!-- 配置參數 -->
<settings>
<!-- 開啓 二級緩存 -->
<setting name="cacheEnabled" value="true"/>
</settings>
<!-- 指定包:實體類-->
<typeAliases>
<package name="cn.water.domain"/>
</typeAliases>
<!-- 配置環境 -->
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<!-- 配置連接數據庫的4個基本信息 -->
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!-- 指定包:持久層接口 -->
<mappers>
<package name="cn.water.dao"/>
</mappers>
</configuration>
實體類
User.java
package cn.water.domain;
import java.io.Serializable;
import java.util.Date;
/**
* @author Water
* @date 2019/10/12 - 7:51
* @description
*/
public class User implements Serializable {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", birthday=" + birthday +
", sex='" + sex + '\'' +
", address='" + address + '\'' +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
持久層接口
UserDao.java
package cn.water.dao;
import cn.water.domain.User;
/**
* @author Water
* @date 2019/10/12 - 7:51
* @description
*/
public interface UserDao {
/* */
User findById(Integer userId);
}
映射配置文件
UserDao.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.water.dao.UserDao">
<cache></cache>
<select id="findById" parameterType="INT" resultType="user" useCache="true">
<!-- <select id="findById" parameterType="INT" resultType="user" >-->
SELECT * FROM user WHERE id = #{userId};
</select>
</mapper>
測試類
MyBatisTest.java
package cn.water;
import cn.water.dao.UserDao;
import cn.water.domain.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.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
/**
* @author Water
* @date 2019/10/12 - 7:56
* @description
*/
public class MybatisTest {
/* 成員變量 */
private InputStream inputStream;
private SqlSessionFactory factory;
private SqlSession session;
/* 初始化操作 */
@Before
public void init() throws IOException {
/* 加載 MyBatis配置文件 */
inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
/* 獲取 工廠類 */
factory = new SqlSessionFactoryBuilder().build(inputStream);
/* 獲取 產品類 */
session = factory.openSession(true);/* 設置自動提交 */
}
/* 銷燬操作 */
@After
public void destroy() throws IOException {
session.close();
inputStream.close();
}
/** 一級緩存:同一個 SqlSession */
@Test
public void test01(){
UserDao dao = session.getMapper(UserDao.class);
User user01 = dao.findById(41);
System.out.println("第一次查詢:"+user01);
User user02 = dao.findById(41);
System.out.println("第二次查詢:"+user02);
System.out.println("兩次查詢結果是否相等:"+(user01==user02)); //true
}
/** 一級緩存:不同 SqlSession */
@Test
public void test02(){
UserDao dao = session.getMapper(UserDao.class);
User user01 = dao.findById(41);
System.out.println("第一次查詢:"+user01);
/* commit()(執行插入、更新、刪除)、close() */
session.close();
session = factory.openSession();
UserDao dao02 = session.getMapper(UserDao.class);
User user02 = dao02.findById(41);
System.out.println("第二次查詢:"+user02);
System.out.println("兩次查詢結果是否相等:"+(user01==user02));//false
}
}
一級緩存
測試類
/** 一級緩存:同一個 SqlSession */
@Test
public void test01(){
UserDao dao = session.getMapper(UserDao.class);
User user01 = dao.findById(41);
System.out.println("第一次查詢:"+user01);
User user02 = dao.findById(41);
System.out.println("第二次查詢:"+user02);
System.out.println("兩次查詢結果是否相等:"+(user01==user02));
}
運行結果
- 一級緩存是SqlSession級別的緩存,只要SqlSession沒有flush或close,它就存在。
- 我們可以發現,在測試一中雖然我們查詢了兩次,但最後只執行了一次數據庫操作,這就是Mybatis提供給我們的一級緩存在起作用了。
- 因爲一級緩存的存在,導致第二次查詢id爲41的記錄時,並沒有發出sql語句從數據庫中查詢數據,而是從一級緩存中查詢。
一級緩存的清空
測試類
/** 一級緩存:不同 SqlSession */
@Test
public void test02(){
UserDao dao = session.getMapper(UserDao.class);
User user01 = dao.findById(41);
System.out.println("第一次查詢:"+user01);
/* commit()(執行插入、更新、刪除)、close() */
session.close();
session = factory.openSession();
UserDao dao02 = session.getMapper(UserDao.class);
User user02 = dao02.findById(41);
System.out.println("第二次查詢:"+user02);
System.out.println("兩次查詢結果是否相等:"+(user01==user02));
}
運行結果
- 一級緩存是SqlSession範圍的緩存,當調用SqlSession的commit()(修改,添加,刪除),close()等方法時,就會清空一級緩存。
- 第一次發起查詢用戶id爲1的用戶信息,先去找緩存中是否有id爲1的用戶信息,如果沒有,從數據庫查
詢用戶信息。得到用戶信息,將用戶信息存儲到一級緩存中。如果sqlSession去執行commit操作(執行插入、更新、刪除),清空SqlSession中的一級緩存,這樣做的目的爲了讓緩存中存儲的是最新的信息,避免髒讀。 - 第二次發起查詢用戶id爲1的用戶信息,先去找緩存中是否有id爲1的用戶信息,緩存中有,直接從緩存中獲取用戶信息。
二級緩存
- 當我們在使用二級緩存時,所緩存的類一定要實現 java.io.Serializable接口,這種就可以使用序列化
方式來保存對象。
開啓二級緩存
- 二級緩存的使用步驟:
- 第一步:讓Mybatis框架支持二級緩存(在SqlMapConfig.xml中配置)
- 第二步:讓當前的映射文件支持二級緩存(在IUserDao.xml中配置
- 第三步:讓當前的操作支持二級緩存(在select標籤中配置)
MyBatis主配置文件
-
開啓二級緩存支持
cacheEnabled 默認值爲 true(此配置可以省略)
<!-- 配置參數 -->
<settings>
<!-- 開啓 延遲加載 -->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
<!-- 開啓 二級緩存 -->
<setting name="cacheEnabled" value="true"/>
</settings>
映射配置文件
-
設置 <cache> 標籤
表示當前 mapper 映射將使用二級緩存,區分的標準爲 mapper 標籤的 namespace 值。
<mapper namespace="cn.water.dao.UserDao">
<cache></cache>
</mapper>
-
<select> 標籤 設置 useCache=”true” 屬性
注意:針對每次查詢都需要最新的數據sql,要設置成useCache=false,禁用二級緩存。
<mapper namespace="cn.water.dao.UserDao">
<cache></cache>
<select id="findById" parameterType="INT" resultType="user" useCache="true" >
SELECT * FROM user WHERE id = #{userId};
</select>
</mapper>
測試類
- 依然執行 Test02()
@Test
public void test02(){
UserDao dao = session.getMapper(UserDao.class);
User user01 = dao.findById(41);
System.out.println("第一次查詢:"+user01);
/* commit()(執行插入、更新、刪除)、close() */
session.close();
session = factory.openSession();
UserDao dao02 = session.getMapper(UserDao.class);
User user02 = dao02.findById(41);
System.out.println("第二次查詢:"+user02);
System.out.println("兩次查詢結果是否相等:"+(user01==user02));//false
}
運行結果
- 經過上面的測試,我們發現執行了兩次查詢,並且在執行第一次查詢後,我們關閉了一級緩存,再去執行第二
次查詢時,我們發現並沒有對數據庫發出sql語句,所以此時的數據就只能是來自於我們所說的二級緩存。
一級緩存
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-bQWezfwy-1580952138442)(11.緩存\控制檯 證明 一級緩存02.png)]