如何防止异常导致Grails下的事务回滚?

Jam*_*hon 14 grails transactions grails-orm

我的Grails服务存在一个问题,即与事务无关的吞没异常导致事务回滚,即使它与域对象的持久性无关.

在我的服务中,我有一些东西

updateSomething(domainObj) {
    def oldFilename = domainObj.filename
    def newFilename = getNewFilename()

    domainObj.filename = newFilename
    domainObj.save(flush: true)

    try {
        cleanUpOldFile(oldFilename)
    } catch (cleanupException) {
        // oh well, log and swallow
    }
}
Run Code Online (Sandbox Code Playgroud)

我所看到的是,当我清理旧文件时遇到异常,我记录它并吞下它,但它仍然导致事务回滚,即使我已经完成了更新域对象.

如何在清理之前限制范围事务完成,或者是否有其他方法可以使清理异常不导致回滚?

只是为了记录我使用Grails 2.1.1

Bur*_*ith 28

您可以使用注释来执行更细粒度的事务划分.默认情况下,服务是事务性的,所有公共方法都是事务性的.但是,如果您使用任何@Transactional注释,Grails不会使所有事务都成为事务 - 您可以完全控制.

运行时异常会自动触发回滚,但检查异常则不会.即使Groovy不要求您捕获已检查的异常,该功能也是一个Spring,它不了解Groovy异常处理.

通过将服务类实例包装在代理中来实现事务.如果异常"逃避"代理,无论它是否被捕获,回滚都已经发生.

所以你有几个选择.注释updateSomething@Transactional但不注释cleanUpOldFile:

import org.springframework.transaction.annotation.Transactional

@Transactional
def updateSomething(domainObj) {
...
}

def cleanUpOldFile(...) {
   ...
}
Run Code Online (Sandbox Code Playgroud)

您还可以使用一个或多个不应该回滚事务的未经检查的异常来注释cleanUpOldFile(或者在其他用例中检查应该检查的异常),例如

@Transactional(noRollbackFor=[FooException, BarException])
def cleanUpOldFile(...) {
   ...
}
Run Code Online (Sandbox Code Playgroud)

  • 有一点我不完全理解,我的catch块是否应该捕获所有异常,包括运行时和检查?Spring怎么知道我有任何例外? (2认同)
  • 从 Grails v3.2.0 开始,检查异常也会触发回滚。[来源](https://docs.grails.org/latest/guide/services.html#_declarative_transactions) (2认同)

Jam*_*hon 8

除了@Burt Beckwith的答案之外,如果你有一个你不想要交易的服务(我实际上是在我的情况下这样做),你可以通过添加所有公共方法来关闭交易

static transactional = false
Run Code Online (Sandbox Code Playgroud)

到服务类.

  • 是的,这对于不写入数据库的实用程序服务来说非常常见.这是一个很好的性能优化,因为否则会产生为每个调用创建不必要事务的小成本. (6认同)