Grails下如何防止异常导致事务回滚?
Posted
技术标签:
【中文标题】Grails下如何防止异常导致事务回滚?【英文标题】:How do I prevent exceptions from causing a transaction rollback under Grails? 【发布时间】:2013-03-27 06:19:21 【问题描述】:我的 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
我看到的是,当我在清理旧文件时遇到异常时,我会记录并吞下它,但它仍然会导致事务回滚,即使我已经完成了域对象的更新。
如何限制范围事务在清理之前完成,或者是否有其他方法可以让清理异常不导致回滚?
为了记录,我使用的是 Grails 2.1.1
【问题讨论】:
这个问题会导致“org.hibernate.LazyInitializationException: could not initialize proxy - no Session”。我以为我已经正确捕获了我的异常,但没有意识到我的 try/catch 的位置为时已晚! 【参考方案1】:您可以使用注释来进行更细粒度的事务划分。默认情况下,服务是事务性的,所有公共方法都是事务性的。但是,如果您使用任何 @Transactional
注释,Grails 不会让所有事情都成为事务性的——您拥有完全的控制权。
运行时异常会自动触发回滚,但检查的异常不会。尽管 Groovy 不要求您捕获已检查的异常,但该功能是 Spring 的东西,它不知道 Groovy 异常处理。
事务是通过将服务类实例包装在代理中来实现的。如果一个异常“逃脱”了代理,无论它是否被捕获,回滚都已经发生了。
所以你有几个选择。将updateSomething
注释为@Transactional
但不要注释cleanUpOldFile
:
import org.springframework.transaction.annotation.Transactional
@Transactional
def updateSomething(domainObj)
...
def cleanUpOldFile(...)
...
您还可以用一个或多个不应回滚事务的未经检查的异常(或在其他用例中应该回滚的检查异常)来注释 cleanUpOldFile,例如
@Transactional(noRollbackFor=[FooException, BarException])
def cleanUpOldFile(...)
...
【讨论】:
我不完全理解的一件事是,我的 catch 块不应该捕获所有异常,包括运行时和检查的异常吗? Spring 怎么知道我有任何异常? 我认为它注意到服务层之间的异常,在我的情况下,我有一个服务层与另一个服务层通信。这是唯一对我有意义的事情。 如果您所做的只是将@Transactional(noRollbackFor=[FooException, BarException]) 添加到cleanUpOldFile(),那么这对类中的所有其他方法有什么影响?他们是否仍然是事务性的? 如果我指定noRollbackFor=[Exception]
,这是否仍会针对未捕获的异常回滚?
自 Grails v3.2.0 起,检查异常也会触发回滚。 source【参考方案2】:
除了@Burt Beckwith 的回答之外,如果您有一项服务,您只是不想要交易(在我的情况下我实际上是这样做的),您可以通过添加关闭所有公共方法上的交易
static transactional = false
到服务类。
【讨论】:
是的,这对于不写入数据库的实用程序服务很常见。这是一个很好的性能优化,否则您会为每次调用创建不必要的事务而产生少量成本。以上是关于Grails下如何防止异常导致事务回滚?的主要内容,如果未能解决你的问题,请参考以下文章