我如何知道Grails模型自检索后是否发生了变化?

时间:2013-09-18 00:15:10

标签: hibernate grails gorm

我希望多个用户能够同时在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在思考时发生的更改,可能会做出不同的更改。

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.但是,这条路线不宜长时间运行。