Skip to content


Working with Transactions in Grails

At a certain point when developing an application you will run into a situation that requires you to use transactions. With transactions (in it’s simplest form) you basically control when/whether to commit/rollback a bunch of related sql inserts, updates and/or deletes depending on your business requirements.

I’ve been tinkering around with the different options available in grails to work with transactions and would like to share those different options with who ever might need this info through a very simplistic example just to make things short (keep in mind that behind the scenes it is actually Spring that is managing those transactions)
To begin with, I will explain the scenario we will be working with, and then present the different pieces of source code that I’ve used.

First, this simple application will work with 3 domain classes; Foo, Bar and Baz , all three domains have 1 field called name and a unique constraint on that name. My application has a requirement that
Every time an instance of “Foo” is saved, an instance with the same name should be saved in Bar and Baz
Since each class has a unique constraint on it’s name, saving an instance into Bar and/or Baz could fail when trying to save Foo, in which case, neither Foo nor Bar nor Baz should be saved.

It’s a very weird requirement (and technically you can could achieve those requirements without using transactions), but it serves the purpose of the example. Below is an example flow of someone using the application and what is expected to happen.

Step Action Outcome
1 Add a new Bar with name “Bar1” A new Bar with name “Bar1” is added
2 Add a new Baz with name “Baz1” A new Baz with name “Baz1” is added
3 Add a new Foo with name “Foo1” A new Foo with name “Foo1” is added
A new Bar with name “Foo1” is added
A new Baz with name “Foo1” is added
4 Add a new Foo with name “Bar1” An Error message should be returned and neither Foo, nor Bar, nor Baz should be added since “Bar1” violates a unique constraint on Bar
5 Add a new Foo with name “Baz1” An Error message should be returned and neither Foo, nor Bar, nor Baz should be added since “Baz1” violates a unique constraint on Baz
6 etc…


Implementing Domains and Controllers

Ok, so how do we go about implementing this. Lets start by modeling the domain classes.

Here is an example implementation of the domain Bar, the other classes are exactly the same but with a different class name

class Bar {
    String name
    static constraints = {
        name unique:true
    }
}

Now that I have the domains ready, I will use grails generate-all for each domain class, to generate views and controllers, and then I will do a small change to the Foo controller to add the functionality of adding a Bar and a Baz when saving a Foo instance. (This is will show how things could go wrong)

Add the following to the FooController.groovy under the save action

def save = {
        new Bar(params).save(flush:true)
        new Baz(params).save(flush:true)
        def fooInstance = new Foo(params)
        if (fooInstance.save(flush: true)) {
            flash.message = "${message(code: 'default.created.message', args: [message(code: 'foo.label', default: 'Foo'), fooInstance.id])}"
            redirect(action: "show", id: fooInstance.id)
        }
        else {
            render(view: "create", model: [fooInstance: fooInstance])
        }
    }

What this basically does is save a Bar instance, a Baz instance and a Foo instance when trying to save a Foo instance. We can run the application now and try to follow the same steps outlined above.

You will notice that after completing step 4, a Bar1 instance was added to Foo as well as Baz which is wrong, since a validation constraint was violated on Bar.
I could have checked for validation errors on Bar and Baz to prevent the inconsistent behavior, but the whole purpose is to test out the transactions and how to use them :)


Adding Transactions to the mix

One of many ways to use transactions is using programmatic transactions which involves working with the withTransaction method. I will change the above code to use the withTransaction and see what happens. Change the save action to look something like this.

def save = {
        def fooInstance = new Foo(params)
        Foo.withTransaction{ status ->
            try{
                new Bar(params).save(flush:true, failOnError:true)
                new Baz(params).save(flush:true, failOnError:true)
                fooInstance.save(flush: true, failOnError:true);
            }catch(Exception exp){
                fooInstance.errors.reject(
		    'foo.name.inuse',
                    ["${params.name}"].toArray() as Object[],
                    'Sorry Name [{0}] is already used by either Foor, Bar or Baz!!!')

                status.setRollbackOnly()
            }
        }
        
        if (!fooInstance.hasErrors()) {
            flash.message = "${message(code: 'default.created.message', args: [message(code: 'foo.label', default: 'Foo'), fooInstance.id])}"
            redirect(action: "show", id: fooInstance.id)
        }
        else {
            render(view: "create", model: [fooInstance: fooInstance])
        }
    }

First thing to note, is that the save logic is moved inside the Foo.withTransaction{} block. Everything inside this block is run in a transaction as the name indicates. For simplicity, I have set the failOnError to true when saving, this will throw an exception when we hit the unique constraint, and will allow me to rollback the transaction by setting status.setRollbackOnly(), otherwise, things will carry on in the normal way.
If you try to run the application now, you will notice that it behaves according to the requirements set at the beginning.

Another way to work with transactions, is through grails Services. I will go ahead and create a FooService (grails create-service FooService). The first thing you will notice when you look at the newly created service is the following line of code

static transactional = true

This enables automatic transaction management for the service. Setting this to false, will disable the automatic transaction management. What does automatic transaction management mean? It means that every method in your service is automatically wrapped in a transaction, which will either commit if no exceptions are thrown, or will rollback if an exception is thrown. I will try it out.

I will move the code above to the service, it will look something like this.

class FooService {
    
    static transactional = true

    def saveFoo(params) {
        def foo = new Foo(params)
        new Bar(params).save(flush:true, failOnError:true)
        new Baz(params).save(flush:true, failOnError:true)
        foo.save(flush:true, failOnError:true)
        return foo
    }
}

Since I have failOnError set to true, then an exception will be thrown, which will trigger the rollback of the transaction, otherwise, things will continue normally.

Now In the controller, I will inject the new service and then change the save action as follows

def save = {
        def fooInstance = new Foo(params)
        try{
            fooInstance = fooService.saveFoo(params)
        }catch(RuntimeException e){
            fooInstance.errors.reject(
		'foo.name.inuse',
                ["${params.name}"].toArray() as Object[],
                'Sorry Name [{0}] is already used by either Foor, Bar or Baz!!!')
        }
        
        if (!fooInstance.hasErrors()) {
            flash.message = "${message(code: 'default.created.message', args: [message(code: 'foo.label', default: 'Foo'), fooInstance.id])}"
            redirect(action: "show", id: fooInstance.id)
        }
        else {
            render(view: "create", model: [fooInstance: fooInstance])
        }
    }

This should result in the same exact behavior as the programmatic transaction.

As you can see, it is very easy to work with transactions in grails, although the above example is a little contrived, but hopefully it was helpful to illustrate some of the ways to use transactions in grails. There are other ways to define transactions and control them which are found in the user guide, I haven’t tried them out yet since I didn’t really need to, but fell free to do so.

On another note, if you find yourself usgin failOnError:true a lot, you can always configure it to be the default. As a matter of fact, there is currently a discussion on the grails mailing list whether to change this to be the default behavior or to add a new method to achieve this. You can read all about it over here.

Posted in Programming.

Tagged with .


One Response

Stay in touch with the conversation, subscribe to the RSS feed for comments on this post.

  1. John says

    Thanks. It helps clearing some details about transactions.
    God I love Grails!.



Some HTML is OK

or, reply to this post via trackback.