如何在Grails Service中克服StaleObjectStateException [英] How to overcome StaleObjectStateException in grails Service

查看:170
本文介绍了如何在Grails Service中克服StaleObjectStateException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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


  • 尝试执行给定的事务(=闭包)

  • 失败并

  • 如果失败,请再试一次



基本上看起来像这样: p>

 类TransactionService {
transactional = false //因为withTransaction被低于
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由于并发更新,它不断尝试,但异常不会消失。



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



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



或者可能是将事务作为一个闭包传递的问题?



我假设之前使用的域类.withTransaction没有关系,或者它没有?

解决方案

它本身不是关闭的,但我相信事务里面有一些陈旧的变量引用。
如果您只尝试通过在执行时重新读取对象的闭包,该怎么办?像

  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')
  3. code>这将使用最新的评级。如果你正在为重负载进行优化,那么无论如何你都不会去做Groovy的一切。不要将评级对象保存在Grails中,只能阅读它们。


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?

解决方案

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.

这篇关于如何在Grails Service中克服StaleObjectStateException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆