2 Optimistic Locking - Reference Documentation
Authors: Yasuharu NAKANO
Version: 0.4
2 Optimistic Locking
In order to handle optimistic locking, usewithOptimisticLock method which is injected to domain class by the plugin.
withOptimisticLockmethod compares a version of modification base with current version.withOptimisticLock's closure is invoked only when "version of modification base" < "current persistent version" istrue.- When "version of modification base" < "current persistent version" is
falseorOptimisticLockingFailureExceptionis catched, - it invokes
onConflictclosure if exists. - it sets a field error message to the domain instance.
- When an exception except
OptimisticLockingFailureExceptionis thrown, it's not caught and just thrown up.
sampleDomain.withOptimisticLock(modificationBaseVersion) { Object domain -> // Operations which might causes OptimisticLockingFailureException.
// Here is invoked only when a version of modification base < current persistent version.}.onConflict { Object domain, Throwable caused -> // Operations to handle a failure of optimistic locking
// e.g. to render edit page to re-input
}Type of modificationBaseVersion
modificationBaseVersion allows either Number (Long, Integer, etc.) or String.
So you can pass a value of params in a controller.
Skip a version comparation
If a copmaration of version is unnecessary, you can omit themodificationBaseVersion argument as follows:sampleDomain.withOptimisticLock { Object domain ->
// Here is always invoked regardless of the versions.
}onConflict is optional
If you have nothing to do when a conflict occurs, you can omitonConflict closure:sampleDomain.withOptimisticLock(modificationBaseVersion) { Object domain ->
// …
}Closure arguments
The first argument of the closure equals to delegate object. So you can use each one as you want.sampleDomain.withOptimisticLock(modificationBaseVersion) { Object domain ->
assert domain.is(sampleDomain)
}.onConflict { Object domain ->
assert domain.is(sampleDomain)
}onConflict's closure are optional.
So you can omit them.
The followings are all valid.sampleDomain.withOptimisticLock { /* … */ }.onConflict { Object domain, Throwable caused -> /* … */ }
sampleDomain.withOptimisticLock { /* … */ }.onConflict { Object domain -> /* … */ }
sampleDomain.withOptimisticLock { /* … */ }.onConflict { -> /* … */ }Return value
If you want to use a return value of a closure, you can get it fromreturnValue property.def result = sampleDomain.withOptimisticLock(modificationBaseVersion) { Object domain ->
return "OK"
}.onConflict { Object domain, Throwable caused ->
return "NG"
}
assert result.returnValue == "OK"onConflict's closure is returned.
assert result.returnValue == "NG"Flushing session
In case of using persistence methods (e.g.save method), you must be flush a session.def result = sampleDomain.withOptimisticLock(modificationBaseVersion) { Object domain ->
domain.save(flush: true)
}.onConflict { Object domain, Throwable caused ->
return "NG" // In case of error, you can catch and handle it here.
}Field Error Binding
By default, this plugin will bind a field error to an instance with a message codedefault.optimistic.locking.failure.
If you don't want it because i18n message is unnecessary or you want to use your custom message code, you can disable it by errorBinding parameter:sampleDomain.withOptimisticLock(modificationBaseVersion, [errorBinding: false]) { Object domain -> … }
modificationBaseVersion is unnecessary, you can simply write it:sampleDomain.withOptimisticLock(errorBinding: false) { Object domain -> … }
sampleDomain.withOptimisticLock(errorBinding: false) { Object domain -> … }.onConflict { Object domain -> domain.errors.rejectValue("your.custom.code", [123] as Object[], "Default sample") }
errorBinding parameter is true by default.
So if you don't specify the parameter, the default field error will be bound.
You can configure the default value in Config.groovy:grails.plugins.domainlocking.defaultErrorBinding = false