1. 程式人生 > >hibernate學習(緩存)

hibernate學習(緩存)

app inf 三方 mod 圖片 數據庫 自動 demo .com

緩存,介於應用程序和永久數據存儲源之間,作用是為了降低應用程序對物理數據源訪問的頻率,從而提高應用的運行性能。

例如我們cpu執行效率每秒處理的數據高達上千兆,而我們的硬盤讀取速度卻沒那麽高,讀取幾百兆,這時候我們使用緩存來存儲數據,存儲滿後一次性交由cpu處理。

Hibernate中的緩存,同樣是為了提高效率。Hibernate的緩存包括Session的緩存和SessionFactory的緩存。

Session的緩存是內置的,不能被卸載,也被稱為Hibertnate的一級緩存。

SessionFactory有一個內置緩存和外置緩存。SessionFactory的外置緩存是一個可配置的緩存插件。默認情況下,Hibernate不會啟用這個緩存插件。被稱為Hibernate的二級緩存。

緩存的範圍

緩存的範圍決定了緩存的生命周期以及可以被誰訪問。

事務範圍:緩存只能被當前事務訪問。一級緩存是Session的緩存,Session對象生命周期通常對應一個事務,因此是事務範圍的緩存。

進程範圍:緩存被進程內的所有事務共享。二級緩存是可配置的緩存插件,由SessionFactory管理,SessionFactory生命周期和應用程序的進程對應,因此是進程範圍的緩存。

集群範圍:在集群環境中,緩存被同一個機器或者多個機器上的多個進程共享。

Hibernate一級緩存:Session緩存

Session緩存是Hibernate的一級緩存。Session對象中具有一個緩存。Session的緩存是一塊內存空間,存放的是持久化對象。
當Session通過save方法持久化一個對象時,該對象被加入到Session緩存中。
當Session通過get方法獲取一個持久化對象時,Session會先判斷Session緩存中是否存在這個對象,如果存在,就不需要再從數據庫中查找。

我們來測試一下緩存的存在:

     //開啟事務
        Transaction ts=session.beginTransaction();
        //加上斷點,當我們執行完這一步,會打印select語句,而後面的都不會打印,說明並沒有從數據庫中獲取
        User user1=session.get(User.class, 5);   
        //這次get方法會先從session緩存中查找,由於已經存在,直接返回引用 
        User user2=session.get(User.class, 5);
        User user3=session.get(User.class
, 5); System.out.println(user1==user2);//true System.out.println(user1==user3);//true session.close();

臟檢查及清理緩存的機制

我們先來看下面的例子

     Transaction ts=session.beginTransaction();
        User user1=session.get(User.class, 5);
        user1.setName("swaggy");
        ts.commit();

我們發現我們改變了Name屬性,這時候session緩存中的對象的name屬性和數據庫表中的NAME字段不一致了。但是我們並沒有進行更新操作,而是直接提交了事務。
幸運的是,Session中在清理緩存的時候,會自動進行臟檢查。如果發現Session緩存中的持久化對象和數據庫中的記錄不一致,就會根據對象的最新屬性去更新數據庫。
所以在本例中,Session會自動提交一個update語句對數據庫進行更新。

Session是怎樣進行臟檢查的呢?

當一個對象被加入到Sesion緩存中時,Session會為該對象復制一份快照。當Session清理緩存時,會比較當前對象的屬性和快照來判斷是否發生變化,如果發生變化,就會根據最新屬性來執行相關的更新操作。

我們看下面一個例子加深對快照的理解

        //我們從數據庫中取出 id為5,name為tom,password為123456的對象
        Transaction ts=session.beginTransaction();
        User user1=session.get(User.class, 5);
        session.update(user1);
        session.close();         
過程:獲取了持久化對象,放入緩存中,並創建了快照,我們執行更新,Session緩存中的對象會和快照進行比較,沒有任何變化,所以不會執行update語句。
        //我們自己設置一個和數據庫中一模一樣的對象,這時候會打印update語句
        Transaction ts=session.beginTransaction();
        User user=new User();
        user.setId(5);
        user.setName("tom");
        user.setPassword("123456");
        session.update(user);
        ts.commit();
        session.close();

過程:因為此時我們執行update語句時會將對象直接放入緩存中,但是沒有持久化對象的快照,所以進行對比結果就是不一致,所以盡管什麽都沒更改,還是會執行update語句,在控制臺打印。

什麽時候會清理緩存呢?

  • -默認情況下,在調用commit()方法時會先清理緩存。再向數據庫提交事務。
  • -當執行查詢操作時,如果緩存中的持久化對象屬性已經發生了改變,就會先清理緩存,同步數據,保證查詢到的是正確的結果。
  • -當應用程序顯式調用Session的flush()方法時
  • -Session清理緩存的例外情況,如果對象使用的是native生成策略生成OID,那麽調用Session的save()方法來保存該對象時,會立刻執行向數據庫的插入語句。

如果不希望Session在以上默認的時間點清理緩存,可以通過Session的setFlushMode()方法來設定清理緩存的時間點。
FlushMode類定義了三種清理模式。

                            各種查詢方法          commit()方法        flush()方法 
-FlushMode.AUTO(默認模式)        清理                 清理                清理
-FlushMode.COMMIT              不清理                清理                清理 
-FlushMode.NEVER               不清理                不清理              清理   

例如Session.setFlushMode(FlushMode.AUTO) 

Hibernate的二級緩存:SessionFactory

二級緩存簡介

二級緩存是一個可插拔的緩存插件,由SessionFactory管理,是進程範圍的緩存。
二級緩存有可能出現並發問題,因此需要采用適當的並發訪問策略。
該策略為緩存中的數據提供了事務隔離級別。
Hibernate還提供了查詢緩存,依賴於二級緩存。

二級緩存中存放什麽?

符合以下條件的數據適合存放在二級緩存中

  • -很少被修改的數據
  • -不是很重要的數據,允許偶然出現的並發問題
  • -參考數據(指供應用程序參考的常量數據)

以下數據不適合存放到二級緩存中

  • -經常被修改的數據
  • -財務數據,絕對不允許出現並發文日
  • -與其他應用共享的數據

二級緩存中緩存的並不是對象,而是對象的散裝數據。

常用二級緩存插件

二級緩存是可配置的插件,Hibernate允許選用以下的緩存插件

  • -EHCache:可作為進程範圍內的緩存。存放數據的物理介質可以是硬盤或者內存,支持hibernate的查詢緩存。
  • -OSCache:可作為進程範圍內的緩存,存放數據的物理介質可以是硬盤或者內存,支持hibernate的查詢緩存,提供了豐富的緩存數據過期策略。
  • -SwarmCache:可作為集群範圍內的緩存,不支持Hibernate的查詢緩存。
  • -JBossCache:可作為集群範圍內的緩存,支持事務並發訪問策略。支持Hibernate的查詢緩存。

二級緩存的事務隔離級別

    transactional(事務型):
        僅在受管理的環境中適用
        提供Repeatable Read事務隔離級別
        適用經常被讀,很少修改的數據
        可以防止臟讀和不可重復讀的並發問題
        緩存支持事務,發生異常的時候,緩存也能夠回滾
    read-write(讀寫型);
        提供Read Committed事務隔離級別
        在非集群的環境中適用
        適用經常被讀,很少修改的數據
        可以防止臟讀
        更新緩存的時候會鎖定緩存中的數據
    nonstrict-read-write(非嚴格讀寫型):
        適用極少被修改,偶爾允許臟讀的數據(兩個事務同時修改數據的情況很少見)
        不保證緩存和數據庫中數據的一致性
        為緩存數據設置很短的過期時間,從而盡量避免臟讀
        不鎖定緩存中的數據
    read-only(只讀型):
        適用從來不會被修改的數據(如參考數據)
        在此模式下,如果對數據進行更新操作,會有異常
        事務隔離級別低,並發性能高
        在集群環境中也能完美運作

技術分享圖片

為了把這些第三方緩存插件集成到Hibernate中,Hibernate提供了org.hibernate.cache.CacheProvider接口
它是緩存插件與Hibernate之間的適配器。Hibernate為以上四個緩存插件提供了內置的適配器實現類。
如果需要使用其他的緩存插件,只需要為這個插件提供實現了接口的類即可。

使用二級緩存

配置二級緩存
1.打開二級緩存
2.選擇需要使用的二級緩存的持久化類,設置二級緩存的並發訪問策略。
3.選擇合適的緩存插件,配置緩存插件的配置文件。

我們演示使用EHCache插件

(1)先導包
(2)在hibernate.cfg.xml中配置使用二級緩存

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

(3)配置使用EHcache的實現類

<property name="hibernate.cache.region.factory_class">org.hibernate.cache.EhCacheRegionFactory</property>

Hibernate允許配置類和集合上設置二級緩存。還可以設置查詢緩存。

(一)在類上設置二級緩存

  在hibernate.cfg.xml的<mapping>元素後面配置

<!--usage設置隔離級別,class設置哪個類-->
        <class-cache usage="read-only" class="com.cad.domain.Customer"/> 

  測試一下是否對象存到了二級緩存

 public class Demo {

            private Session session;

            @Test
            public void test() {
                //讀取配置文件
                Configuration conf=new Configuration().configure();

                //根據配置創建factory
                SessionFactory sessionfactory=conf.buildSessionFactory();
                session = sessionfactory.openSession(); 
                 Transaction ts=session.beginTransaction(); 
                 //獲取對象,打印select語句
                 Customer c1=session.get(Customer.class, 7);
                 //清除一級緩存
                 session.clear(); 
                 //再獲取對象,沒有打印select語句,說明對象存放在了二級緩存中
                 Customer c2=session.get(Customer.class, 7);
                ts.commit();
                session.close();
                sessionfactory.close();
            }

        } 

(二) 在集合上設置二級緩存區

要把集合中的對象也給設置二級緩存區。

      <class-cache usage="read-only" class="com.cad.domain.Customer"/>
          <class-cache usage="read-only" class="com.cad.domain.Order"/> 
          <!--collection設置對象中的集合-->
          <collection-cache usage="read-only" collection="com.cad.domain.Customer.orders"/> 

測試一下

public class Demo {

                private Session session;

                @Test
                public void test() {
                    //讀取配置文件
                    Configuration conf=new Configuration().configure();

                    //根據配置創建factory
                    SessionFactory sessionfactory=conf.buildSessionFactory();
                    session = sessionfactory.openSession(); 
                     Transaction ts=session.beginTransaction(); 
                     //打印select語句
                     Customer c1=session.get(Customer.class, 7);
                     for(com.cad.domain.Order o:c1.getOrders()){
                         System.out.println(o.getName());
                     } 
                     //清空緩沖區
                     session.clear(); 
                     //再查找,不打印,說明集合中的對象都被放到了二級緩存中
                     Customer c2=session.get(Customer.class, 7);
                     for(com.cad.domain.Order o:c2.getOrders()){
                         System.out.println(o.getName());
                     }
                    ts.commit();
                    session.close();
                    sessionfactory.close();
                }

            }

hibernate學習(緩存)