1. 程式人生 > >(五)Hibernate一級緩存

(五)Hibernate一級緩存

性能 策略 引用 生命周期 臨時對象 一次 cto select session

一、簡介

緩存,介於應用程序和永久數據存儲源之間,作用是為了降低應用程序對物理數據源訪問的頻率,從而提高應用的運行性能。
例如我們cpu執行效率每秒處理的數據高達上千兆,而我們的硬盤讀取速度卻沒那麽高,讀取幾百兆,這時候我們使用緩存來存儲數據,存儲滿後一次性交由cpu處理。

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

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

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

二、緩存範圍

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

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

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

三、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語句對數據庫進行更新。

(1)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語句,在控制臺打印。

(2)什麽時候會清理緩存呢?

-默認情況下,在調用commit()方法時會先清理緩存。再向數據庫提交事務。 

-當執行查詢操作時,如果緩存中的持久化對象屬性已經發生了改變,就會先清理緩存,同步數據,保證查詢到的是正確的結果。 

-當應用程序顯式調用Session的flush()方法時  

-Session清理緩存的例外情況,如果對象使用的是native生成策略生成OID,那麽調用Session的save()方法來保存該對象時,會立刻執行向數據庫的插入語句。

  

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

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

例如Session.setFlushMode(FlushMode.AUTO) 

  

 

五、Session接口的其他API

--Session的save()和persist()方法
        兩個方法都是用來保存對象,能把臨時狀態轉變為持久化狀態。 

    兩個方法的區別在於:
    save方法持久化對象時,會返回持久化對象的OID。所以程序執行save時會立刻執行insert語句,來返回數據庫生成的OID。 

    persist方法持久化對象時,不會保證立即為持久化對象的OID賦值,不會立即生成insert語句,而是有可能在Session清理緩存時才為OID賦值。


--Session的clear()方法
    清空一級緩存 

--Session的update方法
                    update()方法可以將遊離對象轉變為持久化對象。用來執行修改操作。
                    update()方法完成以下操作
                    -把遊離對象加入到當前緩存中,變為持久化對象
                    -然後計劃執行update語句

                    只要通過update使遊離對象轉變為持久化對象,即使沒有修改任何屬性,在清理緩存時還是會執行update語句。
                    如果希望Session僅當修改了屬性時才執行update語句,可以在映射文件中的<class>元素中設置select-before-update="true",默認為false
                    這樣當Session清理緩存時,會先發送一條查詢語句,然後判斷緩存中的對象和記錄是否一致,不一致才執行update語句。

                    當update()方法將遊離對象轉變為持久化對象時,如果Session緩存中已經存在相同的OID持久化對象,那麽會拋出異常。
                    例如:
                        Transaction ts=session.beginTransaction();
                        User user1=session.get(User.class, 5);
                        session.evict(user1);
                        User user2=session.get(User.class, 5);
                        session.update(user1);
                        ts.commit();

                        因為Session的緩存是一個Map結構,OID為key,對象為value。
                        當執行session的update方法時,由於緩存中已經存在了OID為5的持久化對象,因此會拋出異常。 

-Session的saveOrUpdate()方法 

            Session的saveOrUpdate()方法同時包含了save()和update()方法的功能
            如果傳入的參數是臨時對象(OID為null),就調用save方法。
            如果傳入的參數是遊離對象(OID不為null),就執行update方法。

  

 

  

  

(五)Hibernate一級緩存