【问题标题】:How to handle GORM exceptions如何处理 GORM 异常
【发布时间】:2012-09-17 00:14:43
【问题描述】:

我正在尝试对 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 块。

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

我错过了什么?

【问题讨论】:

    标签: grails exception-handling grails-orm


    【解决方案1】:

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

    看来:

    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) {
      ...
    }
    

    我希望这对其他人有用!

    【讨论】:

    • 感谢您分享您的发现和解决方案。这似乎很违反直觉,所以如果您觉得自己是慈善机构,请将您发现的内容发布到 Grails 邮件列表中,以便可以更正该行为或添加更好的文档来解决此问题。
    【解决方案2】:

    我花了一些时间来解决这个问题,并编写了一个更完整的解决方案来处理 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")
    }
    

    【讨论】:

    • 我想知道这里是否还有其他事情发生。我在非事务方法(既没有服务类也没有标记事务的方法)中尝试了这个解决方案,它仍然抛出异常,然后向上传播。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-11
    • 2013-08-02
    • 2019-04-25
    相关资源
    最近更新 更多