3

我希望多个用户能够同时在网络浏览器中编辑模型,并在保存时检测到冲突(这样第一个写入的用户不会在没有第二个用户明确表示的情况下覆盖他们的更改)。这是示例:

用户 A 检索对象 X 并在他们的浏览器中查看它。

然后用户 B 检索对象 X,在他们的浏览器中查看它,对其进行修改,然后将其保存,从而将其发布到 Grails 中的 REST apisave并将模型上的 a 发布到数据库。

然后用户 A 修改并保存该对象。

我希望应用程序检测到对象 X 在用户 A 检索到它后已被修改,并且我将向用户 A 显示一条合适的消息,其中包含一些选项。

我如何知道模型是否已更改?请注意,这isDirty将不起作用,save(flush:true)也不会起作用。

更新

我看到一些关于 Optimistic Locking 和检测模型更改的答案,因为它已提交。我想检测更改,因为它是由用户检索的。请再次阅读问题。

在这里我将澄清为什么乐观锁定不能解决上述问题。但是,我可以想象可能有一种方法可以让我使用乐观锁定,但是正如下面的答案和文档中所描述的那样,它无济于事。这就是为什么。

乐观锁定在请求中起作用,而不是跨请求。如果第二个用户在第一个用户的更新请求正在进行时更新了一个对象,那么乐观锁定将只允许一个用户执行更新。乐观锁定可防止在同一请求中读取-修改-写入与另一个读取-修改-写入交错。这是乐观锁定所防止的事件的时间线(时间从上到下):

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

从这个示例中,您可以看到用户 1 覆盖了用户 2 的更改。但是用户 1 根据数据库中模型的旧副本进行了更改,如果她在思考时看到用户 2 的更改,她可能会做一些不同的事情。

4

2 回答 2

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)
    }
于 2013-09-18T01:05:33.237 回答
1

为什么感觉save(flush: true)行不通?你不想使用它还是你想别的东西?

您一定已经阅读过Optimistic Locking,它在save(flush: true).
version域类 X 以便版本在成功保存时更新(使用刷新)。

在脏更新时,应用程序会抛出OptimisticLockingFailureException如下所示:

def airport = Airport.get(10)
try {
    airport.name = "Heathrow"
    airport.save(flush: true)
}
catch (org.springframework.dao.OptimisticLockingFailureException e) {
    // deal with exception
}

如果您不想处理如上所示的异常,则在为用户 A 编辑域之前,尝试刷新它,然后更新信息,以获取对象 X 的最新版本。但是,此路线不适合长时间运行的会话。

于 2013-09-18T00:53:04.797 回答