Does Spring @Transactional attribute work on a private method?
If I have a @Transactional -annotation on a private method in a Spring bean, does the annotation have any effect?
If the @Transactional
annotation is on a public method, it works and open a transaction.
public class Bean {
public void doStuff() {
doPrivateStuff();
}
@Transactional
private void doPrivateStuff() {
}
}
...
Bean bean = (Bean)appContext.getBean("bean");
bean.doStuff();
Solution 1:
The answer your question is no - @Transactional
will have no effect if used to annotate private methods. The proxy generator will ignore them.
This is documented in Spring Manual chapter 10.5.6:
Method visibility and
@Transactional
When using proxies, you should apply the
@Transactional
annotation only to methods with public visibility. If you do annotate protected, private or package-visible methods with the@Transactional
annotation, no error is raised, but the annotated method does not exhibit the configured transactional settings. Consider the use of AspectJ (see below) if you need to annotate non-public methods.
Solution 2:
The Question is not private or public, the question is: How is it invoked and which AOP implementation you use!
If you use (default) Spring Proxy AOP, then all AOP functionality provided by Spring (like @Transactional
) will only be taken into account if the call goes through the proxy. -- This is normally the case if the annotated method is invoked from another bean.
This has two implications:
- Because private methods must not be invoked from another bean (the exception is reflection), their
@Transactional
Annotation is not taken into account. - If the method is public, but it is invoked from the same bean, it will not be taken into account either (this statement is only correct if (default) Spring Proxy AOP is used).
@See Spring Reference: Chapter 9.6 9.6 Proxying mechanisms
IMHO you should use the aspectJ mode, instead of the Spring Proxies, that will overcome the problem. And the AspectJ Transactional Aspects are woven even into private methods (checked for Spring 3.0).
Solution 3:
By default the @Transactional
attribute works only when calling an annotated method on a reference obtained from applicationContext.
public class Bean {
public void doStuff() {
doTransactionStuff();
}
@Transactional
public void doTransactionStuff() {
}
}
This will open a transaction:
Bean bean = (Bean)appContext.getBean("bean");
bean.doTransactionStuff();
This will not:
Bean bean = (Bean)appContext.getBean("bean");
bean.doStuff();
Spring Reference: Using @Transactional
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
!Consider the use of AspectJ mode (see below) if you expect self-invocations to be wrapped with transactions as well. In this case, there won't be a proxy in the first place; instead, the target class will be 'weaved' (i.e. its byte code will be modified) in order to turn
@Transactional
into runtime behavior on any kind of method.
Solution 4:
If you need to wrap a private method inside a transaction and don't want to use AspectJ, you can use TransactionTemplate
.
@Service
public class MyService {
@Autowired
private TransactionTemplate transactionTemplate;
private void process() {
transactionTemplate.executeWithoutResult(status -> processInTransaction());
}
private void processInTransaction(){
//...
}
}