Grails乐观锁定不检测并发更新?

时间:2016-04-28 18:45:38

标签: hibernate grails gorm optimistic-locking optimistic-concurrency

乐观锁定是否应该捕获并发更新问题?

通过并发更新,我的意思是两个不同的用户都尝试更新具有相同版本号的对象。例如,如果存在Person域类,并且id = 1且版本= 0的Person具有name = Jack,并且两个不同的用户都尝试更新版本0上的名称,那么我希望只有一个用户成功并将版本更改为1.我希望第二个用户获得Hibernate staleStateException或类似的东西。但事实并非如此。

这是我的用例:

Grails 3.1.5
grails generate-app person
grails create-domain-class Person
Edit Person.groovy to include String name
grails generate-all person.Person
gradle bootRun

使用两种不同的浏览器访问应用,例如Chrome和Firefox,以确保两个浏览器位于不同的会话中。在其中一个中创建一个Person,然后在两个浏览器中打开同一个人(版本0)进行编辑。两个浏览器现在都应该编辑人的版本0。在一个浏览器中保存名称更改,这可以工作并将对象的持久版本更改为1,但第二个浏览器仍在编辑版本0.现在在第二个浏览器中保存更改,这也有效。尽管第二个浏览器刚刚保存了对现在陈旧对象(版本0)的更改,但没有抛出StaleObject或StaleStateException。这是正确的行为吗?

2 个答案:

答案 0 :(得分:1)

是的,Grails乐观锁定将检测并发更新并抛出异常。但是,根据您所描述的内容,您不会进行并发更新。让我们仔细看看。

  1. 起点是两个浏览器都检索到相同版本的对象。但请注意,浏览器不包含对这些对象的引用。 GSP代码从数据库中抓取它们并呈现它们以供浏览器显示。换句话说,同一个对象正在查看而不是已编辑
  2. 当第一个浏览器调用控制器保存更改时,控制器从数据库中检索对象的新副本:ex。 DomainClass.get(params.id)。然后保存更改。
  3. 当第二个浏览器调用控制器保存更改时,控制器再次从数据库中检索该对象的新副本。这次它是版本1,但这是无关紧要的,因为版本与检索对象无关,只有在保存时。因此,第二次保存成功,因为版本与数据库中已有的版本相匹配。
  4. 要创建您正在寻找的条件,您必须使用它,直到两次保存之间的重叠时间足够长,以便发生这样的序列:

    def a = DomainClass.get(1)
    def b = DomainClass.get(1)
    
    /* change a */
    a.save()
    
    /* change b */
    b.save() // This would throw an exception because b.version does not match what's in the database.
    

答案 1 :(得分:0)

我认为确切的答案是 Grail并没有在开箱即用的请求之间实现乐观锁定

混淆来自于脚手架为edit.gsp中的代码生成了一些必要的代码(但不够):

<g:hiddenField name="version" value="${myInstance?.version}" />

必须在您的控制器save()操作中执行额外检查,以便将您的记录的原始呈现版本与确切保存时刻中的数据库版本进行比较,并通知用户并发编辑操作期间发生脏读。类似的东西:

if (new Long(params.version) != myInstance.version) {
    myInstance.errors.rejectValue("", "", "This record has been updated by a concurrent user. Please reopen it before saving again");
    myInstance.version = new Long(params.version); //the version to be re-rendered must still be the original one, just in case the user try saving again without refreshing
    return render(view: "edit", model: [myInstance: myInstance]);
}