1

Let's say I have the following domain class:

class Book {
    String name
    // more properties here, but name is the only one relevant for this example
}

I'd like to update its name via a view. I'm using the following form to update it:

<g:form action="updateName" id="${book.id}">
    <g:hiddenField name="version"/>
    <g:textField name="name"/>
    ...
</g:form>

In the controller, I'm using the following logic:

def updateName() {
    println("IN UPDATENAME()")
    def book = Book.get(params.id)
    println("VERSION BEFORE ASSIGN: ${book.version})
    book.version = params.long('version')
    println("VERSION AFTER ASSIGN: ${book.version})
    book.name = params.name
    book.save(flush: true)
    ...
}

I'm testing it by opening the same edit page in 2 different browsers. I do updateName in one browser, followed by the other one. The second updateName should be throwing an OptimisticLockingFailureException, but it's not.

I enabled SQL output and this is what I get in the logs:

IN UPDATENAME()
VERSION BEFORE ASSIGN: 0
VERSION AFTER ASSIGN: 0
update book set version=?, name=? where id=? and version=?
binding parameter [1] as [BIGINT] - 1
binding parameter [2] as [STRING] - 'abc123'
binding parameter [3] as [BIGINT] - 1
binding parameter [4] as [BIGINT] - 0

IN UPDATENAME()
VERSION BEFORE ASSIGN: 1
VERSION AFTER ASSIGN: 0
update book set version=?, name=? where id=? and version=?
binding parameter [1] as [BIGINT] - 1
binding parameter [2] as [STRING] - 'def456'
binding parameter [3] as [BIGINT] - 1
binding parameter [4] as [BIGINT] - 1

In other words, in the second call, I was able to successfully assign the version from 1 to 0, which should have caused the exception, but for some reason, the SQL call still checks incorrectly for version = 1 instead of 0. Does anybody know why this is happening?

Daniel T.
  • 37,212
  • 36
  • 139
  • 206
  • Can you show the domain class `Book` as well? `version` in an implicit persistence property (like `id`) provided by GORM which gets updated on a successful `flush`? I am just wondering why are you implementing the boiler plate code for updating the version? – dmahapatro Sep 05 '13 at 02:32
  • @dmahapatro: The intention in manually setting version is (I presume) to make sure that Hibernate checks against the version that has been displayed in the browser. – Thilo Sep 05 '13 at 02:36
  • Yes, that was the intention. The way that Grails does it in a scaffolded controller is a manual check, but the `save()` call should also throw an exception, which it is not. – Daniel T. Sep 05 '13 at 02:53
  • Again, can you show the domain class? – dmahapatro Sep 05 '13 at 02:59
  • What I provided was just an example. What I can tell you though is that I don't have version: false in my mapping, and the version column is updating properly. – Daniel T. Sep 05 '13 at 03:09
  • You cannot set version manually, see answer. – dmahapatro Sep 05 '13 at 04:40

1 Answers1

0

version is a property (and not a field) of Domain Class which is actually defined as an accessor method getVersion(). No setter for version is defined in DefaultGrailsDomainClass.

In case of scaffolded controller as well, there is no where version property is set manually, although the check/validation is done manually. Optimistic Locking is handled by hibernate during session flush when it comes across a mismatch in version, and the mismatch only happens if the second user is a victim of a dirty read.

In your case, either you have to harvest the scaffolded controller logic by manually validating the version or you have to rely on Hibernate to do its job instead of manipulating the version manually which is not feasible.

dmahapatro
  • 49,365
  • 7
  • 88
  • 117