问题
My Grails service is having an issue where a swallowed exception unrelated to a transaction is causing the transaction to rollback even when it is unrelated to the persistance of the domain object.
In my service I have something along the lines of
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
}
}
What I am seeing is that when I have exception when I am cleaning up the old file, I log it and swallow it, but it still causes the transaction to rollback, even though I am already done updating the domain object.
How do I limit the scope transaction to complete before the clean up or is there another way to get the clean up exception to not cause a rollback?
Just for the record I am using Grails 2.1.1
回答1:
You can use annotations to do more fine-grained transaction demarcation. By default services are transactional, and all public methods are transactional. But if you use any @Transactional
annotations, Grails doesn't make everything transactional - you have complete control.
Runtime exceptions automatically trigger rollbacks, but checked exceptions don't. Even though Groovy doesn't required that you catch checked exceptions, the feature is a Spring thing which doesn't know about Groovy exception handling.
Transactions are implemented by wrapping your service class instance in a proxy. If an exception "escapes" the proxy, whether it's then caught or not, the rollback will have already happened.
So you have a few options. Annotate updateSomething
as @Transactional
but don't annotate cleanUpOldFile
:
import org.springframework.transaction.annotation.Transactional
@Transactional
def updateSomething(domainObj) {
...
}
def cleanUpOldFile(...) {
...
}
You can also annotate cleanUpOldFile with one or more unchecked exceptions that shouldn't roll back a transaction (or in other use cases checked exceptions that should), e.g.
@Transactional(noRollbackFor=[FooException, BarException])
def cleanUpOldFile(...) {
...
}
回答2:
In addition to @Burt Beckwith's answer, if you have a service where you just don't want transactions (which I actually did in my case) you can turn off transactions on all public methods by adding
static transactional = false
to the Service class.
来源:https://stackoverflow.com/questions/15823677/how-do-i-prevent-exceptions-from-causing-a-transaction-rollback-under-grails