I've introduced a TransactionService that I use in my controllers to execute optimistic transactions. It should
- try to execute a given transaction (= closure)
- roll it back if it fails and
- try it again if it fails
It basically looks like this:
class TransactionService {
transactional = false // Because withTransaction is used below anyway
def executeOptimisticTransaction(Closure transaction) {
def success = false
while (!success) {
anyDomainClass.withTransaction { status ->
try {
transaction()
success = true
} catch(Exception e) {
status.setRollbackOnly()
}
}
}
}
}
It is a little more complex, e.g. it uses different Thread.sleeps before trying again and aborts at some stage, but that doesn't matter here. It's called from controllers who pass the transaction to be safely executed as a closure.
My Problem: When the service hits a org.hibernate.StaleObjectStateException due to concurrent updates, it keeps trying again but the Exception never disappears.
I already tried different things like re-attaching domain classes in the transaction passed by the controller, clearing the session in the service or in the controller, but it didn't help. What am I missing?
I should note that I got an error that my "Transaction Manager does not allow nested transactions" when I tried to insert a savePoint before transaction() is called using status.createSavepoint(). I tried this because I also suspected that the error exists because the transaction is passed from the controller to the service and that I needed to start a new / nested transaction to avoid it, but as the error shows this is not possible in my case.
Or maybe is passing the transaction as a closure the problem?
I assume that the domain class used before the .withTransaction doesn't matter, or does it?