3

我正在尝试为 Hibernate 抛出的乐观锁类​​型异常实现异常处理,但我遇到了一个奇怪的问题。看来我无法捕捉到任何 Gorm 异常。

例如,我的服务中有以下代码:

try {
  User user = User.get(1);
  Thread.sleep(10000);
  user.viewedAt(new Date());
  user.save(flush:true);
} catch (OptimisticLockingException ex) {
  log.error("Optimistic lock exception");
} catch (StaleObjectStateException ex) {
  log.error("Optimistic lock exception");
}

当我用两个线程点击这个块时,它会爆炸并且异常传播到 Grails 的标准异常处理程序。即使报告的异常是 .catch 块也不会被调用StaleObjectStateException

我注意到如果我让它传播到控制器并在那里捕获它,我可以捕获异常,但似乎我无法在服务中实现异常处理,这很奇怪。

我错过了什么?

4

2 回答 2

7

我查到了这个问题,我把它贴出来以防其他人遇到这个问题。出现此问题是因为 try/catch 块位于事务服务中。尽管 grails 报告在调用期间抛出了异常,但save()实际上它是在整个方法之后,即事务提交时调用的。

所以看起来:

  1. flush: true对交易服务没有影响
  2. 在事务服务中捕获与 GORM 相关的异常是不可能的,至少在没有一些工作的情况下是不可能的

我终于通过自己手动管理事务来解决这个问题,即

try {
  User.withNewTransaction {
    User user = User.get(id); // Must reload object
    .. // do stuff
    user.save(flush:true)
  }
} catch (OptimisticLockingException ex) {
  ...
}

我希望这对其他人有用!

于 2012-09-17T08:18:56.367 回答
1

我花了一些时间来解决这个问题,并编写了一个更完整的解决方案来处理 Grails 中的乐观锁定异常情况。

首先,虽然堆栈跟踪中报告的异常是 StaleObjectStateException,但引发的实际异常是 HibernateOptimisticLockingFailureException(不是“OptimisticLockingException”)。其次,如果你想把它概括为处理修改域对象的任意闭包,你需要重新抛出闭包内抛出的异常。

以下静态函数将获取一个对象和一个对该对象进行操作的闭包,将其保存,如果失败,则重试,直到成功:

public static retryUpdate(Object o, Closure c) throws Exception {
    def retVal
    int retryCount = 0
    while (retryCount < 5) {
        try {
            Model.withTransaction { status ->
                retVal = c(status)
                o.save()
            }
            return retVal
        } catch (HibernateOptimisticLockingFailureException e) {
            log.warn "Stale exception caught saving " + o
            if (++retryCount >= 3) { // if retry has failed three times, pause before reloading
                Thread.sleep(1000)
            }
            o.refresh()
        } catch (UndeclaredThrowableException e2) {
            // rethrow exceptions thrown inside transaction
            throw e2.getCause()
        }
    }

    return null
}

在这种情况下,模型是任何 GORM 模型类,不管是哪一个。特别是它是否是传入对象的类并不重要。

使用示例:

AnotherModelClass object = AnotherModelClass.get(id)
retryUpdate(object) {
    object.setField("new value")
}
于 2014-04-17T02:59:35.297 回答