如何克服grails服务中的StaleObjectStateException

时间:2010-12-07 10:42:18

标签: hibernate grails transactions service optimistic-locking

我引入了一个TransactionService,我在控制器中使用它来执行乐观事务。它应该

  • 尝试执行给定的事务(=闭包)
  • 如果失败则回滚
  • 如果失败则再次尝试

它基本上是这样的:

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()
        }
      }
    }
  }
}

有点复杂,例如它在再次尝试之前使用不同的Thread.sleeps并在某个阶段中止,但这并不重要。它是从传递事务的控制器调用的,以便作为闭包安全地执行。

我的问题:当服务由于并发更新而遇到org.hibernate.StaleObjectStateException时,它会再次尝试,但异常永远不会消失。

我已经尝试了不同的东西,例如在控制器传递的事务中重新附加域类,清除服务或控制器中的会话,但它没有帮助。我错过了什么?

我应该注意到,当我尝试在使用status.createSavepoint()调用transaction()之前插入savePoint时,我的“事务管理器不允许嵌套事务”出错。我试过这个,因为我还怀疑错误存在,因为事务从控制器传递到服务,我需要启动一个新的/嵌套事务来避免它,但是因为错误显示这在我的情况下是不可能的。

或者可能是将事务作为关闭问题传递出来的?

我假设.withTransaction之前使用的域类无关紧要,或者是吗?

1 个答案:

答案 0 :(得分:1)

它不是闭包本身,但我相信transaction内部有一些陈旧的变量引用。 如果您尝试仅传递在执行时重新读取其对象的闭包,该怎么办?像

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

我不认为在没有在Hibernate中重新读取对象的情况下“刷新”对象是不可能的。

是的,你打电话给.withTransaction()的班级无关紧要。

对于更新计算总计/评级的示例,这是一个问题本身的数据重复。我宁愿:

  1. 创建一个(Quartz)作业,根据一些“脏”标志更新评级 - 这可能会节省一些DB CPU的更新时间;
  2. 或者在SQL或HQL中执行,例如Book.executeQuery('update Rating set rating=xxx')将使用最新的评级。如果您正在为重负载进行优化,那么无论如何都不会以Groovy方式执行所有操作。不要在Grails中保存Rating对象,只能阅读它们。