22
votes

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?

3
By the way, I've had a look at the relevant SpringSource documentation, but that doesn't seem to cover this.DuncanKinnear
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 openredzedi

3 Answers

13
votes

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.

1
votes

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).

0
votes

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