How to overcome StaleObjectStateException in grails Service

为君一笑 提交于 2019-12-10 11:22:28

问题


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?


回答1:


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.


来源:https://stackoverflow.com/questions/4375757/how-to-overcome-staleobjectstateexception-in-grails-service

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!