26

I have a set of @Service beans which inherit core functionality from an abstract class. I marked each of the concrete sub-class services with @Service and @Transactional. The abstract super class contains the public entry point method for each of these services. In other words, I have something similar to the following:

abstract class AbstractService {

    public void process() {
        // Do common initialisation code here
        processSpecific();
        // Do common completion code here
    }

    abstract protected void processSpecific();
}


@Service @Transactional
public class FirstSpecificService extends AbstractService {
    protected void processSpecific() {
        // Do specific processing code here
    }
}


@Service @Transactional
public class SecondSpecificService extends AbstractService {
    protected void processSpecific() {
        // Do different specific processing code here
    }
}

The specific code in each concrete sub-class service makes multiple calls to the DAO layer to make changes to the database, which have REQUIRED as the transactional propagation type.

Now with the services defined as above, I discovered that there was no current transaction inside any of the code of these concrete sub-class services, and each call to the DAO layer was creating a new transaction, doing the changes, committing the transaction and returning.

However, if I annotate the abstract super-class with @Transactional, then a transaction is created properly, and the sub-calls to the DAO layer all participate in the current transaction.

So my question is, what are the rules for inheriting the @Transactional behaviour? Why does Spring not use the @Transactional on the concrete sub-class services that it is actually instantiating? Does the @Transactional need to be on the super-class in this case because that is where the public entry-point method is?

Daniel Daranas
  • 22,454
  • 9
  • 63
  • 116
DuncanKinnear
  • 4,563
  • 2
  • 34
  • 65
  • 1
    By the way, I've had a look at the [relevant SpringSource documentation](http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/htmlsingle/spring-framework-reference.html#transaction-declarative-annotations), but that doesn't seem to cover this. – DuncanKinnear Mar 29 '12 at 03:45
  • I have the exact same problem- in my case I am not even delegating to something like `processSpecific()` that calls DAO , even then marking the subclass @Transactional didn't make process seem to have a Txn open – redzedi Jun 28 '18 at 16:42

3 Answers3

15

From the spring transaction documentation,

Note: In proxy mode (which is the default), only 'external' method calls coming in through the proxy will be intercepted. This means that 'self-invocation', i.e. a method within the target object calling some other method of the target object, won't lead to an actual transaction at runtime even if the invoked method is marked with @Transactional!

Even though you have the @Transactional on your concrete implementation and you are calling process method which is actually transactional by your annotation, but the process method calling processSpecific on your sub class is not transactional because of this internal call.

Look into Weaving.

Kathir
  • 6,136
  • 8
  • 36
  • 67
  • 2
    But won't the proxy be an instance of 'FirstSpecificService'? In which case, the system will be calling the external 'process' method of that instance, and the instance itself is marked as `@Transactional`. I fully understand that internal private or protected methods that are marked as `@Transactional` will not affect the transaction, but that is not what I have. My whole bean is marked as `@Transactional`. – DuncanKinnear Mar 29 '12 at 19:06
  • 1
    No it will not be transactional if its called from the internal method. To start with when you call the process method from external, the proxy instance is transaction controlled and when the process method calls processSpecific, spring does not know about the transactional as the point cut was made on the proxy object not on the sub-class processSpecific method. We had the same problem and and we added load time weaving and everything worked. – Kathir Mar 29 '12 at 21:02
  • 1
    Cab you explain 'load time weaving' with respect to my example above. How would it change the code of these (contrived) example services? – DuncanKinnear Mar 30 '12 at 01:23
  • you will have to add to the context file and provide -javaagent when you start the application with -javaagent:. I think this spring-agent is org.springframework.instrument-3.1.1.RELEASE.jar in the newer spring versions. so it would be -javaagent:. If you are developing webapp and have tomcat there are different steps for this to work. Let me know if u have doubts. – Kathir Mar 30 '12 at 05:36
  • another idea: wire those processspecifics as separate classes, implementing their own interface. – eis Aug 28 '13 at 16:58
2

This is an old question. But I ran into a similar situation and found the explanation in the current javadoc for the @Transactional annotation:

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/transaction/annotation/Transactional.html

At the class level, this annotation applies as a default to all methods of the declaring class and its subclasses. Note that it does not apply to ancestor classes up the class hierarchy; methods need to be locally redeclared in order to participate in a subclass-level annotation.

So, when using inheritance and class level annotation you should annotate the super class and not the sub classes if the super class contains any public methods that should be transactional or do call methods implemented by the sub classes that need to be transactional.

Calling methods which are implemented and annotated @Transactional by the subclasses from methods in the super class without the super class or that method being annotated @Transactional is a bug. And if both are annotated but the attributes on the annotation are inconsistent that is also a bug.

In a good design virtual methods in the super class which are implemented by subclasses should be used by the super class only and they should because of that always have scope protected and never need any @Transactional annotation in the sub class

1

Did you read the part about transaction propagation and how it can be configured using @Transactional?

Another area of interest is that Spring recommends that you should annotate concrete classes (as opposes to annotate interfaces).

matsev
  • 32,104
  • 16
  • 121
  • 156
  • 1
    Yes, as stated above, I have read all those sections of the documentation and none of it seemed to apply in this case. My abstract super-class is **not** an interface, it is code inherited by the actual concrete sub-class. Perhaps you could quote the section of the documentation that you think applies to my example. – DuncanKinnear Mar 29 '12 at 19:01
  • 2
    If you want to make sure that your DAOs always take part in an existing transaction (initiated by your service) you should configure the DAOs to be @Transactional(propagation = [Propagation.MANDATORY](http://static.springsource.org/spring/docs/3.1.x/javadoc-api/org/springframework/transaction/annotation/Propagation.html#MANDATORY)), because using [REQUIRED](http://static.springsource.org/spring/docs/3.1.x/javadoc-api/org/springframework/transaction/annotation/Propagation.html#REQUIRED) will create a new transaction if none exists. – matsev Mar 29 '12 at 19:19
  • 1
    Yes, that would be a way for us to catch these issues in the future, but it still doesn't explain what the inheritance rules are. – DuncanKinnear Mar 29 '12 at 19:57