Hibernate5筆記7--Hibernate緩存機制


  Hibernate緩存機制:

  緩存范圍:

    應用程序中根據緩存的范圍,可以將緩存分為三類: 

     (1)事務范圍緩存(單Session,即一級緩存)
      事務范圍的緩存只能被當前事務訪問,每個事務都有各自的緩存。緩存的生命周期依賴於事務的生命周期:當事務結束時,緩存的生命周期也會結束。事務范圍的緩存使用內存作為存儲介質。Hibernate中的一級緩存就屬於事務范圍。

     (2)應用范圍緩存(單SessionFactory,即二級緩存)
      應用范圍的緩存可以被應用程序內的所有事務共享訪問。緩存的生命周期依賴於應用的生命周期,當應用結束時,緩存的生命周期同時結束。應用范圍的緩存可以使用內存或硬盤作為存儲介質。Hibernate的二級緩存就屬於應用范圍。 

     (3)集群范圍緩存(多SessionFactory)
      在集群環境中,緩存被一個機器或多個機器的進程共享,緩存中的數據被復制到集群環境中的每個進程節點,進程間通過遠程通信來保證緩存中的數據的一致性,緩存中的數據通常采用對象的松散數據形式。有些Hibernate的二級緩存第三方插件支持集群范圍緩存。

   (1) 一級緩存:

      一級緩存,就是Session緩存,其實就是內存中的一塊空間,在這個內存空間存放了相互關聯的Java對象。Session緩存是事務級緩存。伴隨着事務的開啟而開啟,伴隨着事務的關閉而關閉。Session緩存由Hibernate進行管理。Session緩存,是Hibernate內置的。是不能被程序員取消的。即,只要使用Hibernate,就一定要使用,更確切地說,就一定在使用Session緩存
      當程序調用Session的load()方法、get()方法、save()方法、saveOrUpdate()方法、update()方法或查詢接口方法時,Hibernate會對實體對象進行緩存。當通過load()或get()方法查詢實體對象時,Hibernate會首先到緩存中查詢,在找不到實體對象的情況下,Hibernate才會發出SQL語句到DB中查詢。從而提高了Hibernate的使用效率。

      一級緩存相關的方法:

        evict(Object o):從Session中刪除指定對象

        clear():無參數,將Session緩存清空

        contains(Object o):判斷指定對象是否在Session中存在

        flush():無參數,將Session中對象狀態同步到DB中

      快照(引用自北京動力節點):

        

        代碼說明:

 1 @Test
 2 public void test01_SQL() {
 3     //1. 獲取Session
 4     Session session = HbnUtils.getSession();
 5     try {
 6         //2. 開啟事務
 7         session.beginTransaction();
 8         //3. 操作
 9         //session.get的時候,hibernate就將數據庫中的數據加載到session緩存中,同時也會備份到快照中
10         Student student = session.get(Student.class, 2);
11         student.setName("n_2");
12         session.update(student);
13         //4. 事務提交
14         //commit是唯一的同步時間點,當程序運行到同步時間點時,
15         //hibernate先判斷session緩存中的數據和快照中的數據是否相同,
16         //如果不相同,則更新數據庫,如果相同,則不更新數據庫,
17         //以上,兩種情況,無論是否寫了update,都成立
18         session.getTransaction().commit();
19     } catch (Exception e) {
20         e.printStackTrace();
21         //5. 事務回滾
22         session.getTransaction().rollback();
23     }
24 }

     Session的刷新與同步:

      Session的刷新是指,Session緩存中的數據的更新。Session的同步是指,將Session緩存中的數據同步更新到DB中。執行同步的時間點只有一個:事務的提交

      當代碼中執行了對Session中現有數據的修改操作,即update()與delete()語句后,Session緩存並不會馬上刷新,即並不會馬上執行update與delete的SQL語句,而是在某個時間點到來時,才會刷新緩存,更新緩存中的數據。刷新的時間點主要有三個:

        (1)執行Query查詢

        (2)執行session.flush()

        (3)執行事務的提交

      通過Session的setFlushMode()方法,可以設置緩存刷新模式:

      

      注意:增刪改操作,當刷新時間點到來時是否馬上進行緩存更新,各自情況還是不同的。

        刪除操作,一到達刷新時間點,馬上執行delete語句,更新Session中數據;

        更新操作,到達刷新時間點后,是否馬上執行update語句,更新Session中數據,還要看該修改內容是否與快照一致若一致,則執行,否則,則不執行;

        插入操作,無需等到刷新時間點的到達,見到save()后馬上執行insert語句。因為插入操作不是修改Session中已經的存在數據,而是給Session中添加數據。

        

   (2) 二級緩存:

      二級緩存是SessionFactory級的緩存,其生命周期與SessionFactory一致。SessionFactory緩存可以依據功能和目的的不同而划分為內置緩存和外置緩存。 
      SessionFactory的內置緩存中存放了映射元數據和預定義SQL語句映射元數據是映射文件中數據的副本,而預定義SQL語句是在Hibernate初始化階段根據映射元數據推導出來的SQL。SessionFactory的內置緩存是只讀的應用程序不能修改緩存中的映射元數據和預定義SQL語句,因此SessionFactory不需要進行內置緩存與映射文件的同步。
      SessionFactory的外置緩存一個可配置的插件。在默認情況下,SessionFactory不會啟用這個插件。外置緩存的數據是數據庫數據的副本外置緩存的介質可以是內存或者硬盤。SessionFactory的外置緩存也被稱為Hibernate的二級緩存
Hibernate本身只提供了二級緩存的規范,但並未實現,故需要第三方緩存產品的支持。常用的二級緩存第三方插件有: 
        EHCache、Memcached、OSCache、SwarmCache、JBossCache等。這些插件的功能各有側重,各有特點。

        

       二級緩存的執行:當Hibernate根據ID訪問數據對象時,首先會從一級緩存Session中查找。若查不到且配置了二級緩存,則會從二級緩存中查找;若還查不到,就會查詢數據庫,把結果按照ID放入到緩存中。執行增、刪、改操作時,會同步更新緩存。

       二級緩存內容分類,根據緩存內容的不同,可以將Hibernate二級緩存分為四類:  

          (1)類緩存:緩存對象為實體類對象  

          (2)集合緩存:緩存對象為集合類對象  

          (3)更新時間戳緩存:

          (4)查詢緩存:緩存對象為查詢結果

        二級緩存的並發訪問策略:

        事務型(transactional):隔離級別最高,對於經常被讀但很少被改的數據,可以采用此策略。因為它可以防止臟讀與不可重復讀的並發問題發生異常的時候,緩存也能夠回滾(系統開銷大)。

        讀寫型(read-write):對於經常被讀但很少被改的數據,可以采用此策略。因為它可以防止臟讀的並發問題更新緩存的時候會鎖定緩存中的數據

        非嚴格讀寫型(nonstrict-read-write):不保證緩存與數據庫中數據的一致性。對於極少被改,並且允許偶爾臟讀的數據,可采用此策略。不鎖定緩存中的數據

        只讀型(read-only):對於從來不會被修改的數據,可使用此策略。

       二級緩存管理相關的方法:

        與二級緩存管理相關的方法,一般都定義在Cache接口中。而Cache對象的獲取,需要通過SessionFactory的getCache()方法:

          Cache cache = sessionFactory.getCache();      

       二級緩存的配置(應用EHCache插件):

        (1)導入ehcache的jar包,略

        (2)在hibernate.cfg.xml中的<session-factory>元素中加入如下內容,開啟二級緩存和注冊二級緩存區域工廠:

1 <!-- 開啟二級緩存 -->
2 <property name="hibernate.cache.use_second_level_cache">
3     true
4 </property>
5 <!-- 注冊二級緩存區域工廠 -->
6 <property name="hibernate.cache.region.factory_class">
7     org.hibernate.cache.ehcache.EhCacheRegionFactory
8 </property>

        (3)解壓EHCache的核心Jar包ehcache-core-2.4.3.jar,將其中的一個配置文件ehcache-failsafe.xml直接放到項目的src目錄下,並更名為ehcache.xml,注意其中的配置可以修改:

 1 <defaultCache
 2         maxElementsInMemory="10000"
 3         eternal="false"
 4         timeToIdleSeconds="120"
 5         timeToLiveSeconds="120"
 6         maxElementsOnDisk="10000000"
 7         diskExpiryThreadIntervalSeconds="120"
 8         memoryStoreEvictionPolicy="LRU">
 9     <persistence strategy="localTempSwap"/>
10 </defaultCache>

        (4)指定緩存內容:

          指定緩存內容,即指定哪個類或哪個集合要進行二級緩存指定的位置有兩處映射文件主配置文件這兩種任選其一即可它們的效果是相同的,但各有利弊: 
            在映射文件中指定緩存內容,查看其類的映射文件時,一眼就可看到類為緩存類,集合為緩存集合。但,弊端是,緩存的指定位置分散,缺乏項目的整體性。 
            在主配置文件中指定緩存內容,可一眼看到整個項目中所有緩存類與緩存集合。但,弊端是,緩存內容的指定與類映射分離。

          映射文件中指定緩存內容:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <!DOCTYPE hibernate-mapping PUBLIC 
 3     "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
 4     "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
 5   
 6 <hibernate-mapping package="com.tongji.beans">
 7     <class name="Country">
 8         <!-- 指定當前類為類緩存對象 -->
 9         <!-- <cache usage="read-only"/> -->
10         <id name="cid">
11             <generator class="native"/>
12         </id>
13         <property name="cname"/>
14         <set name="ministers" cascade="save-update">
15             <!-- 指定當前集合為集合緩存對象 -->
16             <!-- <cache usage="read-only"/> -->
17             <key column="countryId"/>
18             <one-to-many class="Minister"/>
19         </set>
20     </class>
21 </hibernate-mapping>

          在主配置文件中指定緩存內容:在<mapping/>標簽的后面指定類緩存與集合緩存

1 <!-- 指定類緩存 -->
2 <class-cache usage="read-only" class="com.tongji.beans.Minister" />
3 <class-cache usage="read-only" class="com.tongji.beans.Country" />
4 <!-- 指定集合緩存 -->
5 <collection-cache usage="read-only"
6     collection="com.tongji.beans.Country.ministers" />

       補充:

          (1)類緩存,緩存的是類的詳情;集合緩存,在沒有對集合中元素對應的類進行類緩存的時候,緩存的是所有元素的id

          (2)Query查詢緩存,即session.createQuery(hql)時進行緩存。說明以下三點:

            (1)Query查詢的結果也會存放到一、二級緩存中

            (2)Query查詢默認不會從一、二級緩存中讀取數據,但可以改變:

              先在主配置文件中,開啟Query查詢總開關:<property name="hibernate.cache.use_query_cache">true</property>

              查詢時的代碼:

 1 @Test
 2 public void test02() {
 3     //1. 獲取Session
 4     Session session = HbnUtils.getSession();
 5     try {
 6         //2. 開啟事務
 7         session.beginTransaction();
 8         //3. 操作
 9         //第一次查詢
10         String hql = "from Country where cid=1";
11         Country country1 = (Country) session.createQuery(hql).setCacheable(true).uniqueResult();
12         System.out.println("第一次查詢:Country = " + country1);
13         //第二次查詢
14         Country country2 = (Country) session.createQuery(hql).setCacheable(true).uniqueResult();
15         System.out.println("第二次查詢:Country = " + country2);
16         
17         //將一級緩存數據清空
18         session.clear();
19         
20         //第三次查詢,從二級緩存中讀取
21         Country country3 = (Country) session.createQuery(hql).setCacheable(true).uniqueResult();
22         System.out.println("第三次查詢:Country = " + country3);
23         //4. 事務提交
24         session.getTransaction().commit();
25     } catch (Exception e) {
26         e.printStackTrace();
27         //5. 事務回滾
28         session.getTransaction().rollback();
29     }
30 }

          (3)Query查詢要從緩存中讀取數據,必須保證Query所執行的HQL語句完全相同。因為Query查詢,不僅將數據存放到了緩存中,還將HQL語句存放到了緩存中。

          (4)修改時間戳:在二級緩存存放的對象中,比一級緩存中多出一個屬性,updateTimeStamp,修改時間戳。只要這個屬性發生改變,就說明有操作修改了DB中的數據,二級緩存中的該緩存對象已經不是最新數據,需要從DB中再次查詢更新。

            而Query接口的executeUpdate()方法所進行的更新,可以繞過一級緩存,但會修改二級緩存中緩存對象的updateTimeStamp值,由於該值的改變,二級緩存就會通過新的查詢來更新緩存中的數據(一、二級緩存都更新了)。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM