8
votes

I am working with a Grails web application and I get so many strange things when using Grails service. So I want to ask some questions on this to make me understand more about Grails services. This will be very helpful for me (and maybe for others ^_^). Thanks in advance.

  1. If a service is configured with static transactional = true, will it flush every data changes to DB after any method invoke with an object which is dirty and being locked in Hibernate session?

  2. Can I use @Transactional annotation on class level instead of static transactional = true? And is it possbile to put @Transactional(readOnly = true) at some methods that I just want them to read (query, find) data from DB?

  3. How about the transaction inheritance? I mean that, if the parent service is configured static transactional = true, and the child service has it own @Transactional annotation (on class) and some @Transactional(readOnly = true) (on some methods), what will happen if I call a method at parent from the child one?

  4. Does transactional work with an abstract service? Because as I know, with abstract service we cannot initialize its bean, and maybe when starting app, there are some differences in Grails context.

1

1 Answers

7
votes

You should ask one question per question :)

For question #1, yes - the Spring/Hibernate integration ensures that a flush happens before the commit. So calls to save() and delete() will get flushed and there's no need to add flush: true to either. Also, dirty instances that you haven't called save() on will also be flushed unless you call discard().

For #2: Services are transactional by default, so transactional = true is actually redundant - you only need to specify it to say transactional = false. But the automatic transaction wrapper that's created is only done if there are no @Transactional annotations. If you have one or more annotations then those define transaction demarcation. So by default (i.e. no annotations and either no transactional property or transactional = true) all methods are transactional, but if you only annotate a subset of the methods then only those will be transactional.

Typically you would use annotations when you want non-default behavior, i.e. custom propagation, isolation, timeout, etc. (or making it read-only like in your example).

You can annotate at the class level to have the same configuration for all methods, and optionally annotate individual methods to override the class-scope defaults.

For #3 and #4, the standard rules apply (see #2). If the subclass has annotations, then transactional = true from that class or a parent class would be ignored since by using annotations you've told Grails that you're configuring things yourself.

Since abstract services can't be instantiated, the concrete subclass that's actually instantiated will have combined behavior from the base class and itself. If everything is transactional = true then it's simple, and if you have any annotations then they define the rules.

Calling methods in the superclass would behave just like calling methods in the current class. But the behavior is a little counter-intuitive if you haven't considered the implications of Spring's proxy approach. When you call a transactional method, the proxy intercepts the call and joins the active transaction, or starts a new one if needed, or starts a new one if REQUIRES_NEW is specified. But once you're in the real class and call another method, you'll bypass the proxy. So if you call another method with different annotation settings, they'll be ignored. If you're going to do that, see this mailing list discussion for what's going on and how to work with it: http://grails.1312388.n4.nabble.com/non-transactional-service-extends-transactional-service-outcome-td3619420.html