I know when a transactional method is called from inside the same class it wouldn't be run in a transaction. Spring creates a proxy for transactional methods and wraps them in a try-catch block and rolls back if an exception occurs. Consider the following scenario:
@Transactional
public void saveAB(A a, B b)
{
saveA(a);
saveB(b);
}
@Transactional
public void saveA(A a)
{
dao.saveA(a);
}
@Transactional
public void saveB(B b)
{
dao.saveB(b);
}
Assume saveAB
is called from another object and an exception occurred in saveB
, so saveA
completed successfully but saveB
did not. To my knowledge even though saveA
and saveB
are not transactional (because they are called from the same object), since saveAB
is transactional it should still roll back.
What I don't understand is why do people say self invocation breaks transaction? As long as the caller method is transactional shouldn't everything work as expected? Is there anything I'm missing here?
What I don't understand is why do people say self invocation breaks
transaction?
I never heard that self-invocation breaks transaction. All I know is that self-invocation will not start a new transaction and you already mentioned the reason why.
Snippet from Spring's Transaction Management Specification
Note In proxy mode (which is the default), only external method calls
coming in through the proxy are intercepted. This means that
self-invocation, in effect, a method within the target object calling
another method of the target object, will not lead to an actual
transaction at runtime even if the invoked method is marked with
@Transactional.
If you remove @Transaction
annotation from saveAB()
, you would observe that method saveA()
and saveB()
would not run under transaction even though it is annotated with @Transactional
. However, if you call saveA()
or saveB()
from outside the class, it will run under transaction as expected. That is the reason why people advice to be cautious with self-invocation.
public void saveAB(A a, B b)
{
saveA(a);
saveB(b);
}
@Transactional
public void saveA(A a)
{
dao.saveA(a);
}
@Transactional
public void saveB(B b)
{
dao.saveB(b);
}
In my view, self-invoking any public method is a bad idea.
If you can saveAB and saveB throws an Exception your transaction will rollback.
Seld-invocation would break transnational behaviour if saveAB was not annotated with @Transactional.
So you didn't miss anything in your example.