I would like to address this comment "However, in 2.3 the team felt it was a good idea (I disagree personally) to introduce a new annotation which could be applied not only to Services but also to Controllers.”
It was never the primary intention of the Grails team to introduce an annotation that could be used on both controllers and services. Our primary intention was to introduce an AST transform that eliminated the need for a proxy and performed better than Spring’s @Transactional. Grails’ @Transactional wires the necessary logic for dealing with transactions directly into the byte code and doesn’t need a proxy hence why we feel it is better than Spring’s version.
The fact that it also works on controllers is merely a side effect of the above that we discovered. Having said that in our experience too many Grails users do not correctly demarcate their transactional boundaries when using controllers. If you mark a controller action as being read-only then Hibernate does not need to perform dirty checking for any queries you do within the scope the action. This greatly improves performance of Grails applications and can very much be a good thing.
We absolutely encourage separation of logic into services, but if you consider the following trivial example:
@Transactional(readOnly=true)
def show(Long id) {
respond Foo.get(id)
}
It is overkill to introduce a service for such a trivial action, but if you don’t demarcate the query with a read-only transaction then Hibernate performs a dirty check on the instance which hurts performance.
Update 22/02/16: As of Grails 3.1 the Spring version is considered deprecated and is disabled by default (you can still re-enable if necessary)