簡體   English   中英

Grails /休眠:控制器中的樂觀鎖定

[英]Grails/Hibernate: Optimistic Locking in Controller

使用Grails 2.3.9(Groovy(2.2.2),Mysql 5.5.37(MySQLUTF8InnoDBDialect),JDK 1.7

我正在嘗試在控制器端通過Grails / Hibernate實現和測試樂觀鎖定功能。

根據我的直覺,以下內容

def instance = Group.findByXXX(...)
instance.properties = params
// ...
instance.version = 5 // something smaller than the current
instance.save flush:true, failOnError: true

會因為版本錯誤而引發異常。 但是,無論如何都將保存實例。

這個問題可能是一樣的這一個 ,只是我不明白。 這是我在閱讀以下問題/答案后嘗試的方法:

def copyInstance = copy(instance)   // I instantiate a new item, copy all members
                                    // from instance to the new one
copyInstance = copyInstance.merge()
copyInstance.version = 5            // something smaller than the current
copyInstance.save flush:true, failOnError: true

這具有預期的結果(保存失敗)。 但是我還是不太清楚:有人可以解釋上層對象“ instance”和下層“ copyInstance”之間的區別嗎? 而且,這是實現樂觀鎖定的方式嗎(在我看來,可能不需要額外的復制)?

只有當您在會話上下文中具有同一持久對象的兩個並發版本時,Hibernate中的AFAIK鎖定才真正起作用。

您的最佳示例之所以有效,是因為您只有一個實例,因此可以透明地監視其狀態,而無需鎖定。 Hibernate知道對象只有兩個實例,因此不可能有兩個不同的持久狀態,因此不必費心檢查版本。 它知道該對象比數據庫中的對象新,因此它僅寫入您的更改。

您的第二個實例失敗,因為您有2個相同對象的實例。 當您嘗試將第二個實例保存為較低版本時,該實例將失敗,因為該對象已被數據庫鎖定。 Hibernate將使用版本號來確定哪個對象是較新的,並將這些更改保存到數據庫中。

TL; DR:您的第一個示例無法崩潰,因為您以錯誤的方式干擾了版本控制功能...

盡管公認的答案大體上是正確的,但確實有一些錯誤之處,我認為值得補充一些說明。 我認為了解與樂觀鎖定一起在幕后發生的事情非常重要,因此,我的學步在這里。

來自已接受的答案:“您的最佳示例之所以有效,是因為您只有一個實例,因此可以透明地監視其狀態,而無需鎖定”

首先,“樂觀鎖定”是錯誤的稱呼; 在過程的任何地方都沒有進行鎖定(這只是版本控制-悲觀鎖定確實使用了鎖定-令人困惑),因此手動操作“可以透明地進行監視”使基礎過程聽起來比實際復雜得多。

實際發生的情況:發出更新時,Hibernate 會將對象的版本號( 該對象首次在此事務中首次加載時起)包含在結果更新語句中。 如果更新零行,則拋出OptimisticLockException。

這樣就可以回答為什么您的第一個示例可以正確運行了...

def instance = Group.findByID(49)

結果是:

select * from tbl_group where id=49

假設版本為28。休眠時,它會保留對象的所有屬性的副本-這樣做是為了在提交事務時可以進行臟檢查-如果所有值均未更改,則不會不需要進行更新。

因此,這一行代碼對休眠將在更新中使用的版本號沒有影響:

instance.version = 5 // something smaller than the current

最終調用保存時:

instance.save flush:true, failOnError: true

這將導致

update tbl_group set version=**29** where id=49 **and version=28**

(順便說一句,Hibernate中的邏輯是,如果此更新無法更新任何行,則另一個事務必須已經修改了該行並增加了其版本號。)

這就是為什么您沒有看到異常的原因,使用的版本是此事務最初讀取的版本號,而不是您人為地寫入對象的版本號。

對該功能的更好測試是在執行save()之前暫停執行(例如,使用調試器)。 現在進入數據庫,並將該行的版本列更改為一個更大的數字。 現在,save()將因OptimisticLockException而失敗,因為Hibernate認為另一個事務首先修改了該行。

[我想回答為什么第二個示例DID引發異常,但我需要知道您如何進行復制(包括ID的所有成員都復制過來了嗎?),它肯定會因OptimisticLockException或其他原因而失敗嗎? 不管是什么原因,這都是一組怪誕的操作,這些操作既不現實也不具有說明性,因此盡管弄清楚正在發生的事情可能是明智的選擇,但我將其留給他人!]

編輯添加:我已經運行了一個測試,第二個示例的確確實引發了OptimisticLockException-但它甚至在您更改版本號之前位於merge()處。 因此,由於錯誤的原因,您將獲得正確的結果。 我將進一步調查,但這是一個古老的問題,並且我懷疑有人會在意-但我的粗略感覺是,通過復制對象並嘗試使兩個對象代表同一持久性實體,entitymanager感到困惑,並將其提升為樂觀鎖異常。 我敢肯定有一個更完整的解釋(這與在合並的第1步中發出的選擇有關),我可能會回到另一天。 基本上,如果您給Hibernate做一些奇怪的事情,它將做奇怪的事情!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM