Rolling back a transaction in a Grails Service
Yep that'll do it.
Transactions in Grails are by default handled at a Service method level. If the method returns normally then the transaction will be committed, if a RuntimeException is thrown the transaction will be rolled back.
Note this means even if you use flush:true on while saving an object in the server method the db changes will still be rolled back if you throw a RuntimeException.
For example:
class MyService { def fiddle(id,id2){ def domain = Domain.findById(id) domain.stuff = "A change" domain.save( flush:true ) // will cause hibernate to perform the update statements def otherDomain = OtherDomain.findById(id2) otherDomain.name = "Fiddled" if( !otherDomain.save( flush:true )){ // will also write to the db // the transaction will be roled back throw new RuntimeException("Panic what the hell happened") } }}
What I'm not 100% clear on with Grails is what happens if a checked exception is thrown in straight java/spring world the default behaviour is for the transaction inceptor to commit the transaction, althrough this can be overriden in the config.
Note: there is a caveat, and that is that your db has to support transactions on the tables you are updating. Yes, this is poke at MySQL :)
This also applies to the Domain.withTransaction
method.
Just wanted to add additional comments to the accepted answer, and this was too long to fit into a comment.
What I'm not 100% clear on with Grails is what happens if a checked exception is thrown
By default, the exception must NOT be checked, or the transaction won't be rolled back. Apparently that's a Spring thing.
If you really want to check the exceptions on a method, you can explicitly mark the service method as @Transactional
and use the rollbackFor
argument to list which exceptions should still cause a rollback. (Note that I haven't actually tested this.)
Be aware, though, that marking any one method in a service with @Transactional
disables the automatic wrapping of its other methods with a transaction. So, if you do it for one, you have to do it for all of them. Be sure you really need to declare those checked exceptions ;)
You can read more about this at http://docs.grails.org/latest/guide/services.html.