Java後端--32--Mybatis的多級快取和懶載入

隨 亦發表於2020-12-13

一、一級快取

快取:程式經常要呼叫的物件存在記憶體中,方便其使用時可以快速呼叫,不必去資料庫或者其他持久化裝置中查詢,主要就是提高效能。

Mybatis一級快取:一級快取的作用域是SqlSession,同一個SqlSession中執行相同的SQL查詢時(相同的SQL和引數),第一次會去查詢資料庫並寫在快取中,第二次會直接從快取中取。一級快取是基於PerpetualCache的HashMap本地快取,另外,一級快取是預設開啟的。

失效策略:當執行兩次SQL的查詢中間發生了增刪改的操作,即insert、update、delete等操作commit後會清空該SqlSession快取,比如SqlSession關閉或者清空等。

程式碼示例:

SqlSessionDemo.java

// 查詢musicId為3的資料
// for迴圈兩次,第二次的時候會觸發一級快取
for(int i=0; i<2; i++) {
	Music music = musicMapper.selectById(3);
	System.out.println(music.toString());
}

二、Mybatis二級快取

簡介:二級快取是NameSpace級別的,多個SqlSession去操作同一個NameSpace下Mapper的sql語句,多個SqlSession可以共用二級快取,如果兩個mapper的NameSpace相同,(即使是兩個mapper,那麼這兩個mapper中執行sql查詢到的資料也將存在相同的二級快取區域中,但是最後是每個Mapper單獨的名稱空間)。二級快取是基於PerpetualCache的HashMap本地快取,可自定義儲存源,如 Ehcache/Redis等。另外,二級快取預設是沒有開啟的。

操作流程:第一次呼叫某個NameSpace下的SQL去查詢資訊,查詢到的資訊會存放該mapper對應的二級快取區域。 第二次呼叫同個NameSpace下的mapper對映檔案中,相同的sql去查詢資訊,會去對應的二級快取內取結果。

失效策略:執行同個NameSpace下的mapepr對映檔案中增刪改sql,並執行了commit操作,會清空該二級快取。

注意:實現二級快取的時候,MyBatis建議返回的POJO是可序列化的, 也就是建議實現Serializable介面。

快取淘汰策略:會使用預設的LRU演算法來收回(最近最少使用的被淘汰)。

程式碼示例:

1、domain/Music.java

// 音樂類
// 這裡使用了Serializable介面,主要是為了實現二級快取
public class Music implements Serializable {
	......
}

2、domian/MusicOrder.java

// 這裡使用了Serializable介面,主要是為了實現二級快取
public class MusicOrder implements Serializable {
	......
}

3、resources/config/mybatis-config.xml

<!-- settings設定必須加在environments上面 -->
<settings>
    <!-- 下劃線自動對映駝峰欄位,預設是false -->
    <setting name="mapUnderscoreToCamelCase" value="true"/>
    
    <!-- 這個配置使全域性的對映器(二級快取)啟用或禁用快取,全域性總開關,這裡關閉後mapper中開啟了也沒用 -->
    <setting name="cacheEnabled" value="true" />
</settings>

4、resources/mapper/MusicMapper.xml

<!-- 開啟mapper的NameSpace下的二級快取 -->
<!--
    eviction:代表的是快取回收策略,常見下面兩種。
    1、LRU:最近最少使用的淘汰,一處最長時間不用的物件
    2、FIFO:先進先出淘汰,按物件進入快取的順序來移除他們
    
    flushInterval::重新整理間隔時間,單位為毫秒,這裡配置的是100秒重新整理,如果不配置它,當SQL被執行的時候才會去重新整理快取。

    size:引用數目,代表快取最多可以儲存多少個物件,設定過大會導致記憶體溢位

    readOnly:只讀,快取資料只能讀取而不能修改,預設值是false
-->
<cache eviction="LRU" flushInterval="100000" readOnly="true" size="1024"/>

5、SqlSessionCacheDemo.java

public class SqlSessionCacheDemo {
    public static void main(String [] args) throws IOException {

        String resouce = "config/mybatis-config.xml";

        //讀取配置檔案
        InputStream inputStream =  Resources.getResourceAsStream(resouce);

        //構建Session工廠
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        //獲取Session
        try{
            // 開啟第一個SqlSession
            SqlSession sqlSession1 = sqlSessionFactory.openSession();
            MusicMapper MusicMapper1 =  sqlSession1.getMapper(MusicMapper.class);
            // 查詢musicID為3的資料
            Music music1 = MusicMapper1.selectById(3);
            System.out.println(music1.toString());
            // 提交快取
            sqlSession1.commit();

            // 開啟第二個SqlSession
            SqlSession sqlSession2 = sqlSessionFactory.openSession();
            MusicMapper musicMapper2 =  sqlSession1.getMapper(MusicMapper.class);
            // 查詢musicID為3的資料
            Music music2 = musicMapper2.selectById(3);
            System.out.println(music2.toString());
            // 提交快取
            sqlSession2.commit();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

6、執行效果演示

可見查詢了兩次資料,但只執行了一次SQL語句,二級快取生效

在這裡插入圖片描述

三、懶載入

懶載入:按需載入,先從單表查詢,需要時再從關聯表去關聯查詢,能大大提高資料庫效能,但並不是所有場景下使用懶載入都能提高效率。

Mybatis懶載入:resultMap裡面的association、collection有延遲載入功能(延遲載入也就是懶載入)。

程式碼示例:

1、resources/mapper/MusicOrderMapper.xml

<!-- 懶載入 -->
<resultMap id="MusicOrderResultMapLazy" type="MusicOrder">
    <id column="id" property="id"/>

    <result column="user_id" property="userId"/>
    <result column="out_trade_no" property="outTradeNo"/>
    <result column="create_time" property="createTime"/>
    <result column="state" property="state"/>
    <result column="total_fee" property="totalFee"/>
    <result column="music_id" property="musicId"/>
    <result column="music_title" property="musicTitle"/>
    <result column="music_img" property="musicImg"/>

    <!--
    select:指定延遲載入需要執行的statement id
    column:和select查詢關聯的欄位
    -->
    <association property="user" javaType="User" column="user_id" select="findUserByUserId"/>
</resultMap>

<!-- 一對一管理查詢訂單,訂單內部包含使用者屬性。懶載入 -->
<select id="queryMusicOrderListLazy" resultMap="MusicOrderResultMapLazy">
    select
        o.id id,
        o.user_id ,
        o.out_trade_no,
        o.create_time,
        o.state,
        o.total_fee,
        o.music_id,
        o.music_title,
        o.music_img
    from music_order o
</select>

<!-- 查詢使用者資訊 -->
<select id="findUserByUserId" resultType="User">
    select  * from user where id=#{id}
</select>

2、resources/config/mybatis-config.xml

<!-- settings設定必須加在environments上面 -->
<settings>
    <!-- 下劃線自動對映駝峰欄位,預設是false -->
    <setting name="mapUnderscoreToCamelCase" value="true"/>

    <!-- 這個配置使全域性的對映器(二級快取)啟用或禁用快取,全域性總開關,這裡關閉後mapper中開啟了也沒用 -->
    <setting name="cacheEnabled" value="true" />

    <!-- 延遲載入總開關 -->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!-- 將aggressiveLazyLoading設定為false表示按需載入,預設為true -->
    <setting name="aggressiveLazyLoading" value="false"/>
</settings>

3、dao/MusicOrderMapper.java

// 查詢全部訂單,關聯使用者資訊,懶載入
List<MusicOrder> queryMusicOrderListLazy();

4、SqlSessionDemo.java

// resultmap association關聯查詢(測試懶載入)
MusicOrderMapper musicOrderMapper = sqlSession.getMapper(MusicOrderMapper.class);
List<MusicOrder> musicOrderList = musicOrderMapper.queryMusicOrderListLazy();
System.out.println(musicOrderList.size());

for(MusicOrder musicOrder : musicOrderList){
    System.out.println(musicOrder.getMusicTitle());
    System.out.println(musicOrder.getUser().getName());
}

5、執行演示,可見懶載入生效

在這裡插入圖片描述

四、總結

總結一下本節的程式碼結構:

在這裡插入圖片描述

相關文章