国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
第10章 Hibernate事務(wù)和并行(Transactions And Concurrency)

第 10 章 事務(wù)和并行(Transactions And Concurrency)

Hibernate本身并不是數(shù)據(jù)庫,它只是一個輕量級的對象-關(guān)系數(shù)據(jù)庫映射(object-relational)工具。它的事務(wù)交由底層的數(shù)據(jù)庫連接管理,如果數(shù)據(jù)庫連接有JTA的支持,那么在Session中進行的操作將是整個原子性JTA事務(wù)的一部分。Hibernate可以看作是添加了面向?qū)ο笳Z義的JDBC瘦適配器(thin adapter)。

10.1. 配置,會話和工廠(Configurations, Sessions and Factories)

SessionFactory的創(chuàng)建需要耗費大量資源,它是線程安全(threadsafe)的對象,在應(yīng)用中它被所有線程共享。而Session的創(chuàng)建耗費資源很少,它不是線程安全的對象,對于一個簡單商業(yè)過程(business process),它應(yīng)該只被使用一次,然后被丟棄。舉例來說,當Hibernate在基于servlet的應(yīng)用中,servlet能夠以下面的方式得到SessionFactory。

SessionFactory sf = (SessionFactory)getServletContext().getAttribute("my.session.factory");

每次調(diào)用SessionFactory的service方法能夠生成一個新的Session對象,然后調(diào)用Session的flush(),調(diào)用commit()提交它的連接,調(diào)用close()關(guān)閉它,最終丟棄它。(SessionFactory可能被保存在JNDI或者一個靜態(tài)的單例(Singleton)輔助變量中。)

在無狀態(tài)的session bean中,可以同樣使用類似的方法。bean在setSessionContext()中得到SessionFactory的實例,每個商業(yè)方法會生成一個Session對象,調(diào)用它的flush()close(),當然,應(yīng)用不應(yīng)該commit()connection. (把它留給JTA.在容器管理的事務(wù)中,數(shù)據(jù)庫連接會自動完成事務(wù)。)

我們用上述方法使用Hibernate 的Transaction API,對Transaction執(zhí)行一次commit()會把所有狀態(tài)同步,把底層的數(shù)據(jù)庫連接提交(對JTA 事務(wù)會特殊處理。)

這里需要理解flush()的含義。 flush()將持久化存儲與內(nèi)存中的變化進行同步,但不是將內(nèi)存的變化與持久化存儲進行同步。注意對所有的Hibernate JDBD 連接/事務(wù)來說,其隔離級別將施加于所有的Hibernate執(zhí)行的操作之上!

接下來的幾小節(jié)將討論利用版本化的方法來確保事務(wù)原子性,這些“高級”方法需要小心使用。

10.2. 線程和連接(Threads and connections)

在創(chuàng)建Hibernate會話(Session)時,你應(yīng)該留意以下的實踐(practices):

  • 對于一個數(shù)據(jù)庫連接,不要創(chuàng)建一個以上的SessionTransaction

  • 在對于一個數(shù)據(jù)庫連接、一個事務(wù)使用多個Session時,你尤其需要格外地小心。Session對象會記錄下調(diào)入數(shù)據(jù)更新的情況,所以另一個Session對象可能會遇到過時的數(shù)據(jù)。

  • Session不是線程安全的。決不要在兩個并發(fā)的線程中訪問同一個Session。一個Session一般只對應(yīng)一批需要一次性完成的單元操作!

10.3. 考慮對象辨別

程序可能在兩批單元操作中并發(fā)訪問同一個對象的持久化狀態(tài)。不管怎樣,持久化類的一個實例不可能在兩個Session中共享。所以有兩種不同的辨別方式:

數(shù)據(jù)庫辨別

foo.getId().equals( bar.getId() )

JVM 辨別

foo==bar

對于依附于某個特定Session的對象,兩種辨別方式是等價的。然而,當程序可能在兩個不同的session中并發(fā)訪問“同一個”(持久化辨別)商業(yè)對象時,兩個實例(對于JVM辨別來說)卻可能是“不同”的。

這種方式把關(guān)于并發(fā)的頭疼問題留給了Hibernate和數(shù)據(jù)庫。程序不需要對任何商業(yè)對象進行同步,只要程序堅持每個Session一個線程,或者對象辨別的策略(在一個Session重,程序可以安全的使用==來比較對象)。

10.4. 樂觀并發(fā)控制(Optimistic concurrency control)

許多商業(yè)過程需要一系列與用戶進行交互的過程,數(shù)據(jù)庫訪問穿插在這些過程中。對于web和企業(yè)應(yīng)用來說,跨一個用戶交互過程的數(shù)據(jù)事務(wù)是不可接受的。

維護各商業(yè)事務(wù)間的隔離(isolocation)就成為應(yīng)用層的部分責任,我們把這種過程稱為長時間運行的應(yīng)用事務(wù)(application transaction)。單一的應(yīng)用事務(wù)可能跨越多個數(shù)據(jù)庫事務(wù)。如果這些數(shù)據(jù)庫事務(wù)中只有一個(最后一個)保存了被修改的數(shù)據(jù),其他事務(wù)只是簡單地讀數(shù)據(jù),則這個應(yīng)用事務(wù)就是原子性的。

唯一滿足高并發(fā)性以及高可擴展性的方法是使用帶有版本化的樂觀并發(fā)控制。Hibernate為使用樂觀并發(fā)控制的代碼提供了三種可能的方法。

10.4.1. 使用長生命周期帶有自動版本化的會話

在整個商業(yè)過程中使用一個單獨的Session實例以及它的持久化實例,這個Session使用帶有版本化的樂觀鎖定機制,來確保多個數(shù)據(jù)庫事務(wù)對于應(yīng)用來說只是一個邏輯上的事務(wù)。在等待用戶交互時,Session斷開與數(shù)據(jù)庫的連接。這個方法從數(shù)據(jù)庫訪問方面來看是最有效的,應(yīng)用不需要關(guān)心對自己的版本檢查或是重新與不需要序列化(transient)的實例進行關(guān)聯(lián)。

在整個應(yīng)用事務(wù)中,使用單一的Session 實例和它的持久化實例。

Session 使用帶有版本化的樂觀鎖定來保證多個數(shù)據(jù)庫事務(wù)對程序來說就如同是單一的邏輯應(yīng)用事務(wù)。在等待用戶交互的時候,Session 脫離所有的底層JDBC連接。對于數(shù)據(jù)庫訪問來說,這種方法是最高效的。程序自己不需要關(guān)心版本檢查或者把已經(jīng)脫離session的實例重新關(guān)聯(lián)到session。

// foo is an instance loaded earlier by the Sessionsession.reconnect();foo.setProperty("bar");session.flush();session.connection().commit();session.disconnect();

foo對象仍然知道是哪個Session把自己裝載的。 只要Session 擁有一個JDBC連接,我們可以把對象的更改提交。

如果我們的 Session 太大,以至于在用戶思考的時間內(nèi)無法保存住,這種模式就會出現(xiàn)問題。比如,HttpSession應(yīng)該保持盡量小。因為Session也持有(必須的)第一級緩存,包含所有被裝載的對象,我們只能在很少的request/response周期中使用這一策略。這種少用是被鼓勵的,因為Session 很快就會出現(xiàn)過時的數(shù)據(jù)。

10.4.2. 使用帶有自動版本化的多個會話

每個與持久化存儲的交互出現(xiàn)在一個新的Session中,在每次與數(shù)據(jù)庫的交互中,使用相同的持久化實例。應(yīng)用操作那些從其它Session調(diào)入的已經(jīng)脫離session的實例的狀態(tài),通過使用Session.update()或者Session.saveOrUpdate()來重新建立與它們的關(guān)聯(lián)。

// foo is an instance loaded by a previous Sessionfoo.setProperty("bar");session = factory.openSession();session.saveOrUpdate(foo);session.flush();session.connection().commit();session.close();

你也可以調(diào)用lock()而非update(),如果你確信對象沒有被修改過,可以使用LockMode.READ(進行一次版本檢查,而跳過所有的緩存)。

10.4.3. 應(yīng)用程序自己進行版本檢查

每當一個新的Session中與數(shù)據(jù)庫出現(xiàn)交互的時候,這個session會在操作持久化實例前重新把它們從數(shù)據(jù)庫中裝載進來。我們現(xiàn)在所說的方式就是你的應(yīng)用程序自己使用版本檢查來確保應(yīng)用事務(wù)的隔離性。(當然,Hibernate仍會為你更新版本號)。從數(shù)據(jù)庫訪問方面來看,這種方法是最沒有效率的,與entity EJB方式類似。

// foo is an instance loaded by a previous Sessionsession = factory.openSession();int oldVersion = foo.getVersion();session.load( foo, foo.getKey() );if ( oldVersion!=foo.getVersion ) throw new StaleObjectStateException();foo.setProperty("bar");session.flush();session.connection().commit();session.close();

當然,如果在低數(shù)據(jù)并行(low-data-concurrency)的環(huán)境中,并不需要版本檢查,你仍可以使用這個方法,只需要忽略版本檢查。

10.5. 會話斷開連接(Session disconnection)

The first approach described above is to maintain a single Session for a whole business process thats spans user think time. (For example, a servlet might keep a Session in the user‘s HttpSession.) For performance reasons you should

上面提到的第一種方法是對于對一個用戶的一次登錄產(chǎn)生的整個商業(yè)過程維護一個Session。(舉例來說,servlet有可能會在用戶的HttpSession中保留一個Session)。為性能考慮,你必須

  1. 提交Transaction(或者JDBC連接),然后

  2. (在等待用戶操作前,)斷開Session與JDBC連接。

Session.disconnect()方法會斷開會話與JDBC的連接,把連接返還給連接池(除非是你自己提供這個連接的)。

Session.reconnect()方法會得到一個新的連接(你也可以自己提供一個),重新開始會話。在重新連接后,你可以通過對任何可能被其它事務(wù)更新的對象調(diào)用Session.lock()方法,來強迫對你沒有更新的數(shù)據(jù)進行版本檢查。你不需要對正在更新的數(shù)據(jù)調(diào)用lock()。

這是一個例子:

SessionFactory sessions;List fooList;Bar bar;....Session s = sessions.openSession();Transaction tx = null;try {tx = s.beginTransaction();fooList = s.find("select foo from eg.Foo foo where foo.Date = current date"http:// uses db2 date function);bar = (Bar) s.create(Bar.class);tx.commit();}catch (Exception e) {if (tx!=null) tx.rollback();s.close();throw e;}s.disconnect();

接下來:

s.reconnect();try {tx = s.beginTransaction();bar.setFooTable( new HashMap() );Iterator iter = fooList.iterator();while ( iter.hasNext() ) {Foo foo = (Foo) iter.next();s.lock(foo, LockMode.READ);    //check that foo isn‘t stalebar.getFooTable().put( foo.getName(), foo );}tx.commit();}catch (Exception e) {if (tx!=null) tx.rollback();throw e;}finally {s.close();}

從上面的例子可以看到TransactionSession之間是多對一的關(guān)系。一個Session表示了應(yīng)用程序與數(shù)據(jù)庫之間的一個對話,Transaction把這個對話分隔成一個個在數(shù)據(jù)庫級別具有原子性的單元。

10.6. 悲觀鎖定(Pessimistic Locking)

用戶不需要在鎖定策略上花費過多時間,通常我們可以對JDBC連接選定一種隔離級別(isolationn level),然后讓數(shù)據(jù)庫完成所有的工作。高級用戶可能希望得到悲觀鎖定或者在新的事務(wù)開始時重新得到鎖。

Hibernate一直都會使用數(shù)據(jù)庫的鎖定機制,而不會在內(nèi)存中鎖定對象。

LockMode類定義了Hibernate需要的不同的鎖級別。鎖由以下的機制得到:

  • LockMode.WRITE在Hibernate更新或插入一行數(shù)據(jù)時自動得到。

  • LockMode.UPGRADE在用戶通過SELECT ... FOR UPDATE這樣的特定請求得到,需要數(shù)據(jù)庫支持這種語法。

  • LockMode.UPGRADE_NOWAIT在用戶通過SELECT ... FOR UPDATE NOWAIT這樣的特定請求在Oracle數(shù)據(jù)庫環(huán)境下得到。

  • LockMode.READ在Hibernate在不斷讀(Repeatable Read)和序列化(Serializable)的隔離級別下讀取數(shù)據(jù)時得到。也可以通過用戶的明確請求重新獲得。

  • LockMode.NONE表示沒有鎖。所有對象在Transaction結(jié)束時會切換到這種鎖模式,通過調(diào)用update()或者saveOrUpdate()與會話進行關(guān)聯(lián)的對象,開始時也會在這種鎖模式。

“明確的用戶請求”會以下的幾種方式出現(xiàn):

  • 調(diào)用Session.load(),指定一種LockMode。

  • 調(diào)用Session.lock()。

  • 調(diào)用Query.setLockMode()

如果在調(diào)用Session.load()時指定了UPGRADE或者UPGRADE_NOWAIT,并且請求的對象還沒有被會話調(diào)入,那么這個對象會以SELECT ... FOR UPDATE的方式調(diào)入。如果調(diào)用load()在一個已經(jīng)調(diào)入的對象,并且這個對象調(diào)入時的鎖級別沒有請求時來得嚴格,Hibernate會對這個對象調(diào)用lock()。

Session.lock()會執(zhí)行版本號檢查的特定的鎖模式是:READ,UPGRADE或者UPGRADE_NOWAIT。(在UPGRADE或者UPGRADE_NOWAIT,SELECT ... FOR UPGRADE使用的情況下。)

如果數(shù)據(jù)庫不支持所請求的鎖模式,Hibernate將會選擇一種合適的受支持的鎖模式替換(而不是拋出一個異常)。這確保了應(yīng)用具有可移植性。

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
Hibernate4實戰(zhàn) 之第五部分:Hibernate的事務(wù)和并發(fā)
第?13?章?事務(wù)和并發(fā)
Hibernate的事務(wù)和并發(fā)(4)
hibernate3入門
Hibernate工作原理(圖解)
Hibernate的優(yōu)缺點
更多類似文章 >>
生活服務(wù)
分享 收藏 導長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服