- 相關(guān)推薦
J2EE控制策略
J2EE事務(wù)并發(fā)訪(fǎng)問(wèn)主要可以分為兩類(lèi),分別是同一個(gè)系統事務(wù)和跨事務(wù)訪(fǎng)問(wèn)的并發(fā)訪(fǎng)問(wèn)控制,其中同一個(gè)系統事務(wù)可以采取樂(lè )觀(guān)鎖以及悲觀(guān)鎖策略,而跨多個(gè)系統事務(wù)時(shí)則需要樂(lè )觀(guān)離線(xiàn)鎖和悲觀(guān)離線(xiàn)鎖。
樂(lè )觀(guān)鎖
樂(lè )觀(guān)鎖是在同一個(gè)數據庫事務(wù)中我們常采取的策略,因為它能使得我們的系統保持高的性能的情況下,提供很好的并發(fā)訪(fǎng)問(wèn)控制。樂(lè )觀(guān)鎖,顧名思義就是保持一種樂(lè )觀(guān)的態(tài)度,我們認為系統中的事務(wù)并發(fā)更新不會(huì )很頻繁,即使沖突了也沒(méi)事,大不了重新再來(lái)一次。它的基本思想就是每次提交一個(gè)事務(wù)更新時(shí),我們想看看要修改的東西從上次讀取以后有沒(méi)有被其它事務(wù)修改過(guò),如果修改過(guò),那么更新就會(huì )失敗。
因為樂(lè )觀(guān)鎖其實(shí)并不會(huì )鎖定任何記錄,所以數據庫的事務(wù)隔離級別設置為讀取已提交或者更低的隔離界別,那么是不能避免不可重復讀問(wèn)題的(因為此時(shí)讀事務(wù)不會(huì )阻塞其它事務(wù)),所以采用樂(lè )觀(guān)鎖的時(shí)候,系統應該要容許不可重復讀問(wèn)題的出現。
一般可以采用以下三種方法:
版本(Version)字段:在我們的實(shí)體中增加一個(gè)版本控制字段,每次事務(wù)更新后就將版本字段的值加1.
時(shí)間戳(timestamps):采取這種策略后,當每次要提交更新的時(shí)候就會(huì )將系統當前時(shí)間和實(shí)體加載時(shí)的時(shí)間進(jìn)行比較,如果不一致,那么就報告樂(lè )觀(guān)鎖失敗,從而回滾事務(wù)或者重新嘗試提交。采用時(shí)間戳有一些不足,比如在集群環(huán)境下,每個(gè)節點(diǎn)的時(shí)間同步也許會(huì )成問(wèn)題,并且如果并發(fā)事務(wù)間隔時(shí)間小于當前平臺最小的時(shí)鐘單位,那么就會(huì )發(fā)生覆蓋前一個(gè)事務(wù)結果的問(wèn)題。因此一般采用版本字段比較好。
基于所有屬性進(jìn)行檢測:采用這種策略的時(shí)候,需要比較每個(gè)字段在讀取以后有沒(méi)有被修改過(guò),所以這種策略實(shí)現起來(lái)比較麻煩,要求對每個(gè)屬性都進(jìn)行比較,如果采用hibernate的話(huà),因為Hibernate在一級緩存中可以進(jìn)行臟檢測,那么可以判斷哪些字段被修改過(guò),從而動(dòng)態(tài)的生成sql語(yǔ)句進(jìn)行更新。
在JDBC和Hibernate中使用樂(lè )觀(guān)鎖:
JDBC中使用樂(lè )觀(guān)鎖:如果我們采用JDBC來(lái)實(shí)現持久層的話(huà),那么就可以采用以上將的三種支持樂(lè )觀(guān)鎖的策略,在實(shí)體中增加一個(gè)version字段或者一個(gè)Date字段,也可以采用基于所有屬性的策略,下面就采用version字段來(lái)做一演示:
假如系統中有一個(gè)Account的實(shí)體類(lèi),我們在A(yíng)ccount中多加一個(gè)version字段,那么我們JDBC Sql語(yǔ)句將如下寫(xiě):
Select a.version....from Account as a where (where condition..)
Update Account set version = version+1.....(another field) where version =?...(another contidition)
可以通過(guò)更新結果的行數來(lái)進(jìn)行判斷,如果更新結果的行數為0,那么說(shuō)明實(shí)體從加載以來(lái)已經(jīng)被其它事務(wù)更改了,所以就拋出自定義的樂(lè )觀(guān)鎖定異常(或者也可以采用Spring封裝的異常體系)。具體實(shí)例如下:
在使用JDBC API的情況下,需要在每個(gè)update語(yǔ)句中,都要進(jìn)行版本字段的更新以及判斷,因此如果稍不小心就會(huì )出現版本字段沒(méi)有更新的問(wèn)題,相反當前的 ORM框架卻為我們做好了一切,需要做的就是在每個(gè)實(shí)體中都增加version或者是Date字段。
Hibernate中使用樂(lè )觀(guān)鎖:如果采用Hibernate做為持久層的框架,那么實(shí)現樂(lè )觀(guān)鎖將變得非常容易,因為框架會(huì )幫我們生成相應的sql語(yǔ)句,不僅減少了開(kāi)發(fā)人員的負擔,而且不容易出錯。下面同樣采用version字段的方式來(lái)總結一下:
同樣假如系統中有一個(gè)Account的實(shí)體類(lèi),我們在A(yíng)ccount中多加一個(gè)version字段,
提交事務(wù)時(shí),hibernate內部會(huì )生成相應的SQL語(yǔ)句將版本字段加1,并且進(jìn)行相應的版本檢測,如果檢測到并發(fā)樂(lè )觀(guān)鎖定異常,那么就拋出StaleObjectStateException.
悲觀(guān)鎖
所謂悲觀(guān)鎖,顧名思義就是采用一種悲觀(guān)的態(tài)度來(lái)對待事務(wù)并發(fā)問(wèn)題,系統中的并發(fā)更新會(huì )非常頻繁,并且事務(wù)失敗了以后重來(lái)的開(kāi)銷(xiāo)很大,這樣就需要采用真正意義上的鎖來(lái)進(jìn)行實(shí)現。悲觀(guān)鎖的基本思想就是每次一個(gè)事務(wù)讀取某一條記錄后,就會(huì )把這條記錄鎖住,這樣其它的事務(wù)要想更新,必須等以前的事務(wù)提交或者回滾解除鎖。
最后還是需要明確一個(gè)問(wèn)題,假如數據庫事務(wù)的隔離級別設置為讀取已提交或者更低,那么通過(guò)悲觀(guān)鎖,控制了不可重復讀的問(wèn)題,但是不能避免幻影讀的問(wèn)題(因為要想避免我們就需要設置數據庫隔離級別為Serializable,而一般情況下會(huì )采取讀取已提交或者更低隔離級別,并配合樂(lè )觀(guān)或者悲觀(guān)鎖來(lái)實(shí)現并發(fā)控制,所以幻影讀問(wèn)題是不能避免的,如果想避免幻影讀問(wèn)題,那么只能依靠數據庫的serializable隔離級別(幸運的是幻影讀問(wèn)題一般情況下不嚴重)。
下面就分別以JDBC和Hibernate來(lái)總結一下:
JDBC中使用悲觀(guān)鎖:在JDBC中使用悲觀(guān)鎖,需要使用select for update語(yǔ)句,假如我們系統中有一個(gè)Account的類(lèi),我們可以采用如下的方式來(lái)進(jìn)行:
Select * from Account where ...(where condition).. for update.
當使用了for update語(yǔ)句后,每次在讀取或者加載一條記錄的時(shí)候,都會(huì )鎖住被加載的記錄,那么當其他事務(wù)如果要更新或者是加載此條記錄就會(huì )因為不能獲得鎖而阻塞,這樣就避免了不可重復讀以及臟讀的問(wèn)題,但是其他事務(wù)還是可以插入和刪除記錄,這樣也許同一個(gè)事務(wù)中的兩次讀取會(huì )得到不同的結果集,但是這不是悲觀(guān)鎖所造成的問(wèn)題,這是數據庫隔離級別所造成的問(wèn)題。
最后還需要注意的一點(diǎn)就是每個(gè)沖突的事務(wù)中,必須使用select for update 語(yǔ)句來(lái)進(jìn)行數據庫的訪(fǎng)問(wèn),如果一些事務(wù)沒(méi)有使用select for update語(yǔ)句,那么就會(huì )很容易造成錯誤,這也是采用JDBC進(jìn)行悲觀(guān)控制的缺點(diǎn)。
Hibernate中使用悲觀(guān)鎖:相比于JDBC使用悲觀(guān)鎖來(lái)說(shuō),在Hibernate中使用悲觀(guān)鎖將會(huì )容易很多,因為Hibernate有API讓我們來(lái)調用,從而避免直接寫(xiě)SQL語(yǔ)句。下面就Hibernate使用悲觀(guān)鎖做一總結:
首先先要明確一下Hibernate中支持悲觀(guān)鎖的兩種模式LockMode.UPGRADE以L(fǎng)ockMode.UPGRADE_NO_WAIT.(PS:在JPA中,對應的鎖模式是LockModeType.Read,這與Hibernate是不一樣的呵呵)
假如系統中有一個(gè)Account的類(lèi),那么具體的操作可以像這樣:
或者也可以采用如下方式來(lái)加載對象:
這樣以來(lái)當加載對象時(shí),hibernate內部會(huì )生成相應的select for update語(yǔ)句來(lái)加載對象,從而鎖定對應的記錄,避免其它事務(wù)并發(fā)更新。
【J2EE控制策略】相關(guān)文章:
酒店采購成本控制的策略03-08
物流供應鏈成本控制的策略03-06
物流供應鏈成本控制策略是什么03-09
什么是J2EE03-16
j2ee介紹03-20
J2EE的概念03-19
J2EE發(fā)展狀況03-19