如何处理GORM异常

时间:2012-09-17 00:14:44

标签: grails exception-handling gorm

我正在尝试为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的标准异常处理程序。即使报告的异常为StaleObjectStateException,也永远不会调用catch块。

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

我错过了什么?

2 个答案:

答案 0 :(得分:5)

我到底了,我发布它以防其他人遇到这个问题。出现此问题是因为try / catch块处于事务性服务中。虽然grails报告在save()调用期间抛出异常,但事实上,当提交事务时,它被称为 AFTER 整个方法。

所以看来:

  1. flush: true对交易服务没有影响
  2. 在交易服务中捕捉GORM相关的例外是不可能的,至少在没有一些工作的情况下
  3. 我最终通过手动管理交易来解决这个问题,即

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

    我希望这对其他人有用!

答案 1 :(得分: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")
}