mybatis延遲載入和快取

shaonianbz發表於2018-03-27

一、mybatis延遲載入

1、延遲載入概述

(1)延遲載入又叫懶載入,也叫按需載入。也就是說先載入主資訊,在需要的時候,再去載入從資訊

(2)在mybatis中,resultMap標籤 的association標籤和collection標籤具有延遲載入的功能。

2、延遲載入案例分析

(1)需求:查詢訂單資訊,關聯查詢使用者資訊
a、建立一個statement來查詢訂單資訊
b、建立一個statement來查詢使用者資訊
(2)對映檔案:

    <!-- lazyLoadingRstMap -->
    <resultMap type="ordersExt" id="lazyLoadingRstMap">
        <!-- 訂單資訊 -->
        <id column="id" property="id" />
        <result column="user_id" property="userId" />
        <result column="number" property="number" />
        <!-- 使用者資訊(一對一) -->
        <!-- select:指定關聯查詢的查詢statement(即查詢使用者的statement的id),然後將查詢結果,封裝到property屬性指定的變數中 -->
        <!-- column:通過column指定的列所查詢出的結果,作為select指的statement的入參 -->
        <!-- 注意:如果select指定的statement,入參需要多個值,需要在column中{col1=prop1,col2=prop2} -->
        <association property="user"
            select="com.san.mapper.UserMapper.findUserById" column="user_id"></association>
    </resultMap>

    <!-- 延遲載入 -->
    <select id="findOrderAndUserLazyLoading" resultMap="lazyLoadingRstMap">
        SELECT * FROM
        orders
    </select>

建立查詢使用者資訊的對映檔案:

<!-- 根據使用者ID查詢使用者資訊 -->
    <select id="findUserById" parameterType="int" resultType="com.san.po.User">
        SELECT
        * FROM USER WHERE id =#{id}
    </select>

(3)mapper介面:

    //延遲載入
    public List<OrdersExt> findOrderAndUserLazyLoading();

(4)測試程式碼:

    //懶載入
    @Test
    public void Test01() throws IOException{
        // 讀取配置檔案
        // 全域性配置檔案的路徑
        String resource = "SqlMapConfig.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        // 建立SqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        // 建立OrdersMapper物件
        SqlSession sqlSession = sqlSessionFactory.openSession();
        OrdersMapper mapper = sqlSession.getMapper(OrdersMapper.class);
        List<OrdersExt> list = mapper.findOrderAndUserLazyLoading();
        //按需載入時,是需要的時候再去查詢資料庫
        for (OrdersExt ordersExt : list) {
            System.out.println(ordersExt.getUser());
        }
        sqlSession.close();
    }

(5)設定延遲載入:
在SqlMapConfig.xml中,配置settings標籤

    <settings>
        <!-- 開啟延遲載入 ,預設值為true-->
        <setting name="lazyLoadingEnabled" value="true"/>

        <!-- 設定積極的懶載入,預設是true -->
        <setting name="aggressiveLazyLoading" value="false"/>

        <!-- 二級快取的總開關 -->
        <setting name="cacheEnabled" value="true"/>
    </settings>

二、mybatis一級快取

1、mybatis快取理解

(1)Mybatis的快取,包括一級快取和二級快取,一級快取是預設使用的,二級快取需要手動開啟。

(2)一級快取指的就是sqlsession,在sqlsession中有一個資料區域,是map結構,這個區域就是一級快取區域。一級快取中的key是由sql語句、條件、statement等資訊組成一個唯一值。一級快取中的value,就是查詢出的結果物件。

(3)二級快取指的就是同一個namespace下的mapper,二級快取中,也有一個map結構,這個區域就是一級快取區域。一級快取中的key是由sql語句、條件、statement等資訊組成一個唯一值。一級快取中的value,就是查詢出的結果物件。

(4)圖解:
這裡寫圖片描述

2、一級快取原理

這裡寫圖片描述

3、一級快取測試

(1)測試1:

@Test
//一級快取測試1
public void Test01() throws Exception{
    //全域性配置檔案
    String resource="SqlMapConfig.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    //建立Mapper物件
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    //第一次查詢
    User user1=userMapper.findUserById(1);
    System.out.println(user1);
    //第二次查詢
    User user2=userMapper.findUserById(1);
    System.out.println(user2);
    //關閉資源
    sqlSession.close();
}

(2)測試2:

@Test
//一級快取測試2
public void Test02() throws Exception{
    //全域性配置檔案
    String resource="SqlMapConfig.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    //建立mapper物件
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    //第一次查詢
    User user1=mapper.findUserById(1);
    System.out.println(user1);

    //執行新增使用者操作
    mapper.insertUser(user1);
    //執行commit操作,將一級快取清空
    sqlSession.commit();

    //第二次查詢
    User user2=mapper.findUserById(1);
    System.out.println(user2);
}

(3)解釋:
這裡寫圖片描述

三、mybatis二級快取

1、二級快取原理

這裡寫圖片描述

2、二級快取測試

(1)開啟二級快取:
a、開啟二級快取的總開關

<!-- 二級快取的總開關 -->
<setting name="cacheEnabled" value="true"/>

b、在mapper對映檔案中開啟二級快取

<!-- 開啟二級快取,預設使用PerpetualCache -->
<cache/>

(2)序列化:
二級快取,需要序列化是因為需要將快取的資料寫入硬碟

public class User implements Serializable

(3)測試1:

@Test
//二級快取測試1
public void Test01() throws Exception{
    String resource="SqlMapConfig.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession1 = sqlSessionFactory.openSession();
    SqlSession sqlSession2 = sqlSessionFactory.openSession();
    SqlSession sqlSession3 = sqlSessionFactory.openSession();

    //由mybatis通過sqlSession來建立代理物件
    UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
    UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
    UserMapper mapper3 = sqlSession3.getMapper(UserMapper.class);

    //第一次查詢
    User user1=mapper1.findUserById(1);
    System.out.println(user1);
    //在close的時候,才會將資料寫入二級快取中
    sqlSession1.close();

    //第二次查詢
    User user2=mapper2.findUserById(1);
    System.out.println(user2);
    sqlSession2.close();

}

這裡寫圖片描述
(4)測試2:

@Test
//二級快取測試2
public void Test02() throws Exception{
    String resource="SqlMapConfig.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession1 = sqlSessionFactory.openSession();
    SqlSession sqlSession2 = sqlSessionFactory.openSession();
    SqlSession sqlSession3 = sqlSessionFactory.openSession();

    //由mybatis通過sqlSession來建立代理物件
    UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
    UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
    UserMapper mapper3 = sqlSession3.getMapper(UserMapper.class);

    //第一次查詢
    User user1=mapper1.findUserById(1);
    System.out.println(user1);
    //在close的時候,才會將資料寫入二級快取中
    sqlSession1.close();

    //執行使用者新增操作
    mapper3.insertUser(user1);
    //執行commit時,將一級快取清空
    sqlSession3.close();


    //第二次查詢
    User user2=mapper2.findUserById(1);
    //System.out.println(user2);
    sqlSession2.close();
}

3、其他

(1)禁用快取:
useCache=”false”

<select id="findUserById" useCache="false" parameterType="int" resultType="com.san.po.User">
        SELECT
        * FROM USER WHERE id =#{id}
    </select>

(2)重新整理快取:
flushCache=”true”:重新整理快取,在select語句中,預設值是false,在增刪改語句中,預設值是true

<select id="findUserById" flushCache="true" parameterType="int" resultType="com.san.po.User">
        SELECT
        * FROM USER WHERE id =#{id}
    </select>

四、整合ehcache

1、ehcache概述

(1)Ehcache是一個分散式的快取框架

(2)Mybatis本身是一個持久層框架,它不是專門的快取框架,所以它對快取的實現不夠好,不能支援分散式。

2、分散式概述

(1)系統為了提高效能,通常會對系統採用分散式部署(叢集部署方式)

(2)圖解:
這裡寫圖片描述

3、整合思路

(1)Cache是一個介面,它的預設實現是mybatis的PerpetualCache。如果想整合mybatis的二級快取,那麼實現Cache介面即可

(2)圖解:
這裡寫圖片描述

4、整合步驟

(1)新增jar包
這裡寫圖片描述
(2)設定對映檔案中cache標籤的type值為ehcache的實現類
這裡寫圖片描述
(3)新增ehcache的配置檔案
在config下,建立ehcache.xml

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
    <!-- 快取資料要存放的磁碟地址 -->
    <diskStore path="F:\develop\ehcache" />
    <!-- diskStore:指定資料在磁碟中的儲存位置。  
         defaultCache:當藉助CacheManager.add("demoCache")建立Cache時,
        EhCache便會採用<defalutCache/>指定的的管理策略 
        以下屬性是必須的:  maxElementsInMemory - 在記憶體中快取的element的最大數目  
        maxElementsOnDisk - 在磁碟上快取的element的最大數目,若是0表示無窮大  
        eternal - 設定快取的elements是否永遠不過期。如果為true,則快取的資料始終有效,
        如果為false那麼還要根據timeToIdleSeconds,timeToLiveSeconds判斷 
        overflowToDisk - 設定當記憶體快取溢位的時候是否將過期的element快取到磁碟上 以下屬性是可選的:  
        timeToIdleSeconds - 當快取在EhCache中的資料前後兩次訪問的時間超過timeToIdleSeconds的屬性取值時,
        這些資料便會刪除,預設值是0,也就是可閒置時間無窮大 
        timeToLiveSeconds - 快取element的有效生命期,預設是0.,
        也就是element存活時間無窮大 diskSpoolBufferSizeMB 
        這個引數設定DiskStore(磁碟快取)的快取區大小.預設是30MB.每個Cache都應該有自己的一個緩衝區.  
        diskPersistent - 在VM重啟的時候是否啟用磁碟儲存EhCache中的資料,預設是false。  
        diskExpiryThreadIntervalSeconds - 磁碟快取的清理執行緒執行間隔,預設是120秒。
        每個120s,相應的執行緒會進行一次EhCache中資料的清理工作  memoryStoreEvictionPolicy 
        - 當記憶體快取達到最大,有新的element加入的時候, 移除快取中element的策略。預設是LRU(最近最少使用),
        可選的有LFU(最不常使用)和FIFO(先進先出) -->

    <defaultCache maxElementsInMemory="1000"
    maxElementsOnDisk="10000000" eternal="false" overflowToDisk="false"
    timeToIdleSeconds="120" timeToLiveSeconds="120"
    diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU">
    </defaultCache>
</ehcache>

5、其他

(1)應用場景:
對於訪問響應速度要求高,但是實時性不高的查詢,可以採用二級快取技術。
注意:在使用二級快取的時候,要設定一下重新整理間隔(cache標籤中有一個flashInterval屬性)來定時重新整理二級快取,這個重新整理間隔根據具體需求來設定,比如設定30分鐘、60分鐘等,單位為毫秒。

(2)侷限性:
Mybatis二級快取對細粒度的資料,快取實現不好。
場景:對商品資訊進行快取,由於商品資訊查詢訪問量大,但是要求使用者每次查詢都是最新的商品資訊,此時如果使用二級快取,就無法實現當一個商品發生變化只重新整理該商品的快取資訊而不重新整理其他商品快取資訊,因為二級快取是mapper級別的,當一個商品的資訊傳送更新,所有的商品資訊快取資料都會清空。
解決此類問題,需要在業務層根據需要對資料有針對性的快取。
比如可以對經常變化的 資料操作單獨放到另一個namespace的mapper中

相關文章