[英]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.