Hibernate的事務( Transaction對象 )通過Session的beginTransaction( )方法顯式開啟,Hibernate自身並不提供事務控制行為(沒有添加任何附加鎖定行為),Hibernate底層直接使用JDBC串連、JTA資源或其他資源的事務。
Hibernate只是對底層事務進行了抽象,讓應用程式可以直接面向Hibernate事務編程,從而將應用程式和JDBC串連、JTA資源或其他事務資源隔離開了。從編程角度來看,Hibernate的事務由Session對象開啟;從底層實現來看,Hibernate的事務由 TransactionFactory 的執行個體來產生。
TransactionFactory是一個事務工廠的介面,Hibernate為不同的事務環境提供了不同的實作類別。如CMTTransactionFactory是針對容器管理事務環境的實作類別、JDBCTransactionFactory是針對JDBC局部事務環境的實作類別、JTATransactionFactory是針對JTA全域事務環境的實作類別。
應用程式編程無須手動操作TransactionFactory產生事務,這是因為SessionFactory底層已經封裝了TransactionFactory。SessionFactory對象的建立代價很高,它是安全執行緒的對象,被設計成可以被所有線程所共用。通常SessionFactory會在應用程式啟動時建立,一旦建立了SessionFactory將不會輕易關閉,只有當應用退出時才關閉SessionFactory。
Session對象是輕量級的,它是線程不安全的。對於單個業務進程,單個的工作單元而言,Session只被使用一次。建立Session時,並不會立即開啟與資料庫的串連,只有需要進行資料庫操作時,Session才會擷取JDBC串連。因此開啟和關閉Session,並不會對效能造成很大的影響。甚至即使無法確定一個請求是否需要資料訪問,也可以開啟Session對象,因為如果不進行資料庫訪問,Session不會擷取JDBC串連。
由此可見,長Session應用效能影響並不大------只要它沒有長時間開啟資料庫連接。
相反,資料庫事務應該儘可能地短,從而降低資料庫鎖定造成的資源爭用。資料庫長事務會導致應用程式無法承載高並發的負荷。
Hibernate的所有持久化訪問都必須在Session的管理下進行,但並不推薦因為一次簡單的資料庫原子調用,就開啟和關閉一次Session,資料庫事務也是如此。因為對於一次原子操作,開啟事務沒有任何意義------事務應該是將多個操作步驟組合成一個邏輯整體。
Hibernate建議採用每個請求對應一次Session的模式------一次請求通常表示需要執行一個完整的業務功能,這個功能由系列的資料庫原子操作組成,而且它們應該是一個邏輯上的整體。
每個請求對應一個Session的模式,不僅可以用於設計操作單元,甚至,很多業務處理流程都需要組合一系列的使用者操作,即使用者對資料庫的交叉訪問。
需要指出的是,對於企業級應用,跨使用者互動的資料庫事務是無法接受的。例如:在第一個頁面,使用者開啟對話方塊,使用者開啟一個特定Session裝入的資料,使用者可以隨意修改對話方塊中的資料,修改完成後,使用者將修改結果存入資料庫。
從使用者的角度來看,這個操作單元被稱為應用程式長事務。在一個JavaEE應用實現中,可以有很多方法來實現這種應用程式長事務。
一個比價差的做法就是在使用者與伺服器會話(通常就是一次HTTP Session)期間,應用程式一直保持Session與資料庫事務開啟,當使用者思考時,應用程式保持Session和資料庫事務是開啟的,並保持資料庫鎖定,以阻止並發修改,從而保證資料庫交易隔離等級和原子操作。這種資料庫鎖定會導致應用程式無法擴充並發使用者的數量。每次HTTP Session對應一次Hibernate Session的模式會導致應用程式無法擴充並發使用者的數量,因此不推薦使用。
但實際應用中經常需要面對這種應用程式長事務,對於這種情況,Hibernate主要有如下三種模式來解決這個問題:
① 自動版本化:Hibernate能夠自動進行開放式並行存取控制,如果在使用者思考的過程中持久化實體發生並發修改,Hibernate能夠自動檢測到。
② 託管對象:如果採用每次使用者請求對應一個Session的模式,那麼前面載入的執行個體在使用者思考的過程中,始終與Session脫離,處於脫管狀態。Hibernate允許把脫管對象重新關聯到Session上,並且對修改進行持久化。在這種模式下,自動版本化被用來隔離並發修改。這種模式也被稱為使用脫管對象的每次請求對應一個Hibernate Session。
③ 長生命週期Session:Session可以在資料庫事務提交之後,斷開和底層的JDBC串連。當新的用戶端請求到來時,它又重新串連上底層的JDBC串連。這種模式被稱為每個應用程式事務對應一個Session。因為應用程式事務是相當長(跨越多個使用者請求)的,所以也被稱為長生命週期Session。
Session緩衝了處於持久化狀態的每個對象(Hibernate會監視和檢查髒資料),也就是說,如果程式讓Session開啟很長一段時間,或者載入了過多的資料,Session佔用的記憶體會一直增長,直到拋出OutOfMemoryException異常。為瞭解決這個問題,程式定期調用Session的clear( )和evict( )方法來管理Session的緩衝。對於一些大批量的資料處理,推薦使用DML風格的HQL陳述式完成。
如果在Session範圍之外訪問未初始化的集合或代理(由Hibernate的消極式載入特性所引起),Hibernate將會拋出LazyInitializationException異常。也就是說,在脫管狀態下,訪問一個實體所擁有的集合,或者訪問其指向代理的屬性時,都將引發此異常。
為了保證在Session關閉之前初始化代理屬性或集合屬性,程式可以強行調用teacher.getName( )或teacher.getStudents( ).size( )之類的方法來實現,但這樣代碼具有較差的可讀性。除此之外,也可使用Hibernate的initialize( Obejct proxy)靜態方法來強制初始化某個集合或代理。只要Session處於open狀態,Hibernate.initialize(teacher)將會強制初始化teacher代理,Hibernate.initialize(teacher.getStudents(
))對students集合具有同樣的功能。
還有另外一種選擇,就是程式讓Session一直處於開啟狀態,直到裝入所有需要的集合或代理。在某些應用架構中,特別是對於那些需要使用Hibernate進行資料訪問的代碼,以及那些需要在不同應用程式層和不同進程中使用Hibernate的應用,如何保證Session處於開啟狀態也是一個問題。通常有兩種方法可以解決此問題:
① 在一個web應用中,可以利用過濾器Filter,在使用者請求結束、頁面產生結束時關閉Session。也就是保證在視圖顯示層一直開啟Session,這就是所謂的Open Session in View模式。當然採用這種模式時,必須保證所有異常得到正確處理,在呈現視圖介面之前,或在產生視圖介面的過程中發生異常時,必須保證可以正確關閉Session,並結束事務。
② 使用商務邏輯層來負責準備資料,在商務邏輯層返回資料之前,商務邏輯層對每個所需集合調用Hibernate.initialize()方法,或者使用帶fetch子句或FetchMode.JOIN的查詢,事先取得所有資料,並將這些資料封裝成VO(值對象)集合,然後程式可以關閉Session了。商務邏輯層將VO集傳入視圖層,讓視圖層只負責簡單的顯示邏輯。在這種模式下,可以讓視圖層和Hibernate API 徹底分離,保證視圖層不會出現持久層API,從而提供更好的解耦。