@Transactional(propagation=Propagation.REQUIRED)
When the propagation setting is PROPAGATION_REQUIRED, a logical transaction scope is created for each method upon which the setting is applied. Each such logical transaction scope can determine rollback-only status individually, with an outer transaction scope being logically independent from the inner transaction scope. Of course, in case of standard PROPAGATION_REQUIRED behavior, all these scopes will be mapped to the same physical transaction. So a rollback-only marker set in the inner transaction scope does affect the outer transaction's chance to actually commit (as you would expect it to).
http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/transaction.html
@Transactional(propagation = Propagation.REQUIRED) in spring?
It doesn't matter. When calling b()
from a()
it won't be going through the proxy, so any transactional attributes on b()
won't be considered.
The example code has 1 transaction open if a()
or b()
is called through the proxy (i.e. outside of the class) and there isn't a transaction in progress already.
Overriding transaction propagation levels for methods having Spring's @transactional
I do believe the only option is to replace TransactionInterceptor
via BeanPostProcessor
, smth. like:
public class TransactionInterceptorExt extends TransactionInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
// here some logic determining how to proceed invocation
return super.invoke(invocation);
}
}
public class TransactionInterceptorPostProcessor implements BeanFactoryPostProcessor, BeanPostProcessor, BeanFactoryAware {
@Setter
private BeanFactory beanFactory;
@Override
public void postProcessBeanFactory(@NonNull ConfigurableListableBeanFactory beanFactory) throws BeansException {
beanFactory.addBeanPostProcessor(this);
}
@Override
public Object postProcessBeforeInitialization(@NonNull Object bean, @NonNull String beanName) throws BeansException {
if (bean instanceof TransactionInterceptor) {
TransactionInterceptor interceptor = (TransactionInterceptor) bean;
TransactionInterceptor result = new TransactionInterceptorExt();
result.setTransactionAttributeSource(interceptor.getTransactionAttributeSource());
result.setTransactionManager(interceptor.getTransactionManager());
result.setBeanFactory(beanFactory);
return result;
}
return bean;
}
}
@Configuration
public class CustomTransactionConfiguration {
@Bean
//@ConditionalOnBean(TransactionInterceptor.class)
public static BeanFactoryPostProcessor transactionInterceptorPostProcessor() {
return new TransactionInterceptorPostProcessor();
}
}
However, I would agree with @jim-garrison suggestion to refactor your spring beans.
UPD.
But you favour refactoring the beans instead of following this approach. So for the sake of completeness, can you please mention any issues/shortcomings with this
Well, there are a plenty of things/concepts/ideas in spring framework which were implemented without understanding/anticipating consequences (I believe the goal was to make framework attractive to unexperienced developers), and @Transactional
annotation is one of such things. Let's consider the following code:
@Transactional(Propagation.REQUIRED)
public void doSomething() {
do_something;
}
The question is: why do we put @Transactional(Propagation.REQUIRED)
annotation above that method? Someone might say smth. like this:
that method modifies multiple rows/tables in DB and we would like to avoid inconsistencies in our DB, moreover
Propagation.REQUIRED
does not hurt anything, because according to the contract it either starts new transaction or joins to the exisiting one.
and that would be wrong:
@Transactional
annotation poisons stacktraces with irrelevant information- in case of exception it marks existing transaction it joined to as rollback-only - after that caller side has no option to compensate that exception
In the most cases developers should not use @Transactional(Propagation.REQUIRED)
- technically we just need a simple assertion about transaction status.
Using @Transactional(Propagation.REQUIRES_NEW)
is even more harmful:
- in case of existing transaction it acquires another one JDBC-connection from connection pool, and hence you start getting 2+ connections per thread - this hurts performance sizing
- you need to carefully watch for data you are working with - data corruptions and self-locks are the consequences of using
@Transactional(Propagation.REQUIRES_NEW)
, cause now you have two incarnations of the same data within the same thread
In the most cases @Transactional(Propagation.REQUIRES_NEW)
is an indicator that you code requires refactoring.
So, the general idea about @Transactional
annotation is do not use it everywhere just because we can, and your question actually confirms this idea: you have failed to tie up 3 methods together just because developer had some assumptions about how those methods should being executed.
Related Topics
Are There Any Java Method Ordering Conventions
Difference Between an Application Server and a Servlet Container
Spring - Injecting a Dependency into a Servletcontextlistener
Log4J2 - Assigning File Appender Filename at Runtime
Javafx and Maven: Nullpointerexception: Location Is Required
Create Java Runtime Image on One Platform for Another Using Jlink
Write a Mode Method in Java to Find the Most Frequently Occurring Element in an Array
How to Do Query Auto-Completion/Suggestions in Lucene
Problems Connecting via Https/Ssl Through Own Java Client
Java Code for Getting Current Time
Is It Bad Practice to Use Reflection in Unit Testing
How to I Output Org.W3C.Dom.Element to String Format in Java
Arrays.Fill with Multidimensional Array in Java