开发者

How to overcome StaleObjectStateException in grails Service

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 th开发者_高级运维e 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?


It is not closure itself, but I believe transaction has some stale variable reference inside. What if you try to only pass closures that re-read their objects on execution? Like

executeOptimisticTransaction {
  Something some = Something.get(id)
  some.properties = aMap
  some.save()
}

I don't think it's possbile to "refresh" an object without re-reading it in Hibernate.

Yes, it doesn't matter what class you call .withTransaction() on.

For the example updating calculated totals/ratings, that's a data duplication that's a problem itself. I'd rather either:

  1. create a (Quartz) job that will update ratings based on some "dirty" flag - that might save some DB CPU for a cost of update time;
  2. or do it in SQL or HQL, like Book.executeQuery('update Rating set rating=xxx') that's going to use latest Rating. If you're optimizing for heavy load, you're anyway not going to do everything Groovy-way. Don't save Rating objects in Grails, only read them.
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜