切換語言為:簡體
深入解析 MyBatis 的快取機制

深入解析 MyBatis 的快取機制

  • 爱糖宝
  • 2024-06-16
  • 2070
  • 0
  • 0

1. MyBatis 的快取機制


快取(Cache)

快取的作用:透過減少 IO 的方式,來提高程式的執行效率

MyBatis 的快取:將 Select 語句的查詢結果放到快取(記憶體)當中,下一次還是這條  Select 語句的話,直接就從快取當中取了,不再查詢資料庫。這樣一方面減少了 IO,另一方面不再執行繁瑣的查詢演算法。效率大大提升。

MyBatis 快取包括:

  • 一級快取:將查詢到的資料儲存到 SqlSession 中

  • 二級快取:將查詢到的資料儲存到 SqlSessionFactory 中

  • 或者是整合其它第三方的快取:比如 EhCache(Java語言開發的),Memcache(C語言開發的)等。

注意:快取只針對於 DQL(查詢)語句,也就是說快取機制只對應 Select 語句。

一旦你執行了,insert 或者delete或者 update 更新語句,無論是否是更新修改刪除那個資料表中的記錄,都會清空快取,所以,這樣就不會導致 快取當中的 select 語句的資料是:舊的無用的資料了。

2. 準備工作

資料表結構的設計,資料表名為:t_car

深入解析 MyBatis 的快取機制

t_car 表中的資料資訊:

深入解析 MyBatis 的快取機制

pom.xml 檔案當中配置相關的依賴的 jar 包如下:

深入解析 MyBatis 的快取機制

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.rainbowsea</groupId>
    <artifactId>mybatis-005-crud-blog</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>

    <dependencies>
        <!--        mybatis 的依賴-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.10</version>
        </dependency>

        <!--        mysql -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.30</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>

        <!--        引入 logback的依賴,這個日誌框架實現了slf4j 規範-->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.11</version>
        </dependency>
    </dependencies>

</project>


配置 logback 的配置檔案,用於列印顯示,我們的日誌資訊,方便我們檢視我們的執行過程,效果。

深入解析 MyBatis 的快取機制

<?xml version="1.0" encoding="UTF-8"?>

<configuration debug="false">
    <!-- 控制檯輸出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化輸出:%d表示日期,%thread表示執行緒名,%-5level:級別從左顯示5個字元寬度%msg:日誌訊息,%n是換行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
    </appender>

    <!--mybatis log configure-->
    <logger name="com.apache.ibatis" level="TRACE"/>
    <logger name="java.sql.Connection" level="DEBUG"/>
    <logger name="java.sql.Statement" level="DEBUG"/>
    <logger name="java.sql.PreparedStatement" level="DEBUG"/>

    <!-- 日誌輸出級別,logback日誌級別包括五個:TRACE < DEBUG < INFO < WARN < ERROR -->
    <root level="DEBUG">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="FILE"/>
    </root>

</configuration>


配置 MyBatis 的核心配置檔案,

深入解析 MyBatis 的快取機制

<?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">
<configuration>

        <!--  使用 <package>	還可以將這個包下的所有的類的全部自動起別名,別名就是簡名,不區分大小寫 -->
        <package name="com.rainbowsea.mybatis.pojo"/>
    </typeAliases>
    <environments default="mybatis">

        <environment id="mybatis">
            <!--            MANAGED 沒有用第三框架管理的話,都是會被提交的,沒有事務上的管理了 -->
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="MySQL123"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!-- 這裏也是可以使用 package 包名掃描,但是同樣的:對應介面路徑要一致,介面名一致-->
        <package name="com.rainbowsea.mybatis.mapper"></package>
    </mappers>
</configuration>


對照 t_car 建立的ORM 對映的 Car 類

注意:在MyBatis 當中對應的ORM ,一般在框架裡對應的 Bean實體類,一定要實現該 set 和 get 方法以及無引數構造方法,無法框架無法使用反射機制,進行操作

建議用包裝類,這樣可以防止 Null的問題,因為(簡單型別 int num = null ,是不可以賦值為 null)的編譯無法透過

深入解析 MyBatis 的快取機制

package com.rainbowsea.mybatis.pojo;

public class Car {
    // 資料庫表當中的欄位應該和pojo類的屬性一一對應
    // 建議使用包裝類,這樣可以防止null的問題
    private Long id;
    private String carNum;
    private String brand;
    private Double guidePrice;
    private String produceTime;
    private String carType;

    public Car() {
    }

    public Car(Long id, String carNum, String brand, Double guidePrice, String produceTime, String carType) {
        this.id = id;
        this.carNum = carNum;
        this.brand = brand;
        this.guidePrice = guidePrice;
        this.produceTime = produceTime;
        this.carType = carType;
    }

    @Override
    public String toString() {
        return "Car{" +
                "id=" + id +
                ", carNum='" + carNum + '\'' +
                ", brand='" + brand + '\'' +
                ", guidePrice=" + guidePrice +
                ", produceTime='" + produceTime + '\'' +
                ", catType='" + carType + '\'' +
                '}';
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getCarNum() {
        return carNum;
    }

    public void setCarNum(String carNum) {
        this.carNum = carNum;
    }

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public Double getGuidePrice() {
        return guidePrice;
    }

    public void setGuidePrice(Double guidePrice) {
        this.guidePrice = guidePrice;
    }

    public String getProduceTime() {
        return produceTime;
    }

    public void setProduceTime(String produceTime) {
        this.produceTime = produceTime;
    }

    public String getcarType() {
        return carType;
    }

    public void setcarType(String catType) {
        this.carType = catType;
    }
}

3. MyBatis 的一級快取

一級快取預設是開啟的。不需要做任何配置。

原理:只要使用同一個SqlSession物件執行同一條SQL語句,就會走快取。

一級快取的內容是:將查詢到的資料儲存到 SqlSession 當中的。注意:其快取的作用域

深入解析 MyBatis 的快取機制

package com.rainbowsea.mybatis.mapper;

import com.rainbowsea.mybatis.pojo.Car;
import com.rainbowsea.mybatis.pojo.Clazz;

public interface CarMapper {

    Car selectById(Long id);
}


深入解析 MyBatis 的快取機制

<?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">

<!--namespace 一定要是:對應的介面的全限定類名-->
<mapper namespace="com.rainbowsea.mybatis.mapper.CarMapper">


    <!--	id 要是 namespace 對應介面上的方法名: -->
    <select id="selectById" resultType="Car">
        select id, car_num, brand, guide_price, produce_time, car_type
        from t_car
        where id = #{id}
    </select>
</mapper>


執行測試:

深入解析 MyBatis 的快取機制

深入解析 MyBatis 的快取機制

    @Test
    public void testSelectById() throws IOException {
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"), "mybatis");
        SqlSession sqlSession = sqlSessionFactory.openSession();
        CarMapper mapper = sqlSession.getMapper(CarMapper.class);
        Car car = mapper.selectById(118L);
        System.out.println(car);

        CarMapper mapper1 = sqlSession.getMapper(CarMapper.class);
        Car car1 = mapper1.selectById(118L);
        System.out.println(car1);
    }


深入解析 MyBatis 的快取機制

深入解析 MyBatis 的快取機制

執行 Select 查詢語句的時候,首先從對應的這個 Select 語句的 SqlSession 物件(一級快取)當中查詢是否有對應該Select 查詢語句的快取有的話,就不執行該 查詢的 SQL 語句了,而是直接從一級快取當中取出這個 Select 查詢語句的資料結果。第一次執行  Select 語句(因為MyBatis 一級快取預設是開啟的)就會將存入到 sqlSession 物件(一級快取)當中,方便後續的查詢。

3.1 一級快取失效情況/條件

一級快取失效了,二級快取同樣也是失效的了,所以一級快取失效的條件也是二級快取失效的條件,他們的條件都是一樣的。

思考:什麼時候不走快取?

  • sqlSession 物件不是同一個,肯定不走快取。

  • 查詢條件不一樣,肯定不走快取。

一級快取失效情況包括兩種:

  • 第一種:第一次查詢和第二次查詢之間,手動清空了一級快取。執行:執行了 sqlSession.clearCache()方法,這是手動情況快取。

  • 第二種:執行了INSERT 或 DELETE 或UPDATE語句,不管你是操作任意一張表,都會清空一級快取。

無論你是,你做了以上兩件事的任意一種,都會讓一級快取清空


第一種:第一次查詢和第二次查詢之間,手動清空了一級快取。執行:執行了 sqlSession.clearCache()方法,這是手動情況快取。

測試:

深入解析 MyBatis 的快取機制

深入解析 MyBatis 的快取機制

package com.rainbowsea.mybatis.test;


import com.rainbowsea.mybatis.mapper.CarMapper;
import com.rainbowsea.mybatis.pojo.Car;
import com.rainbowsea.mybatis.pojo.Clazz;
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.IOException;

public class CarMapperTest {

    /**
     * 思考:什麼時候不走快取?
     * sqlsession 物件不是同一個,肯定不走快取
     * 查詢條件不一樣,肯定不走快取
     * <p>
     * 思考什麼時候一級快取失敗?
     * 第一次DQL和第二次DQL之間你做了一下兩件事的任意一種,都會讓一級快取清空
     * 1. 執行了 sqlSession.clearCache()方法,這是手動情況快取
     * 2. 執行了INSERT 或 DELETE 或UPDATE語句,不管你是操作那張表,都會清空一級快取
     *
     * @throws IOException
     */
    @Test
    public void testSelectById3() throws IOException {
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"), "mybatis");
        SqlSession sqlSession = sqlSessionFactory.openSession();
        CarMapper mapper = sqlSession.getMapper(CarMapper.class);
        Car car = mapper.selectById(118L);
        System.out.println(car);

        // 手動清空一級快取
        sqlSession.clearCache();

        CarMapper mapper1 = sqlSession.getMapper(CarMapper.class);
        Car car1 = mapper1.selectById(118L);
        System.out.println(car1);

        sqlSession.close();
    }

}


  • 第二種:第一次查詢和第二次查詢之間,執行了增刪改操作。【這個增刪改和哪張表沒有關係,只要有insert delete update操作,一級快取就失效。】

深入解析 MyBatis 的快取機制

深入解析 MyBatis 的快取機制

 @Test
    public void testSelectById3() throws IOException {
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"), "mybatis");
        SqlSession sqlSession = sqlSessionFactory.openSession();
        CarMapper mapper = sqlSession.getMapper(CarMapper.class);
        Car car = mapper.selectById(118L);
        System.out.println(car);


// 在這裏執行 insert或者 delete 或者 update 中的任意一個語句,並且和表沒有關係
        CarMapper mapper2 = sqlSession.getMapper(CarMapper.class);
        mapper2.insertClazz(new Clazz(2000,"高三三班"));

        CarMapper mapper1 = sqlSession.getMapper(CarMapper.class);
        Car car1 = mapper1.selectById(118L);
        System.out.println(car1);

        sqlSession.close();
    }


4. MyBatis 的二級快取

二級快取的範圍是SqlSessionFactory。

二級快取:將查詢到的資料儲存到 SqlSessionFactory 中,範圍比一級快取中更大一些。

使用二級快取步驟/條件

  1. 要在 MyBatis 的核心配置檔案當中,設定<setting name="cacheEnabled" value="true"> 全域性性地開啟或關閉所有對映器配置檔案中已配置的任何快取。預設就是true,無需設定

  2. 在需要使用二級快取的 對應的 SqlMapper.xml  檔案中新增配置:

  3. 使用二級快取的實體類物件必須是可序列化的,也就是對應的POJO實體類,必須實現java.io.Serializable 介面

  4. 只有 當 SqlSession物件關閉或提交之後,一級快取中的資料纔會被寫入到二級快取當中。此時二級快取纔可用,不然沒有提交/關閉,二級快取是沒有儲存到資料資訊的,是無效的。

第一步: 要在 MyBatis 的核心配置檔案當中,設定<setting name="cacheEnabled" value="true"> 全域性性地開啟或關閉所有對映器配置檔案中已配置的任何快取。預設就是true,無需設定

第二步: 在需要使用二級快取的 對應的 SqlMapper.xml  檔案中新增配置:

深入解析 MyBatis 的快取機制

深入解析 MyBatis 的快取機制

<?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">

<!--namespace 一定要是:對應的介面的全限定類名-->
<mapper namespace="com.rainbowsea.mybatis.mapper.CarMapper">

<!--
        預設情況下,二級快取機制是開啟的
        只需要在對應的SqlMapper.xml檔案中新增以下標籤,用來表示“我”使用該二級快取
-->
    <cache></cache>



    <insert id="insertClazz">
        insert into t_clazz values (#{cid},#{cname})
    </insert>
</mapper>


第三步: 使用二級快取的實體類物件必須是可序列化的,也就是對應的POJO實體類,必須實現java.io.Serializable 介面

深入解析 MyBatis 的快取機制

第四步: 只有 當 SqlSession物件關閉或提交之後,一級快取中的資料纔會被寫入到二級快取當中。此時二級快取纔可用,不然沒有提交/關閉,二級快取是沒有儲存到資料資訊的,是無效的。

深入解析 MyBatis 的快取機制

import com.rainbowsea.mybatis.mapper.CarMapper;
import com.rainbowsea.mybatis.pojo.Car;
import com.rainbowsea.mybatis.pojo.Clazz;
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.IOException;

public class CarMapperTest {

    @Test
    public void testSelectById4() throws IOException {
        // 這裏只有一個SqlSessionFactory 物件,二級快取對應的就是SqlSessionFactory
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"), "mybatis");
        SqlSession sqlSession1 = sqlSessionFactory.openSession();
        SqlSession sqlSession2 = sqlSessionFactory.openSession();
        CarMapper mapper1 = sqlSession1.getMapper(CarMapper.class);
        CarMapper mapper2 = sqlSession2.getMapper(CarMapper.class);

        // 這行程式碼執行結束之後,時間上資料快取到一級快取當中了,(sqlSession是一級快取)
        Car car = mapper1.selectById(118L);
        System.out.println(car);

        // 如果這裏不關閉sqlSession物件的話,二級快取中還是沒有資料的
        // 如果執行了這行程式碼,sqlSession1的一級快取中的資料會放到二級快取當中
        sqlSession1.close();



        // 這行程式碼執行結束之後,實際上資料會快取到一級快取當中。(sqlSession2 是一級快取)
        Car car1 = mapper2.selectById(118L);
        System.out.println(car1);

        // 程式執行到這裏的時候,會有SqlSession1這個一級快取中的資料寫入到二級快取當中
        // sqlSession1.close()

        // 程式執行到這裏的時候,會將sqlSession2這個一級快取中的資料寫入到二級快取當中
        sqlSession2.close();
    }

}


執行測試:

深入解析 MyBatis 的快取機制

二級快取的失效:只要兩次查詢之間出現了增刪改操作。二級快取就會失效。【一級快取也會失效】

二級快取的相關配置:

深入解析 MyBatis 的快取機制

  1. eviction :指定從快取中移除某個物件的淘汰演算法。預設採用LRU策略。


    1. LRU:Least Recently Used。最近最少使用。優先淘汰在間隔時間內使用頻率最低的物件。(其實還有一種淘汰演算法LFU,最不常用。)

    2. FIFO:First In First Out。一種先進先出的資料快取器。先進入二級快取的物件最先被淘汰。

    3. SOFT:軟引用。淘汰軟引用指向的物件。具體演算法和JVM的垃圾回收演算法有關。

    4. WEAK:弱引用。淘汰弱引用指向的物件。具體演算法和JVM的垃圾回收演算法有關。

  2. flushInterval


    1. 二級快取的重新整理時間間隔。單位毫秒。如果沒有設定。就代表不重新整理快取,只要記憶體足夠大,一直會向二級快取中快取資料。除非執行了增刪改。

  3. readOnly


    1. true:多條相同的sql語句執行之後返回的物件是共享的同一個。效能好。但是多執行緒併發可能會存在安全問題。

    2. false:多條相同的sql語句執行之後返回的物件是副本,呼叫了clone方法。效能一般。但安全。

  4. size


    1. 設定二級快取中最多可儲存的java物件數量。預設值1024。

5. MyBatis 整合 EhCache 第三方快取

整合EhCache是爲了代替mybatis自帶的二級快取。一級快取是無法替代的。

mybatis對外提供了介面,也可以整合第三方的快取元件。比如EhCache、Memcache等。都可以。

EhCache是Java寫的。Memcache是C語言寫的。所以mybatis整合EhCache較為常見,按照以下步驟操作,就可以完成整合:

第一步: 引入mybatis 整合 ehcache 的依賴。

<!--mybatis整合ehcache的元件-->
<dependency>
  <groupId>org.mybatis.caches</groupId>
  <artifactId>mybatis-ehcache</artifactId>
  <version>1.2.2</version>
</dependency>


深入解析 MyBatis 的快取機制

第二步: 在類的根路徑下新建 echcache.xml (檔名必須是:echcache.xml 不可以修改)檔案,並提供以下配置資訊。

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
         updateCheck="false">
    <!--磁碟儲存:將快取中暫時不使用的物件,轉移到硬碟,類似於Windows系統的虛擬記憶體-->
    <diskStore path="e:/ehcache"/>

    <!--defaultCache:預設的管理策略-->
    <!--eternal:設定快取的elements是否永遠不過期。如果為true,則快取的資料始終有效,如果為false那麼還要根據timeToIdleSeconds,timeToLiveSeconds判斷-->
    <!--maxElementsInMemory:在記憶體中快取的element的最大數目-->
    <!--overflowToDisk:如果記憶體中資料超過記憶體限制,是否要快取到磁碟上-->
    <!--diskPersistent:是否在磁碟上持久化。指重啟jvm後,資料是否有效。預設為false-->
    <!--timeToIdleSeconds:物件空閒時間(單位:秒),指物件在多長時間沒有被訪問就會失效。只對eternal為false的有效。預設值0,表示一直可以訪問-->
    <!--timeToLiveSeconds:物件存活時間(單位:秒),指物件從建立到失效所需要的時間。只對eternal為false的有效。預設值0,表示一直可以訪問-->
    <!--memoryStoreEvictionPolicy:快取的3 種清空策略-->
    <!--FIFO:first in first out (先進先出)-->
    <!--LFU:Less Frequently Used (最少使用).意思是一直以來最少被使用的。快取的元素有一個hit 屬性,hit 值最小的將會被清出快取-->
    <!--LRU:Least Recently Used(最近最少使用). (ehcache 預設值).快取的元素有一個時間戳,當快取容量滿了,而又需要騰出地方來快取新的元素的時候,那麼現有快取元素中時間戳離當前時間最遠的元素將被清出快取-->
    <defaultCache eternal="false" maxElementsInMemory="1000" overflowToDisk="false" diskPersistent="false"
                  timeToIdleSeconds="0" timeToLiveSeconds="600" memoryStoreEvictionPolicy="LRU"/>

</ehcache>


深入解析 MyBatis 的快取機制

第三步: 修改對應的 SqlMapper.xml檔案中的標籤,新增type屬性。

<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>


深入解析 MyBatis 的快取機制

import com.rainbowsea.mybatis.mapper.CarMapper;
import com.rainbowsea.mybatis.pojo.Car;
import com.rainbowsea.mybatis.pojo.Clazz;
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.IOException;

public class CarMapperTest {
    @Test
    public void testSelectById5() throws Exception {
        // 這裏只有一個SqlSessionFactory 物件,二級快取對應的就是SqlSessionFactory
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"), "mybatis");

        SqlSession sqlSession1 = sqlSessionFactory.openSession();
        CarMapper mapper1 = sqlSession1.getMapper(CarMapper.class);
        Car car1 = mapper1.selectById(118L);
        System.out.println(car1);

        sqlSession1.close();

        SqlSession sqlSession2 = sqlSessionFactory.openSession();
        CarMapper mapper2 = sqlSession2.getMapper(CarMapper.class);
        Car car2 = mapper2.selectById(118L);
        System.out.println(car2);
        
        sqlSession2.close();
    }
}


**第四步:**編寫測試程式使用。

深入解析 MyBatis 的快取機制

深入解析 MyBatis 的快取機制

import com.rainbowsea.mybatis.mapper.CarMapper;
import com.rainbowsea.mybatis.pojo.Car;
import com.rainbowsea.mybatis.pojo.Clazz;
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.IOException;

public class CarMapperTest {
    @Test
    public void testSelectById5() throws Exception {
        // 這裏只有一個SqlSessionFactory 物件,二級快取對應的就是SqlSessionFactory
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"), "mybatis");

        SqlSession sqlSession1 = sqlSessionFactory.openSession();
        CarMapper mapper1 = sqlSession1.getMapper(CarMapper.class);
        Car car1 = mapper1.selectById(118L);
        System.out.println(car1);

        sqlSession1.close();

        SqlSession sqlSession2 = sqlSessionFactory.openSession();
        CarMapper mapper2 = sqlSession2.getMapper(CarMapper.class);
        Car car2 = mapper2.selectById(118L);
        System.out.println(car2);
        
        sqlSession2.close();
    }
}


6. 總結:

  1. MyBatis 快取包括:

    • 一級快取:將查詢到的資料儲存到 SqlSession 中

    • 二級快取:將查詢到的資料儲存到 SqlSessionFactory 中

    • 或者是整合其它第三方的快取:比如 EhCache(Java語言開發的),Memcache(C語言開發的)等。

  2. 注意:快取只針對於 DQL(查詢)語句,也就是說快取機制只對應 Select 語句。

  3. 一級快取預設是開啟的

  4. 一級快取失效情況包括兩種:

    • 第一種:第一次查詢和第二次查詢之間,手動清空了一級快取。執行:執行了 sqlSession.clearCache()方法,這是手動情況快取。

    • 第二種:執行了INSERT 或 DELETE 或UPDATE語句,不管你是操作任意一張表,都會清空一級快取。

  5. 一級快取失效了,二級快取也是失效了,二級快取是透過將一級快取的快取儲存到二級快取當中的,所以一級失效,二級也是失效的

  6. 二級快取:將查詢到的資料儲存到 SqlSessionFactory 中,範圍比一級快取中更大一些。


0則評論

您的電子郵件等資訊不會被公開,以下所有項目均必填

OK! You can skip this field.