Persistence Context


每個持久物件都會與一個Persistence Context發生關聯,它可以管理持久物件的快取、dirty check、flush等,牽涉著持久層的效能議題。

所有在Persistence狀態的物件在Persistence Context中會有一個複製品,用於與持久物件進行比較,確定狀態是否有所改變,也就是執行所謂的dirty check。由於所有Persistence狀態的物件都會在Persistence Context有一個複製品,而且他不會被自動移除,所以若在一個Session中載入過多的物件,將會導致記憶體用盡而得到 OutOfMemoryException。

關於Persistence Context的快取管理,可以參考 簡 介快取(Session Level)

持久物件必須與資料庫進行同步,因此對於Persistence Context中所有的變動,最終都必須flush,對於持久物件的變動並不是馬上要求資料庫也進行相對應的變動,以減少對資料庫的請求次數,減少IO方 面的效能問題,例如你可能多次對持久物件的狀態進行變更,但最後flush時,只需要一次的update語句。

預設是在以下的幾個狀況會進行flush:
  • Transaction進行commit
  • 下一個查詢執行之前
  • 主動呼叫Session的flush()方法

在下一次的查詢之前,Hibernate會檢查持久物件目前的狀態是否影響下一次的查詢結果,如果沒有變更,則不會先行flush,只有在有變更的情況下,才會先flush再進行查詢。

以上預設會進行flush行為,是Hibernate的預設行為,您可以使用Session的setFlushMode()來改變,預設是 FlushMode.AUTO,這對於經常進行更新、查詢、更新、查詢這種重複動作的操作,將是否flush的檢查工作交給Hibernate來作,是有益的。


如果您設定為FlushMode.COMMIT,則在下一次查詢之前,Hibernate就不會主動檢查物件狀態並確定是 否要flush,只有在你主動commit一個Transaction,才會進行flush,您也可以設定FlushMode.MANUAL,如此設定之 後,則您必須明確呼叫Session的flush()方法,才會進行flush。

主動呼叫Session的flush()方法時機之一為,當Session在使用save()儲存物件時,會將要儲存的物件納入Session level快取管理,在進行大量數據儲存時,快取中的實例大量增加,最後會導致OutOfMemoryError,可以主動每隔一段時間使用Session的flush()強制儲存物件,並使用clear()清除快取,例如:
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();

while(....) { // 大量載入物件時的迴圈示意
    ....
    session.save(someObject);
    if(count % 100 == 0) { // 每100筆資料
        session.flush(); // 送入資料庫
        session.clear(); // 清除快取
    }
}

tx.commit();
session.close();

在SQL Server、Oracle等資料庫中,可以在Hibernate設定檔中設定屬性hibernate.jdbc.batch_size來控制每多少筆資 料就送至資料庫,例如:
....
<hibernate-configuration>
    <session-factory>
        ....
        <property name="hibernate.jdbc.batch_size">100</property>
        ....
    </session-factory>
<hibernate-configuration>

在MySQL中則不支援這個功能。