发生Grails UnexpectedRollbackException:不确定原因

发生Grails UnexpectedRollbackException:不确定原因,exception,grails,transactions,Exception,Grails,Transactions,我有以下代码: class ServiceA { def save(Object object) { if (somethingBadComesBack) { throw new CustomRuntimeException(data) } } } class ServiceB { def serviceA def save(Object object) { try { serviceA.sa

我有以下代码:

class ServiceA {

   def save(Object object) {
      if (somethingBadComesBack) {
         throw new CustomRuntimeException(data)
      }
   }
}

class ServiceB {

   def serviceA

   def save(Object object) {
      try {
         serviceA.save(object)
         // do more stuff if good to go
      } catch(CustomRuntimeException e) {
        // populate some objects with errors based on exception
      }
   }
}

class ServiceC {

    def serviceB

    def process(Object object) {
       serviceB.save(object)
       if (object.hasErrors() {
          // do some stuff
       }else{
         // do some stuff
       }

       def info = someMethod(object)
       return info
    }
}

class SomeController {

   def serviceC

   def process() {

     def object = .....
     serviceC.save(object) // UnexpectedRollbackException is thrown here

   }
}
调用
ServiceA.save()
并发生异常时,
ServiceA.save()
在尝试返回时抛出一个
UnexpectedRollbackException

我做了以下工作:

try {
   serviceC.process(object)
}catch(UnexpectedRollbackException e) {
   println e.getMostSpecificCause()
}
我得到:

org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
我不知道从哪里开始寻找如何解决这个问题。

看起来,服务A中抛出的未经检查的异常将使事务只能回滚,即使一旦捕获


上面的文档说txn传播级别为,这意味着如果我的内存为我服务的话,同一事务将从您的服务C共享到A。您能让服务A的
save
方法抛出一个选中的异常而不是RuntimeException,以避免后者的自动回滚吗?或者禁用服务上的事务,如果这是您的选择?

您使用运行时异常回滚事务,但这是欺骗-它利用了副作用。运行时异常会自动回滚事务,因为您不需要捕获它们,因此假设如果抛出一个异常,则它不是预期的,默认行为是回滚。您可以将方法配置为不回滚特定的预期运行时异常,但这有点罕见。选中的异常不会回滚异常,因为在Java中,它们必须在抛出的
中捕获或声明,所以必须显式抛出或回避它;不管怎样,你都有机会再试一次

有意回滚事务的正确方法是对当前的
TransactionStatus
调用
setRollbackOnly()
,但这在服务方法中无法直接访问(它位于
withTransaction
块中,因为它是闭包的参数)。但是很容易找到:导入
org.springframework.transaction.interceptor.TransactionSpectSupport
并调用
TransactionSpectSupport.currentTransactionStatus().setRollbackOnly()
。这将要求您重新编写代码,因为不会出现要捕获的异常,因此您需要检查它是否已使用
TransactionSpectSupport.currentTransactionStatus().isRollbackOnly()
回滚

我不确定这是Grails问题还是标准行为,但当我调试时,有3个提交调用,其中有3个不同的
TransactionStatus
实例。只有第一个设置了rollback标志,但是第二个知道第一个,并且还可以。第三个被认为是一个新的事务,它触发了与您看到的相同的异常。为了解决这个问题,我在第二和第三种服务方法中添加了以下内容:

def status = TransactionAspectSupport.currentTransactionStatus()
if (!status.isRollbackOnly()) status.setRollbackOnly()
以链接回滚标志。这起作用了,我没有得到
意外的回滚异常


将其与选中的异常相结合可能更容易。它仍然过于昂贵,因为它将不必要地填充stacktrace,但是如果您调用
setRollbackOnly()
并抛出选中的异常,您将能够使用与现在相同的常规工作流。

谢谢Burt。我将尝试这种方法,看看我能得到什么。我会回来报告…Grails2.3.7默认情况下将包含一个处理这种情况的功能:@FlareCoder-感谢您分享这个!!我刚刚从grails 2.3.6升级到2.3.7,现在问题已经解决了。请像这样修复您的答案:if(status.isRollbackOnly())=>if(!status.isRollbackOnly())