我希望多个用户能够同时在Web浏览器中编辑模型,并在保存时检测到冲突(这样第一个写入的用户没有覆盖他们的更改,而第二个用户没有明确说明) 。这是一个例子:
用户A检索对象X并在浏览器中查看它。
然后,用户B检索对象X,在浏览器中查看它,修改它并保存它,从而导致发送到Grails中的REST api和模型上的save
数据库。
然后用户A修改并保存对象。
我希望应用程序检测到对象X已经被修改,因为用户A检索了它,并且我将向用户A显示一个合适的消息,带有一些选项。
我如何知道模型是否已更改?请注意,isDirty
无效,save(flush:true)
无法使用。
更新
我看到几个答案,谈论乐观锁定和检测模型更改,因为它是提交。我想检测一个更改,因为用户检索。请再次阅读该问题。
这里我将阐明为什么乐观锁定无法解决上述问题。但是,我可以想象我可能有一种方法可以使用乐观锁定,但正如下面的答案所描述的那样,并且正如文档中所描述的那样,它无济于事。这就是原因。
乐观锁定在请求中工作,而不是在请求之间。如果第二个用户在第一个用户正在进行更新请求时更新对象,则乐观锁定将只允许一个用户执行更新。乐观锁定可防止读取 - 修改 - 写入与相同请求中的另一个读取 - 修改 - 写入交错。以下是乐观锁定可防范的事件的时间线(时间从上到下):
User 1 User 2
presses submit presses submit
in update action
| read model
| in update action
| | read model
| | modify model
| | write model
| return
| modify model
| write model - FAILS
return error or something
第一个用户发布模型失败,因为乐观锁定检查检测到记录在读取后被修改。
我想要防范的是以下时间表:
User 1 User 2
visits web app visits web app
clicks link to edit model clicks link to edit model
in edit action
| read model
| render model
return
in edit action
| read model
| render model
return
user edits model user edits model
user thinks... hmm... user submits
in update action
| read model
| modify model from params
| write model
return
user submits
in update action
| read model
| modify model from params
| write model - OOPS! overwrote previous changes
return
从此示例中,您可以看到User 1覆盖了User 2的更改。但是用户1根据数据库中旧模型的副本进行了更改,如果她看到用户2在思考时发生的更改,可能会做出不同的更改。
答案 0 :(得分:2)
正如dmahapatro所说,乐观锁定是内置的。我只想补充一点,Grails脚手架在控制器的更新操作中将此考虑在内。例如,请注意它会检查版本以查看Book自提交以来是否已更新。
def update(Long id, Long version) {
def bookInstance = Book.get(id)
if (!bookInstance) {
flash.message = message(code: 'default.not.found.message', args: [message(code: 'book.label', default: 'Book'), id])
redirect(action: "list")
return
}
if (version != null) {
if (bookInstance.version > version) {
bookInstance.errors.rejectValue("version", "default.optimistic.locking.failure",
[message(code: 'book.label', default: 'Book')] as Object[],
"Another user has updated this Book while you were editing")
render(view: "edit", model: [bookInstance: bookInstance])
return
}
}
bookInstance.properties = params
if (!bookInstance.save(flush: true)) {
render(view: "edit", model: [bookInstance: bookInstance])
return
}
flash.message = message(code: 'default.updated.message', args: [message(code: 'book.label', default: 'Book'), bookInstance.id])
redirect(action: "show", id: bookInstance.id)
}
答案 1 :(得分:1)
为什么你认为save(flush: true)
不起作用?你不想使用它还是想到其他的东西?
您必须阅读save(flush: true)
期间有效的Optimistic Locking
version
域类X,以便在成功保存(使用flush)时更新版本。
在脏更新时,应用会抛出OptimisticLockingFailureException
,如下所示:
def airport = Airport.get(10)
try {
airport.name = "Heathrow"
airport.save(flush: true)
}
catch (org.springframework.dao.OptimisticLockingFailureException e) {
// deal with exception
}
如果您不想处理如上所示的异常,那么在编辑用户A的域之前,请尝试refresh然后更新信息,以获取最新版本的Object X.但是,这条路线不宜长时间运行。