乐观锁定是否应该捕获并发更新问题?
通过并发更新,我的意思是两个不同的用户都尝试更新具有相同版本号的对象。例如,如果存在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。这是正确的行为吗?
答案 0 :(得分:1)
是的,Grails乐观锁定将检测并发更新并抛出异常。但是,根据您所描述的内容,您不会进行并发更新。让我们仔细看看。
DomainClass.get(params.id)
。然后保存更改。要创建您正在寻找的条件,您必须使用它,直到两次保存之间的重叠时间足够长,以便发生这样的序列:
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]);
}