104

The question from the title in code:

@Transactional (readonly = true)
public interface FooService {
   void doSmth ();
}


public class FooServiceImpl implements FooService {
   ...
}

vs

public interface FooService {
   void doSmth ();
}

@Transactional (readonly = true)
public class FooServiceImpl implements FooService {
   ...
}
Roman
  • 64,384
  • 92
  • 238
  • 332

5 Answers5

142

From http://static.springsource.org/spring/docs/2.0.x/reference/transaction.html

The Spring team's recommendation is that you only annotate concrete classes with the @Transactional annotation, as opposed to annotating interfaces. You certainly can place the @Transactional annotation on an interface (or an interface method), but this will only work as you would expect it to if you are using interface-based proxies. The fact that annotations are not inherited means that if you are using class-based proxies then the transaction settings will not be recognised by the class-based proxying infrastructure and the object will not be wrapped in a transactional proxy (which would be decidedly bad). So please do take the Spring team's advice and only annotate concrete classes (and the methods of concrete classes) with the @Transactional annotation.

Note: Since this mechanism is based on proxies, 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!

(Emphasis added to the first sentence, other emphasis from the original.)

Community
  • 1
  • 1
Romain Hippeau
  • 24,113
  • 5
  • 60
  • 79
  • Big +1 BTW, I took the liberty of making the quote a quote, so people aren't confused, and added back the italics and such from the original. – T.J. Crowder Jun 26 '10 at 06:26
  • I have read this statement before in Spring docu, but still I cannot understand why _transaction settings will not be recognised by the class-based proxying infrastructure_? I personally think, this is just Spring implementation limitation, not the problem of underlying proxy infrastructure. – dma_k Sep 26 '10 at 12:53
  • Looking at Spring sources one can find out that `JdkDynamicAopProxy` goes through all bean advisors on each method invocation (see also `DefaultAdvisorChainFactory#getInterceptorsAndDynamicInterceptionAdvice()`), which in case of declarative transaction setup, includes `BeanFactoryTransactionAttributeSourceAdvisor`. In its turn `TransactionAttributeSourcePointcut#matches()` is invoked, which should collect the transaction-relative information. It is passed the target class and it can always traverse all interfaces this class implements. Now: why this cannot not work reliably? – dma_k Sep 26 '10 at 12:54
  • 2
    @dma_k - The Java runtime only gives direct access to class annotations inherited from superclasses, or method annotations with no inheritance at all. So Pointcut.matches() would have to actually recursively traverse all interfaces, find the "real" overridden method with reflection, handling bridge methods, overloading, varargs, generics, proxies, and of course do it *fast*. Then you have to deal with diamond inheritance -- which of possibly many inherited `@Transactional` annotations would apply? So, though it's all *possible*, I don't blame Spring. http://jira.springsource.org/browse/SPR-975 – Peter Davis Oct 06 '11 at 19:26
  • To anyone wondering, this is still applicable 11 years later (in Spring 5.3) : https://docs.spring.io/spring-framework/docs/5.3.x/reference/html/data-access.html#transaction-declarative-annotations – Xr. May 06 '21 at 13:17
  • what happen is i put it in both class and interface? – Amir Nov 03 '21 at 00:16
  • @Amir In some cases it will not work and will take you hours/days to figure it out. – Romain Hippeau Nov 04 '21 at 19:33
11

Spring's recommendation is that you annotate the concrete implementations instead of an interface. It's not incorrect to use the annotation on an interface, it's just possible to misuse that feature and inadvertently bypass your @Transaction declaration.

If you've marked something transactional in an interface and then refer to one of its implementing classes elsewhere in spring, it's not quite obvious that the object that spring creates will not respect the @Transactional annotation.

In practice it looks something like this:

public class MyClass implements MyInterface { 

    private int x;

    public void doSomethingNonTx() {}

    @Transactional
    public void toSomethingTx() {}

}
zmf
  • 9,095
  • 2
  • 26
  • 28
10

You can put them on the interface but be warn that transactions may not end up happening in some cases. See the second tip in Secion 10.5.6 of the Spring docs:

Spring recommends that you only annotate concrete classes (and methods of concrete classes) with the @Transactional annotation, as opposed to annotating interfaces. You certainly can place the @Transactional annotation on an interface (or an interface method), but this works only as you would expect it to if you are using interface-based proxies. The fact that Java annotations are not inherited from interfaces means that if you are using class-based proxies (proxy-target-class="true") or the weaving-based aspect (mode="aspectj"), then the transaction settings are not recognized by the proxying and weaving infrastructure, and the object will not be wrapped in a transactional proxy, which would be decidedly bad.

I would recommend putting them on the implementation for this reason.

Also, to me, transactions seem like an implementation detail so they should be in the implementation class. Imagine having wrapper implementations for logging or test implementations (mocks) that don't need to be transactional.

AngerClown
  • 6,149
  • 1
  • 25
  • 28
1

Supporting @Transactional on the concrete classes:

I prefer to architect a solution in 3 sections generally: an API, an Implementation and a Web (if needed). I try my best to keep the API as light/simple/POJO as possible by minimizing dependencies. It's especially important if you play it in a distributed/integrated environment where you have to share the APIs a lot.

Putting @Transactional requires Spring libraries in the API section, which IMHO is not effective. So I prefer to add it in the Implementation where the transaction is running.

Firdous Amir
  • 1,297
  • 5
  • 21
  • 39
0

Putting it on the interface is fine as long all foreseeable implementers of your IFC care about TX data (transactions aren't problems that just databases deal with). If the method doesn't care about TX (but you need to put it there for Hibernate or whatever), put it on the impl.

Also, it might be a bit better to place @Transactional on the methods in the interface:

public interface FooService {
    @Transactional(readOnly = true)
    void doSmth();
}
engfer
  • 126
  • 1
  • 6