1. 程式人生 > 實用技巧 >MyBatis3.X 多級快取和懶載入

MyBatis3.X 多級快取和懶載入

一、Mybatis3.X一級快取講解

什麼是快取

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

Mybatis一級快取

一級快取的作用域是 SQLSession,同一個 SqlSession 中執行相同的 SQL 查詢(相同的 SQL 和引數),第一次會去查詢資料庫並寫在快取中,第二次會直接從快取中取

  • 基於 PerpetualCache 的 HashMap 本地快取
  • 預設開啟一級快取
  • 失效策略:當執行 SQL 時候兩次查詢中間發生了增刪改的操作,即 insert、update、delete 等操作,commit 後會清空該 SQLSession 快取;比如 SqlSession 關閉,或者清空等

二、Mybatis二級快取和配置

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 演算法來收回(最近最少使用的)

如何開啟某個二級快取

mapper.xml 裡面配置

<!--開啟mapper的namespace下的二級快取-->
<!--
    eviction:代表的是快取回收策略,常見下面兩種。
    (1) LRU,最近最少使用的,一處最長時間不用的物件
    (2) FIFO,先進先出,按物件進入快取的順序來移除他們

    flushInterval:重新整理間隔時間,單位為毫秒,這裡配置的是100秒重新整理,如果不配置它,當SQL被執行的時候才會去重新整理快取。

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

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

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

如果需要控制全域性 mapper 裡面某個方法不使用快取,可以配置 useCache="false"

<select id="selectById" parameterType="java.lang.Integer" resultType="Video" useCache="false">
    select * from video where id = #{video_id, jdbcType=INTEGER}
</select>

一級快取和二級快取使用順序

優先查詢二級快取 -> 查詢一級快取 -> 資料庫

三、Mybatis3.x 的懶載入

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

Mybatis 懶載入:resultMap 裡面的 association、collection 有延遲載入功能

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

示例:

<resultMap id="VideoOrderResultMapLazy" type="VideoOrder">
    <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="video_id" property="videoId"/>
    <result column="video_title" property="videoTitle"/>
    <result column="video_img" property="videoImg"/>

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

</resultMap>

<!--一對一管理查詢訂單, 訂單內部包含使用者屬性  懶載入-->
<select id="queryVideoOrderListLazy" resultMap="VideoOrderResultMapLazy">
        select
         o.id id,
         o.user_id ,
         o.out_trade_no,
         o.create_time,
         o.state,
         o.total_fee,
         o.video_id,
         o.video_title,
         o.video_img
         from video_order o
</select>


<select id="findUserByUserId" resultType="User">
    select  * from user where id=#{id}
</select>

dubug 模式測試懶載入不準確,可以直接 run

// resultmap association關聯查詢(測試懶載入)
VideoOrderMapper videoOrderMapper = sqlSession.getMapper(VideoOrderMapper.class);
List<VideoOrder> videoOrderList = videoOrderMapper.queryVideoOrderListLazy();
System.out.println(videoOrderList.size());

// 演示是6條訂單記錄,但是隻查詢3次使用者資訊,是因為部分使用者資訊走了一級快取SqlSession
for(VideoOrder videoOrder : videoOrderList){
    System.out.println(videoOrder.getVideoTitle());
    System.out.println(videoOrder.getUser().getName());
}