1. 程式人生 > >Hibernate二級快取

Hibernate二級快取

Hibernate中沒有自己去實現二級快取,而是利用第三方的。簡單敘述一下配置過程,也作為自己以後用到的時候配置的一個參考。

1、我們需要加入額外的二級快取包,例如EHcache,將其包匯入。需要:ehcache-core-2.4.3.jar , hibernate-ehcache-4.2.4.Final.jar ,slf4j-api-1.6.1.jar

2、在hibernate.cfg.xml配置檔案中配置我們二級快取的一些屬性(此處針對的是Hibernate4):

<!-- 啟用二級快取 -->
<property name="cache.use_second_level_cache">true</property>
<!-- 配置使用的二級快取的產品 -->
<property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>

3、我們使用的是EHcache,所以我們需要建立一個ehcache.xml的配置檔案,來配置我們的快取資訊,這個是EHcache要求的。該檔案放到根目錄下。

<ehcache>
    <!--  
        指定一個目錄:當 EHCache 把資料寫到硬碟上時, 將把資料寫到這個目錄下.
    -->
    <diskStore path="d:\\tempDirectory"/>
    <!--Default Cache configuration. These will applied to caches programmatically created through
        the CacheManager.
        The following attributes are required for defaultCache:
        maxInMemory       - Sets the maximum number of objects that will be created in memory
        eternal           - Sets whether elements are eternal. If eternal,  timeouts are ignored and the element
                            is never expired.
        timeToIdleSeconds - Sets the time to idle for an element before it expires. Is only used
                            if the element is not eternal. Idle time is now - last accessed time
        timeToLiveSeconds - Sets the time to live for an element before it expires. Is only used
                            if the element is not eternal. TTL is now - creation time
        overflowToDisk    - Sets whether elements can overflow to disk when the in-memory cache
                            has reached the maxInMemory limit.
        -->
    <!--  
        設定快取的預設資料過期策略
    -->
    <defaultCache
        maxElementsInMemory="10000"
        eternal="false"
        timeToIdleSeconds="120"
        timeToLiveSeconds="120"
        overflowToDisk="true"
        />
    <!--  
        設定具體的命名快取的資料過期策略。每個命名快取代表一個快取區域
        快取區域(region):一個具有名稱的快取塊,可以給每一個快取塊設定不同的快取策略。
        如果沒有設定任何的快取區域,則所有被快取的物件,都將使用預設的快取策略。即:<defaultCache.../>
        Hibernate 在不同的快取區域儲存不同的類/集合。
            對於類而言,區域的名稱是類名。如:com.atguigu.domain.Customer
            對於集合而言,區域的名稱是類名加屬性名。如com.atguigu.domain.Customer.orders
    -->
    <!--  
        name: 設定快取的名字,它的取值為類的全限定名或類的集合的名字
        maxElementsInMemory: 設定基於記憶體的快取中可存放的物件最大數目

        eternal: 設定物件是否為永久的, true表示永不過期,
        此時將忽略timeToIdleSeconds 和 timeToLiveSeconds屬性; 預設值是false
        timeToIdleSeconds:設定物件空閒最長時間,以秒為單位, 超過這個時間,物件過期。
        當物件過期時,EHCache會把它從快取中清除。如果此值為0,表示物件可以無限期地處於空閒狀態。
        timeToLiveSeconds:設定物件生存最長時間,超過這個時間,物件過期。
        如果此值為0,表示物件可以無限期地存在於快取中. 該屬性值必須大於或等於 timeToIdleSeconds 屬性值

        overflowToDisk:設定基於記憶體的快取中的物件數目達到上限後,是否把溢位的物件寫到基於硬碟的快取中
    -->
    <cache name="com.atguigu.hibernate.entities.Employee"
        maxElementsInMemory="1"
        eternal="false"
        timeToIdleSeconds="300"
        timeToLiveSeconds="600"
        overflowToDisk="true"
        />

    <cache name="com.atguigu.hibernate.entities.Department.emps"
        maxElementsInMemory="1000"
        eternal="true"
        timeToIdleSeconds="0"
        timeToLiveSeconds="0"
        overflowToDisk="false"
        />

</ehcache>

4、開啟二級快取。我們在這裡使用的xml的配置方式,所以要在Customer.hbm.xml檔案加一點配置資訊:

<cache usage="read-only"/>

注意是在標籤內。 如果是使用註解的方法,在要在Customer這個類中,加入@Cache(usage=CacheConcurrencyStrategy.READ_ONLY)這個註解。

5、下面我們再進行一下測試。還是上面的程式碼:

@Test
public void test() {
    Customer customer1 = (Customer) session.load(Customer.class, 1);
    System.out.println(customer1.getCustomerName());
    transaction.commit();
    session.close();
  session = sessionFactory.openSession();
    transaction = session.beginTransaction();
    Customer customer2 = (Customer) session.load(Customer.class, 1);
    System.out.println(customer2.getCustomerName());
}

我們可以發現控制檯只發出了一條SQL語句。這是我們二級快取的一個小Demo。

我們的二級快取是sessionFactory級別的,所以當我們session關閉再開啟之後,我們再去查詢物件的時候,此時Hibernate會先去二級快取中查詢是否有該物件。

同樣,二級快取快取的是物件,如果我們查詢的是物件的一些屬性,則不會加入到快取

我們通過二級快取是可以解決之前提到的N+1問題。

已經寫了這麼多了,但好像我們關於快取的內容還沒有講完。不要著急,再堅持一下,我們的內容不多了。我們還是通過一個例子來引出下一個話題。 我們說通過二級快取可以快取物件,那麼我們看一下下面的程式碼以及輸出結果:

@Test
public void test() {
    List<Customer> customers1 = session.createQuery("from Customer").list();
    System.out.println(customers1.size());
    tansaction.commit();
    session.close();
    session = sessionFactory.openSession();
    transaction = session.beginTransaction();
    List<Customer> customers2 = session.createQuery("from Customer").list();
    System.out.println(customers2.size());
}

會發現控制檯有兩條SQL語句輸出,我們的快取好像沒有起作用哎?這是為啥?當我們通過list()去查詢兩次物件的時候,二級快取雖然會快取插敘出來的物件,但不會快取我們的hql查詢語句,要想解決這個問題,我們需要用到查詢快取。

查詢快取

在前文中也提到了,我們的一級二級快取都是對整個實體進行快取,它不會快取普通屬性,如果想對普通屬性進行快取,則可以考慮使用查詢快取。

但需要注意的是,大部分情況下,查詢快取並不能提高應用程式的效能,甚至反而會降低應用效能,因此實際專案中要謹慎的使用查詢快取。

對於查詢快取來說,它快取的key就是查詢所用的HQL或者SQL語句,需要指出的是:查詢快取不僅要求所使用的HQL、SQL語句相同,甚至要求所傳入的引數也相同,Hibernate才能直接從快取中取得資料。只有經常使用相同的查詢語句、並且使用相同查詢引數才能通過查詢快取獲得好處,查詢快取的生命週期直到屬性被修改了為止。

查詢快取預設是關閉。要想使用查詢快取,只需要在hibernate.cfg.xml中加入一條配置即可:

<property name="hibernate.cache.use_query_cache">true</property>

而且,我們在查詢hql語句時,要想使用查詢快取,就需要在語句中設定這樣一個方法:setCacheable(true)

但需要注意的是,我們在開啟查詢快取的時候,也應該開啟二級快取。因為如果不使用二級快取,也有可能出現N+1的問題。

這是因為查詢快取快取的僅僅是物件的ID,所以首先會通過一條SQL將物件的ID都查詢出來,但是當我們後面要得到每個物件的資訊的時候,此時又會發送SQL語句,所以如果我們使用查詢快取,一定也要開啟二級快取。