麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 學院 > 開發設計 > 正文

Hibernate的事務和并發

2019-11-18 13:55:23
字體:
來源:轉載
供稿:網友
Hibernate的事務和并發控制很輕易把握。Hibernate直接使用JDBC連接和JTA資源,不添加任何附加鎖定 行為。我們強烈推薦你花點時間了解JDBC編程,ANSI SQL查詢語言和你使用 的數據庫系統的事務隔離規范。Hibernate只添加自動版本治理,而不會鎖 定內存中的對象,也不會改變數據庫事務的隔離級別。基本上,使用 Hibernate就似乎直接使用JDBC(或者JTA/CMT)來訪問你的數據庫資源。





 除了自動版本治理,針對行級悲觀鎖定,Hibernate也提供了輔助的API,它使用了 SELECT FOR UPDATE的SQL語法。本章后面會討論這個API。
我們從Configuration層、sessionFactory層, 和 Session層開始討論Hibernate的并行控制、數據庫事務和應用 程序的長事務。

12.1.Session和事務范圍(transaction scopes)
一個SessionFactory對象的創建代價很昂貴,它是線程安全的對象,它被設計成可以 為所有的應用程序線程所共享。它只創建一次,通常是在應用程序啟動的時候,由一個 Configuraion的實例來創建。
一個Session的對象是輕型的,非線程安全的,對于單個業務進程,單個的 工作單元而言,它只被使用一次,然后就丟棄。只有在需要的時候,Session 才會獲取一個JDBC的Connection(或一個Datasource) 對象。所以你可以放心的打開和關閉Session,甚至當你并不確定一個特定的請 求是否需要數據訪問時,你也可以這樣做。(一旦你實現下面提到的使用了請求攔截的模式,這就 變得很重要了。
此外我們還要考慮數據庫事務。數據庫事務應該盡可能的短,降低數據庫鎖定造成的資源爭用。 數據庫長事務會導致你的應用程序無法擴展到高的并發負載。
一個操作單元(Unit of work)的范圍是多大?單個的Hibernate Session能跨越多個 數據庫事務嗎?還是一個Session的作用范圍對應一個數據庫事務的范圍?應該何時打開 Session,何時關閉Session?,你又如何劃分數據庫事務的邊界呢?

12.1.1.操作單元(Unit of work)
首先,別再用session-per-Operation這種反模式了,也就是說,在單個線程中, 不要因為一次簡單的數據庫調用,就打開和關閉一次Session!數據庫事務也是如此。 應用程序中的數據庫調用是按照計劃好的次序,分組為原子的操作單元。(注重,這也意味著,應用程 序中,在單個的SQL語句發送之后,自動事務提交(auto-commit)模式失效了。這種模式專門為SQL控制臺操作設計的。 Hibernate禁止立即自動事務提交模式,或者期望應用服務器禁止立即自動事務提交模式。)
在多用戶的client/server應用程序中,最常用的模式是 每個請求一個會話(session-per-request)。 在這種模式下,來自客戶端的請求被發送到服務器端(即Hibernate持久化層運行的地方),一 個新的Hibernate Session被打開,并且執行這個操作單元中所有的數據庫操作。 一旦操作完成(同時發送到客戶端的響應也預備就緒),session被同步,然后關閉。你也可以使用單 個數據庫事務來處理客戶端請求,在你打開Session之后啟動事務,在你關閉 Session之前提交事務。會話和請求之間的關系是一對一的關系,這種模式對 于大多數應用程序來說是很棒的。
真正的挑戰在于如何去實現這種模式:不僅Session和事務必須被正確的開始和結束, 而且他們也必須能被數據訪問操作訪問。用攔截器來實現操作單元的劃分,該攔截器在客戶端請求達到服 務器端的時候開始,在服務器端發送響應(即,ServletFilter)之前結束。我們推薦 使用一個ThreadLocal 變量,把 Session綁定到處理客戶端請求的線 程上去。這種方式可以讓運行在該線程上的所有程序代碼輕松的訪問Session(就像訪問一 個靜態變量那樣)。你也可以在一個ThreadLocal 變量中保持事務上下文環境,不過這依靠 于你所選擇的數據庫事務劃分機制。這種實現模式被稱之為 ThreadLocal Session和 Open Session in View。你可以很輕易的擴展本文前面章節展示的 HibernateUtil 輔助類來實現這種模式。當然,你必須找到一種實現攔截器的方法,并 且可以把攔截器集成到你的應用環境中。請參考Hibernate網站上面的提示和例子。

12.1.2.應用程序事務(application transactions)
session-per-request模式不僅僅是一個可以用來設計操作單元的有用概念。很多業務處理流程都需 要一系列完整的和用戶之間的交互,即用戶對數據庫的交叉訪問。在基于web的應用和企業 應用中,跨用戶交互的數據庫事務是無法接受的。考慮下面的例子:
在界面的第一屏,打開對話框,用戶所看到的數據是被一個特定的 Session 和數據 庫事務載入(load)的。用戶可以隨意修改對話框中的數據對象。
5分鐘后,用戶點擊“保存”,期望所做出的修改被持久化;同時他也期望自己是唯一修改這個信息的人,不會出現 修改沖突。
從用戶的角度來看,我們把這個操作單元稱為應用程序長事務(application transaction)。 在你的應用程序中,可以有很多種方法來實現它。
頭一個幼稚的做法是,在用戶思考的過程中,保持Session和數據庫事務是打開的, 保持數據庫鎖定,以阻止并發修改,從而保證數據庫事務隔離級別和原子操作。這種方式當然是一個反模式, 因為數據庫鎖定的維持會導致應用程序無法擴展并發用戶的數目。
很明顯,我們必須使用多個數據庫事務來實現一個應用程序事務。在這個例子中,維護業務處理流程的 事務隔離變成了應用程序層的部分責任。單個應用程序事務通常跨越多個數據庫事務。假如僅僅只有一 個數據庫事務(最后的那個事務)保存更新過的數據,而所有其他事務只是單純的讀取數據(例如在一 個跨越多個請求/響應周期的向導風格的對話框中),那么應用程序事務將保證其原子性。這種方式比聽 起來還要輕易實現,非凡是當你使用了Hibernate的下述特性的時候:
自動版本化 - Hibernate能夠自動進行樂觀并發控制 ,假如在用戶思考 的過程中發生并發修改沖突,Hibernate能夠自動檢測到。
脫管對象(Detached Objects)- 假如你決定采用前面已經討論過的 session-per-request模式,所有載入的實例在用戶思考的過程 中都處于與Session脫離的狀態。Hibernate答應你把與Session脫離的對象重新關聯到Session 上,并且對修改進行持久化,這種模式被稱為 session-per-request-with-detached-objects。自動版本化被用來隔離并發修改。
長生命周期的Session (Long Session)- Hibernate 的Session 可以在數據庫事務提交之后和底層的JDBC連接斷開,當一個新的客戶端請求到來的時候,它又重新連接上底層的 JDBC連接。這種模式被稱之為session-per-application-transaction,這種情況可 能會造成不必要的Session和JDBC連接的重新關聯。自動版本化被用來隔離并發修改。
session-per-request-with-detached-objects 和 session-per-application-transaction 各有優缺點,我們在本章后面樂觀并發 控制那部分再進行討論。

12.1.3.關注對象標識(Considering object identity)
應用程序可能在兩個不同的Session中并發訪問同一持久化狀態,但是, 一個持久化類的實例無法在兩個 Session中共享。因此有兩種不同的標識語義:
數據庫標識
foo.getId().equals( bar.getId() )
JVM 標識
foo==bar
對于那些關聯到 特定Session (也就是在單個Session的范圍內)上的對象來說,這 兩種標識的語義是等價的,與數據庫標識對應的JVM標識是由Hibernate來保 證的。不過,當應用程序在兩個不同的session中并發訪問具有同一持久化標 識的業務對象實例的時候,這個業務對象的兩個實例事實上是不相同的(從 JVM識別來看)。這種沖突可以通過在同步和提交的時候使用自動版本化和樂 觀鎖定方法來解決。
這種方式把關于并發的頭疼問題留給了Hibernate和數據庫;由于在單個線程內,操作單元中的對象識別不 需要代價昂貴的鎖定或其他意義上的同步,因此它同時可以提供最好的可伸縮性。只要在單個線程只持有一個 Session,應用程序就不需要同步任何業務對象。在Session 的范圍內,應用程序可以放心的使用==進行對象比較。
不過,應用程序在Session的外面使用==進行對象比較可能會 導致無法預期的結果。在一些無法預料的場合,例如,假如你把兩個脫管對象實例放進同一個 Set的時候,就可能發生。這兩個對象實例可能有同一個數據庫標識(也就是說, 他們代表了表的同一行數據),從JVM標識的定義上來說,對脫管的對象而言,Hibernate無法保證他們 的的JVM標識一致。開發人員必須覆蓋持久化類的equals()方法和 hashCode() 方法,從而實現自定義的對象相等語義。警告:不要使用數據庫標識 來實現對象相等,應該使用業務鍵值,由唯一的,通常不變的屬性組成。當一個瞬時對象被持久化的時 候,它的數據庫標識會發生改變。假如一個瞬時對象(通常也包括脫管對象實例)被放入一 個Set,改變它的hashcode會導致與這個Set的關系中斷。雖 然業務鍵值的屬性不象數據庫主鍵那樣穩定不變,但是你只需要保證在同一個Set 中的對象屬性的穩定性就足夠了。請到Hibernate網站去尋求這個問題更多的具體的討論。請注重,這不是一 個有關Hibernate的問題,而僅僅是一個關于java對象標識和判等行為如何實現的問題。

12.1.4.常見問題
決不要使用反模式session-per-user-session或者 session-per-application(當然,這個規定幾乎沒有例外)。請注重, 下述一些問題可能也會出現在我們推薦的模式中,在你作出某個設計決定之前,請務必理解該模式的應用前提。
Session 是一個非線程安全的類。假如一個Session 實例答應共享的話,那些支持并發運行的東東,例如HTTP request,session beans,或者是 Swing workers,將會導致出現資源爭用(race condition)。假如在HttpSession中有 Hibernate 的Session的話(稍后討論),你應該考慮同步訪問你的Http session。 否則,只要用戶足夠快的點擊瀏覽器的“刷新”,就會導致兩個并發運行線程使用同一個 Session。
一個由Hibernate拋出的異常意味著你必須立即回滾數據庫事務,并立即關閉Session (稍后會展開討論)。假如你的Session綁定到一個應用程序上,你必 須停止該應用程序。回滾數據庫事務并不會把你的業務對象退回到事務啟動時候的狀態。這 意味著數據庫狀態和業務對象狀態不同步。通常情況下,這不是什么問題,因為異常是不可 恢復的,你必須在回滾之后重新開始執行。
Session 緩存了處于持久化狀態的每個對象(Hibernate會監視和檢查臟數據)。 這意味著,假如你讓Session打開很長一段時間,或是僅僅載入了過多的數據, Session占用的內存會一直增長,直到拋出OutOfMemoryException異常。這個 問題的一個解決方法是調用clear() 和evict()來治理 Session的緩存,但是假如你需要大批量數據操作的話,最好考慮 使用存儲過程。在第14章 批量處理(Batch PRocessing)中有一些解決方案。在用戶會話期間一直保持 Session打開也意味著出現臟數據的可能性很高。

12.2.數據庫事務聲明
數據庫(或者系統)事務的聲明總是必須的。在數據庫事務之外,就無法和數據庫通訊(這可能會讓那些習慣于 自動提交事務模式的開發人員感到迷惑)。永遠使用清楚的事務聲明,即使只讀操作也是如此。進行 顯式的事務聲明并不總是需要的,這取決于你的事務隔離級別和數據庫的能力,但不管怎么說,聲明事務總歸有益無害。
一個Hibernate應用程序可以運行在非托管環境中(也就是獨立運行的應用程序,簡單Web應用程序, 或者Swing圖形桌面應用程序),也可以運行在托管的J2EE環境中。在一個非托管環境中,Hibernate 通常自己負責治理數據庫連接池。應用程序開發人員必須手工設置事務聲明,換句話說,就是手工啟 動,提交,或者回滾數據庫事務。一個托管的環境通常提供了容器治理事務,例如事務裝配通過可聲 明的方式定義在EJB session beans的部署描述符中。可編程式事務聲明不再需要,即使是 Session 的同步也可以自動完成。
讓持久層具備可移植性是人們的理想。Hibernate提供了一套稱為Transaction的封裝API, 用來把你的部署環境中的本地事務治理系統轉換到Hibernate事務上。這個API是可選的,但是我們強烈 推薦你使用,除非你用CMT session bean。
通常情況下,結束 Session 包含了四個不同的階段:
同步session(flush,刷出到磁盤)
提交事務
關閉session
處理異常
session的同步(flush,刷出)前面已經討論過了,我們現在進一步考察在托管和非托管環境下的事務聲明和異常處理。

12.2.1.非托管環境
假如Hibernat持久層運行在一個非托管環境中,數據庫連接通常由Hibernate的連接池機制 來處理。



代碼內容
session/transaction處理方式如下所示:
//Non-managed environment idiom
Session sess = factory.openSession();
Transaction tx = null;
try {
tx = sess.beginTransaction();

// do some work
...

tx.commit();
}
catch (RuntimeException e) {
if (tx != null) tx.rollback();
throw e; // or display error message
}
finally {
sess.close();
}
你不需要顯式flush() Session - 對commit()的調用會自動觸發session的同步。
調用 close() 標志session的結束。 close()方法重要的暗示是,session釋放了JDBC連接。
這段Java代碼是可移植的,可以在非托管環境和JTA環境中運行。
你很可能從未在一個標準的應用程序的業務代碼中見過這樣的用法;致命的(系統)異常應該總是 在應用程序“頂層”被捕捉。換句話說,執行Hibernate調用的代碼(在持久層)和處理 RuntimeException異常的代碼(通常只能清理和退出應用程序)應該在不同 的應用程序邏輯層。這對于你設計自己的軟件系統來說是一個挑戰,只要有可能,你就應該使用 J2EE/EJB容器服務。異常處理將在本章稍后進行討論。
請注重,你應該選擇 org.hibernate.transaction.JDBCTransactionFactory (這是默認選項).

12.2.2.使用JTA
假如你的持久層運行在一個應用服務器中(例如,在EJB session beans的后面),Hibernate獲取 的每個數據源連接將自動成為全局JTA事務的一部分。Hibernate提供了兩種策略進行JTA集成。
假如你使用bean治理事務(BMT),可以通過使用Hibernate的 Transaction API來告訴 應用服務器啟動和結束BMT事務。因此,事務治理代碼和在非托管環境下是一樣的。




代碼內容
// BMT idiom
Session sess = factory.openSession();
Transaction tx = null;
try {
tx = sess.beginTransaction();

// do some work
...

tx.commit();
}
catch (RuntimeException e) {
if (tx != null) tx.rollback();
throw e; // or display error message
}
finally {
sess.close();
} 在CMT方式下,事務聲明是在session bean的部署描述符中,而不需要編程。 除非你設置了屬性hibernate.transaction.flush_before_completion和 hibernate.transaction.auto_close_session為true, 否則你必須自己同步和關閉Session。Hibernate可以為你自動同步和關閉 Session。你唯一要做的就是當發生異常時進行事務回滾。幸運的是, 在一個CMT bean中,事務回滾甚至可以由容器自動進行,因為由session bean方法拋出的未處理的 RuntimeException異常可以通知容器設置全局事務回滾。這意味著 在CMT中,你完全無需使用Hibernate的Transaction API 。
請注重,當你配置Hibernate事務工廠的時候,在一個BMT session bean中,你應該選擇 org.hibernate.transaction.JTATransactionFactory,在一個 CMT session bean中選擇org.hibernate.transaction.CMTTransactionFactory。 記住,同時也要設置org.hibernate.transaction.manager_lookup_class。
假如你使用CMT環境,并且讓容器自動同步和關閉session,你可能也希望在你代碼的不同部分使用 同一個session。一般來說,在一個非托管環境中,你可以使用一個ThreadLocal 變量來持有這個session,但是單個EJB方法調用可能會在不同的線程中執行(舉例來說,一個session bean調用另一個session bean)。假如你不想在應用代碼中被傳遞Session對 象實例的問題困擾的話,那么SessionFactory 提供的 getCurrentSession()方法就很適合你,該方法返回一個綁定到JTA事務 上下文環境中的session實例。這也是把Hibernate集成到一個應用程序中的最簡單的方法!這個“當 前的”session總是可以自動同步和自動關閉(不考慮上述的屬性設置)。我們的session/transaction 治理代碼減少到如下所示:




代碼內容
// CMT idiom
Session sess = factory.getCurrentSession();

// do some work
...
換句話來說,在一個托管環境下,你要做的所有的事情就是調用 SessionFactory.getCurrentSession(),然后進行你的數據訪問,把其余的工作 交給容器來做。事務在你的session bean的部署描述符中以可聲明的方式來設置。session的生命周期完全 由Hibernate來治理。
對after_statement連接釋放方式有一個警告。因為JTA規范的一個很愚蠢的限制,Hibernate不可能自動清理任何未關閉的ScrollableResults 或者Iterator,它們是由scroll()或iterate()產生的。你must通過在finally塊中,顯式調用ScrollableResults.close()或者Hibernate.close(Iterator)方法來釋放底層數據庫游標。(當然,大部分程序完全可以很輕易的避免在CMT代碼中出現scroll()或iterate()。)

12.2.3.異常處理
假如 Session 拋出異常 (包括任何SQLException), 你應該立即回滾數據庫事務,調用 Session.close() ,丟棄該 Session實例。Session的某些方法可能會導致session 處于不一致的狀態。所有由Hibernate拋出的異常都視為不可以恢復的。確保在 finally 代碼塊中調用close()方法,以關閉掉 Session。
HibernateException是一個非檢查期異常(這不同于Hibernate老的版本), 它封裝了Hibernate持久層可能出現的大多數錯誤。我們的觀點是,不應該強迫應用程序開發人員 在底層捕捉無法恢復的異常。在大多數軟件系統中,非檢查期異常和致命異常都是在相應方法調用 的堆棧的頂層被處理的(也就是說,在軟件上面的邏輯層),并且提供一個錯誤信息給應用軟件的用戶 (或者采取其他某些相應的操作)。請注重,Hibernate也有可能拋出其他并不屬于 HibernateException的非檢查期異常。這些異常同樣也是無法恢復的,應該 采取某些相應的操作去處理。
在和數據庫進行交互時,Hibernate把捕捉的SQLException封裝為Hibernate的 JDBCException。事實上,Hibernate嘗試把異常轉換為更有實際含義 的JDBCException異常的子類。底層的SQLException可以 通過JDBCException.getCause()來得到。Hibernate通過使用關聯到 SessionFactory上的SQLExceptionConverter來 把SQLException轉換為一個對應的JDBCException 異常的子類。默認情況下,SQLExceptionConverter可以通過配置dialect 選項指定;此外,也可以使用用戶自定義的實現類(參考javadocs SQLExceptionConverterFactory類來了解詳情)。標準的 JDBCException子類型是:
JDBCConnectionException - 指明底層的JDBC通訊出現錯誤
SQLGrammarException - 指明發送的SQL語句的語法或者格式錯誤
ConstraintViolationException - 指明某種類型的約束違例錯誤
LockAcquisitionException - 指明了在執行請求操作時,獲取 所需的鎖級別時出現的錯誤。
GenericJDBCException - 不屬于任何其他種類的原生異常

12.3.樂觀并發控制(Optimistic concurrency control)
唯一能夠同時保持高并發和高可伸縮性的方法就是使用帶版本化的樂觀并發控制。版本檢查使用版本號、 或者時間戳來檢測更新沖突(并且防止更新丟失)。Hibernate為使用樂觀并發控制的代碼提供了三種可 能的方法,應用程序在編寫這些代碼時,可以采用它們。我們已經在前面應用程序長事務那部分展示了 樂觀并發控制的應用場景,此外,在單個數據庫事務范圍內,版本檢查也提供了防止更新丟失的好處。
12.3.1.應用程序級別的版本檢查(Application version checking)
未能充分利用Hibernate功能的實現代碼中,每次和數據庫交互都需要一個新的 Session,而且開發人員必須在顯示數據之前從數據庫中重 新載入所有的持久化對象實例。這種方式迫使應用程序自己實現版本檢查來確保 應用程序事務的隔離,從數據訪問的角度來說是最低效的。這種使用方式和 entity EJB最相似。
// foo is an instance loaded by a previous Session
session = factory.openSession();
Transaction t = session.beginTransaction();
int oldVersion = foo.getVersion();
session.load( foo, foo.getKey() ); // load the current state
if ( oldVersion!=foo.getVersion ) throw new StaleObjectStateException();
foo.setProperty("bar");
t.commit();
session.close();
version 屬性使用 來映射,假如對象 是臟數據,在同步的時候,Hibernate會自動增加版本號。
當然,假如你的應用是在一個低數據并發環境下,并不需要版本檢查的話,你照樣可以使用 這種方式,只不過跳過版本檢查就是了。在這種情況下,最晚提交生效 (last commit wins)就是你的應用程序長事務的默認處理策略。 請記住這種策略可能會讓應用軟件的用戶感到困惑,因為他們有可能會碰上更新丟失掉卻沒 有出錯信息,或者需要合并更改沖突的情況。
很明顯,手工進行版本檢查只適合于某些軟件規模非常小的應用場景,對于大多數軟件應用場景 來說并不現實。通常情況下,不僅是單個對象實例需要進行版本檢查,整個被修改過的關 聯對象圖也都需要進行版本檢查。作為標準設計范例,Hibernate使用長生命周期 Session的方式,或者脫管對象實例的方式來提供自動版本檢查。

12.3.2.長生命周期session和自動版本化
單個 Session實例和它所關聯的所有持久化對象實例都被用于整個 應用程序事務。Hibernate在同步的時候進行對象實例的版本檢查,假如檢測到并發修 改則拋出異常。由開發人員來決定是否需要捕捉和處理這個異常(通常的抉擇是給用戶 提供一個合并更改,或者在無臟數據情況下重新進行業務操作的機會)。
在等待用戶交互的時候, Session 斷開底層的JDBC連接。這種方式 以數據庫訪問的角度來說是最高效的方式。應用程序不需要關心版本檢查或脫管對象實例 的重新關聯,在每個數據庫事務中,應用程序也不需要載入讀取對象實例。




代碼內容
// foo is an instance loaded earlier by the Session
session.reconnect(); // OBTain a new JDBC connection
Transaction t = session.beginTransaction();
foo.setProperty("bar");
t.commit(); // End database transaction, flushing the change and checking the version
session.disconnect(); // Return JDBC connection
foo 對象始終和載入它的Session相關聯。 Session.reconnect()獲取一個新的數據庫連接(或者 你可以提供一個),并且繼續當前的session。Session.disconnect() 方法把session與JDBC連接斷開,把數據庫連接返回到連接池(除非是你自己提供的數據 庫連接)。在Session重新連接上數據庫連接之后,你可以對任何可能被其他事務更新過 的對象調用Session.lock(),設置LockMode.READ 鎖定模式,這樣你就可以對那些你不預備更新的數據進行強制版本檢查。此外,你并不需要 鎖定那些你預備更新的數據。
假若對disconnect()和reconnect()的顯式調用發生得太頻繁了,你可以使用hibernate.connection.release_mode來代替。
假如在用戶思考的過程中,Session因為太大了而不能保存,那么這種模式是有 問題的。舉例來說,一個HttpSession應該盡可能的小。由于 Session是一級緩存,并且保持了所有被載入過的對象,因此 我們只應該在那些少量的request/response情況下使用這種策略。而且在這種情況下, Session 里面很快就會有臟數據出現,因此請牢牢記住這一建議。
此外,也請注重,你應該讓與數據庫連接斷開的Session對持久層保持 關閉狀態。換句話說,使用有狀態的EJB session bean來持有Session, 而不要把它傳遞到web層(甚至把它序列化到一個單獨的層),保存在HttpSession中。

12.3.3.脫管對象(deatched object)和自動版本化
這種方式下,與持久化存儲的每次交互都發生在一個新的Session中。 然而,同一持久化對象實例可以在多次與數據庫的交互中重用。應用程序操縱脫管對象實例 的狀態,這個脫管對象實例最初是在另一個Session 中載入的,然后 調用 Session.update(),Session.saveOrUpdate(), 或者 Session.merge() 來重新關聯該對象實例。




代碼內容
// foo is an instance loaded by a previous Session
foo.setProperty("bar");
session = factory.openSession();
Transaction t = session.beginTransaction();
session.saveOrUpdate(foo); // Use merge() if "foo" might have been loaded already
t.commit();
session.close();
Hibernate會再一次在同步的時候檢查對象實例的版本,假如發生更新沖突,就拋出異常。
假如你確信對象沒有被修改過,你也可以調用lock() 來設置 LockMode.READ(繞過所有的緩存,執行版本檢查),從而取 代 update()操作。

12.3.4.定制自動版本化行為
對于特定的屬性和集合,通過為它們設置映射屬性optimistic-lock的值 為false,來禁止Hibernate的版本自動增加。這樣的話,假如該屬性 臟數據,Hibernate將不再增加版本號。
遺留系統的數據庫Schema通常是靜態的,不可修改的。或者,其他應用程序也可能訪問同一數據 庫,根本無法得知如何處理版本號,甚至時間戳。在以上的所有場景中,實現版本化不能依靠 數據庫表的某個特定列。在的映射中設置 optimistic-lock="all"可以在沒有版本或者時間戳屬性映射的情況下實現 版本檢查,此時Hibernate將比較一行記錄的每個字段的狀態。請注重,只有當Hibernate能夠比 較新舊狀態的情況下,這種方式才能生效,也就是說, 你必須使用單個長生命周期Session模式,而不能使用 session-per-request-with-detached-objects模式。
有些情況下,只要更改不發生交錯,并發修改也是答應的。當你在 的映射中設置optimistic-lock="dirty",Hibernate在同步的時候將只比較有臟 數據的字段。
在以上所有場景中,不管是專門設置一個版本/時間戳列,還是進行全部字段/臟數據字段比較, Hibernate都會針對每個實體對象發送一條UPDATE(帶有相應的 WHERE語句 )的SQL語句來執行版本檢查和數據更新。假如你對關聯實體 設置級聯關系使用傳播性持久化(transitive persistence),那么Hibernate可能會執行不必 要的update語句。這通常不是個問題,但是數據庫里面對on update點火 的觸發器可能在脫管對象沒有任何更改的情況下被觸發。因此,你可以在 的映射中,通過設置select-before-update="true" 來定制這一行為,強制Hibernate SELECT這個對象實例,從而保證, 在更新記錄之前,對象的確是被修改過。

12.4.悲觀鎖定(Pessimistic Locking)
用戶其實并不需要花很多精力去擔心鎖定策略的問題。通常情況下,只要為JDBC連接指定一下隔 離級別,然后讓數據庫去搞定一切就夠了。然而,高級用戶有時候希望進行一個排它的悲觀鎖定, 或者在一個新的事務啟動的時候,重新進行鎖定。
Hibernate總是使用數據庫的鎖定機制,從不在內存中鎖定對象!
類LockMode 定義了Hibernate所需的不同的鎖定級別。一個鎖定 可以通過以下的機制來設置:
當Hibernate更新或者插入一行記錄的時候,鎖定級別自動設置為LockMode.WRITE。
當用戶顯式的使用數據庫支持的SQL格式SELECT ... FOR UPDATE 發送SQL的時候,鎖定級別設置為LockMode.UPGRADE
當用戶顯式的使用Oracle數據庫的SQL語句SELECT ... FOR UPDATE NOWAIT 的時候,鎖定級別設置LockMode.UPGRADE_NOWAIT
當Hibernate在“可重復讀”或者是“序列化”數據庫隔離級別下讀取數據的時候,鎖定模式 自動設置為LockMode.READ。這種模式也可以通過用戶顯式指定進行設置。
LockMode.NONE 代表無需鎖定。在Transaction結束時, 所有的對象都切換到該模式上來。與session相關聯的對象通過調用update() 或者saveOrUpdate()脫離該模式。
"顯式的用戶指定"可以通過以下幾種方式之一來表示:
調用 Session.load()的時候指定鎖定模式(LockMode)。
調用Session.lock()。
調用Query.setLockMode()。
假如在UPGRADE或者UPGRADE_NOWAIT鎖定模式下調 用Session.load(),并且要讀取的對象尚未被session載入過,那么對象 通過SELECT ... FOR UPDATE這樣的SQL語句被載入。假如為一個對象調用 load()方法時,該對象已經在另一個較少限制的鎖定模式下被載入了,那 么Hibernate就對該對象調用lock() 方法。
假如指定的鎖定模式是READ, UPGRADE 或 UPGRADE_NOWAIT,那么Session.lock()就 執行版本號檢查。(在UPGRADE 或者UPGRADE_NOWAIT 鎖定模式下,執行SELECT ... FOR UPDATE這樣的SQL語句。)
假如數據庫不支持用戶設置的鎖定模式,Hibernate將使用適當的替代模式(而不是扔出異常)。 這一點可以確保應用程序的可移植性。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: h色网站在线观看 | 羞羞答答www网站进入 | 越南一级黄色片 | 国产porn在线| 欧美三级欧美成人高清www | 黄视频免费在线观看 | 亚洲第一黄色网 | 欧美人与牲禽动交精品一区 | 狠狠干天天操 | 国产精品亚洲一区二区三区久久 | 日本在线高清 | 男女羞羞在线观看 | 久草在线手机视频 | 国产69精品久久久久久 | 国产精品午夜未成人免费观看 | 国产毛片aaa一区二区三区视频 | 成人短视频在线观看免费 | 欧美日韩在线视频一区 | 在线观看免费视频麻豆 | 久久久久国产成人精品亚洲午夜 | 欧美另类在线视频 | 黄色大片在线观看 | 91黄瓜视频 | 欧美在线观看禁18 | 国产精品一区99 | 亚洲一区二区观看播放 | 久久成人免费观看 | 欧日一级片 | 91福利影视 | 国产精品一区视频 | 久久精品色 | 成人福利软件 | 久久国产精品久久久久久久久久 | 宅男噜噜噜66国产免费观看 | 少妇一级淫片免费放正片 | 欧美一级做一a做片性视频 日韩黄色片免费看 | 羞羞的视频在线 | 狠狠操操 | 精品一区二区三区四区在线 | 羞羞视频免费网站 | 色七七亚洲 |