MyBatis3.X 多級快取和懶載入
阿新 • • 發佈:2020-08-29
一、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()); }