2012-09-17 135 views
3

我试图实现异常处理的休眠引发的乐观锁类​​型异常,但我遇到了一个奇怪的问题。看来我无法抓住任何Gorm异常。如何处理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块也不会被调用。

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

我错过了什么?

回答

5

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

如此看来:

  1. flush: true对交易服务没有影响
  2. 这是不可能赶上交易服务格姆相关的例外,至少在没有一些工作

我最后通过自己手动管理交易来解决这个问题,例如

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

我希望这对别人有用!

+0

感谢您分享您找到的和您的解决方案。这看起来相当反直觉,所以如果您觉得慈善,请将您发现的内容发布到Grails邮件列表中,以便纠正行为或者在此问题上添加更好的文档。 – cdeszaq

+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") 
} 
+0

我想知道这里是否有其他事情发生。我在非事务方法(既没有服务类也没有标记事务的方法)中试过这个解决方案,它仍然抛出异常,然后传播。 –

0

我就遇到了这个问题,并且适合我的需要的解决方法是添加一个对象。在操作对象之前刷新(),以便在db中获得最新的数据。类似的东西:

User user = User.get(1); 
Thread.sleep(10000); 

//workaround here 
user.refresh() 

//manipulation here 
user.viewedAt(new Date()); 
user.save(flush:true); 

希望这会有所帮助,欢呼!