4.7 事 務控 制
每個業務邏輯方法都是由一系列的數據庫訪問完成,這一系列的數據訪問可能會修改多條數據記錄,這系列的修改應該是一個整體,絕不能僅修改其中的幾條。也就是說,多個數據庫原子訪問應該綁定成一個整體——這就是事務。事務是一個最小的邏輯執行單元,整個事務不能分開執行,要么同時執行,要么同時放棄執行。
4.7.1 事務的概念
事務是一步或幾步基本操作組成的邏輯執行單元,這些基本操作作為一個整體執行單元,它們要么全部執行,要么全部取消,絕不能僅僅執行部分。一般而言,每次用戶請求,對應一個業務邏輯方法,一個業務邏輯方法往往具有邏輯上的原子性,應該使用事務。例如,一個轉賬操作,對應修改兩個賬戶的余額,這兩個賬戶的修改要么同時生效,要么同時取消——同時生效是轉賬成功,同時取消是轉賬失敗;但不可只修改其中一個賬戶,那將破壞數據庫的完整性。
通常來講,事務具備如下4個特性:原子性(atomicity)、一致性(consistency)、隔離性(isolation)和持續性(durability)。這4個特性也簡稱為ACID性。
?? ● 原子性:事務是應用中最小執行單位,就如原子是自然界最小顆粒,具有不可再分的特征一樣。事務是應用中不可再分的最小邏輯執行體。
?? ● 一致性:事務執行的結果,必須使數據庫從一個一致性狀態,變到另一個一致性狀態。當數據庫只包含事務成功提交的結果時,數據庫處于一致性狀態。如果系統運行發生中斷,某個事務尚未完成而被迫中斷,而該未完成的事務對數據庫所做的修改已被寫入數據庫,此時,數據庫就處于一種不正確的狀態。比如銀行在兩個賬戶之間轉賬,從A賬戶向B賬戶轉入1000元。系統先減少A賬戶的1000元,然后再為B賬戶增加1000元。如果全部執行成功,數據庫處于一致性狀態。如果僅執行完A賬戶金額的修改,而沒有增加B賬戶的金額,則數據庫就處于不一致性狀態。因此,一致性是通過原子性來保證的。
?? ● 隔離性:各個事務的執行互不干擾,任意一個事務的內部操作對其他并發的事務,都具有隔離性。也即并發執行的事務之間不能互相影響。
?? ● 持續性:持續性也稱為持久性(persistence),指事務一旦提交,對數據所做的任何改變,都要記錄到永久存儲器中,通常保存進物理數據庫。
4.7.2 Hibernate的事務
Hibernate直接使用JDBC連接和JTA資源,不添加任何附加鎖定行為。Hibernate只添加自動版本管理,而不會鎖定內存中的對象,也不會改變數據庫事務的隔離級別。基本上,使用 Hibernate就好像直接使用JDBC(或者JTA/CMT)進行數據庫訪問。
Hibernate中SessionFactory對象的創建代價很高,它是線程安全的對象,被設計成可以為所有的應用程序線程所共享。通常,SessionFactory會在應用程序啟動時創建,一旦創建了SessionFactory將不會輕易關閉,只有當應用關閉時,SessionFactory才會關閉。
而Session的對象是輕量級的,它也是線程不安全的。對于單個業務進程單個工作單元而言,Session只被使用一次。創建Session時,并不會立即打開與數據庫之間的連接,Session只在需要進行數據庫操作時,才會獲取JDBC連接。因此,打開和關閉Session,并不會對性能造成很大的影響。甚至即使無法確定一個請求是否需要數據訪問,也可以打開Session對象,因為如果不進行數據庫訪問,Session不會獲取JDBC連接。
相反,數據庫事務應該盡可能的短。從而,降低數據庫鎖定造成的資源爭用。數據庫長事務會導致應用程序無法承載高并發的負荷。
由上面的介紹可知,Hiberante的Session和事務是緊密相關的,因為事務是通過Session來打開的。那么事務的范圍是多大?單個Session可以跨越多個數據庫事務嗎?事務和Session的對應關系又如何呢?下面將介紹Hibernate Session和事務的關系。
4.7.3 事務和Session
數據庫操作必須在Hibernate的Session管理下進行,但不推薦因為一次簡單的數據庫原子調用,就打開和關閉一次Session,數據庫事務也是如此。因為,對于一次原子操作打開的事務沒有任何意義——事務應該是將多個操作步驟組合成一個邏輯整體。
事務是按順序發送并組成一個邏輯整體的原子操作單元。
注意:也就是說單個的SQL語句發送之后,自動事務提交模式失效了。這種自動提交模式僅為SQL控制臺設計,在實際項目沒有太大的實用價值。Hibernate禁止事務立即自動提交模式,或者讓應用服務器禁止事務自動提交。
通常,建議每個請求對應一個Session。在這種模式下,來自客戶端的請求被發送到服務器端,此處可能對應一個業務邏輯方法。在這個業務邏輯方法內,一個新的Hibernate Session被打開,然后開始事務,在事務內執行這個操作單元中所有的數據庫操作。一旦操作完成,需要發送給客戶端的響應也準備就緒。此時,提交事務,然后關閉Session。在這種模式下,Session和用戶請求是一對一的關系,這是一種理想的Session管理模式。
為了達到這種效果,推薦使用一個ThreadLocal變量,把Session綁定到處理客戶端請求的線程上去。這種方式可以讓運行在該線程上的所有程序代碼輕松地訪問Session。也可以在一個ThreadLocal變量中保持事務上下文環境,不過這依賴于所選擇的數據庫事務劃分機制。這種實現模式被稱之為ThreadLocal Session和Open Session in View。
下面是一個HibernateUtil類,該類將Hibernate Session存放在一個ThreadLocal變量中,對于同一個線程的請求,將可以輕松訪問該Session。
public class HibernateUtil
{
??? public static final SessionFactory sessionFactory;
??? //靜態初始化塊,使用該類時使用該代碼塊
??? static
??? {
??????? try
??????? {
??????????? //采用默認的hibernate.cfg.xml來啟動一個Configuration的實例
??????????? Configuration configuration=new Configuration().configure();
??????????? //由Configuration的實例來創建一個SessionFactory實例
??????????? sessionFactory = configuration.buildSessionFactory();
??????? }
??????? catch (Throwable ex)
??????? {
??????????? System.err.println("初始化sessionFactory失敗." + ex);
??????????? throw new ExceptionInInitializerError(ex);
??????? }
??? }
??? //ThreadLocal是隔離多個線程的數據共享,不存在多個線程之間共享資源,因此不再需要
??? 對線程同步
??? public static final ThreadLocal session = new ThreadLocal();
??? //該方法用于獲取當前線程的Session對象
??? public static Session currentSession() throws HibernateException
??? {
??????? Session s = (Session) session.get();
??????? //如果該線程還沒有Session,則創建一個新的Session
??????? if (s == null)
??????? {
??????????? s = sessionFactory.openSession();
??????????? //將獲得的Session變量存儲在ThreadLocal變量的Session里
??????????? session.set(s);
??????? }
??????? return s;
??? }
??? //該方法用于關閉當前線程里的Session
??? public static void closeSession() throws HibernateException
??? {
??????? Session s = (Session) session.get();
??????? if (s != null)
??????????? s.close();
??????? session.set(null);
??? }
}
在上面的代碼中,Hibernate Session被綁定到當前線程。當調用currentSession方法時,如果當前線程中的Session已經創建出來,那么將返回這個已經存在的Session實例。
每次請求對應一個Session的模式不僅可以用于設計操作單元,甚至很多業務處理流程都需要組合一系列的用戶操作,即用戶對數據庫的交叉訪問。
但是,對于企業應用,跨用戶交互的數據庫事務是無法接受的。例如,在第一個頁面,用戶打開對話框,打開一個特定Session裝入的數據,可以隨意修改對話框中的數據,修改完成后,將修改結果存入數據庫。
從用戶的角度來看,這個操作單元被稱為應用程序長事務。在一個J2EE應用實現中,可以有很多方法來實現這種應用程序長事務。
一個比較差的做法是,當用戶思考時,應用程序保持Session和數據庫事務是打開的,并保持數據庫鎖定,以阻止并發修改,從而保證數據庫事務隔離級別和原子操作。這種數據庫鎖定會導致應用程序無法擴展并發用戶的數目。
因此,不要使用每個應用對應一次Hibernate Session的模式,也不要使用每次Http Session對應一次Hibernate Session的模式。
注意:幾乎所有情況下,都不要使用每個應用對應一次Hibernate Session的模式,也不要使用每次Http Session對應一次Hibernate Session的模式。
對于這種情況,Hibernate主要有如下兩種模式來解決這個問題:
?? ● 脫管對象,如果采用每次用戶請求對應一次Session的模式。那么,前面載入的實例在用戶思考的過程中,始終與Session脫離,處于脫管狀態。都處于與Session脫離的狀態。Hibernate允許把脫管對象重新關聯到Session上,并且對修改進行持久化。在這種模式下,自動版本化被用來隔離并發修改。這種模式也被稱為使用脫管對象的每個請求對應一個Hibernate Session。
?? ● 長生命周期Session,Session可以在數據庫事務提交之后,斷開和底層的JDBC連接。當新的客戶端請求到來時,它又重新連接上底層的JDBC連接。這種模式被稱為每個應用程序事務對應一個Session,因為應用程序事務相當長(跨越多個用戶請求),所以也被稱為每次應用事務對應一個Hibernate Session。
posted on 2009-07-19 09:11
jadmin 閱讀(85)
評論(0) 編輯 收藏