我正在尝试为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块。
我注意到我可以捕获异常,如果我让它传播到控制器并在那里捕获它,但似乎我无法在服务中实现奇怪的异常处理。
我错过了什么?
答案 0 :(得分:5)
我到底了,我发布它以防其他人遇到这个问题。出现此问题是因为try / catch块处于事务性服务中。虽然grails报告在save()
调用期间抛出异常,但事实上,当提交事务时,它被称为 AFTER 整个方法。
所以看来:
flush: true
对交易服务没有影响我最终通过手动管理交易来解决这个问题,即
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")
}