How to Intercept @transactional arguments if @transactional applied at class level

non transactional method calling transactional method
spring transaction propagation
spring transaction management example
spring transaction management example mkyong
spring transaction isolation level
spring boot transaction management
spring transaction management interview questions
transaction management in spring boot jpa

I want to capture the argument of @transactional if it is applied at class level.

for e.g. if @transactional applied at method level like :-

class A {

    @transactional(readOnly= true)
    public void someMethod(){
      // some code...
     }
}

then I am able to Intercept and capture the formal argument i.e. readOnly with this code like :-

@Aspect
@Component
@Order(0)
public class ReadOnlyRouteInterceptor {

    private static final Logger logger = LoggerFactory.getLogger(ReadOnlyRouteInterceptor.class);

    @Around("@annotation(transactional)")
    public Object proceed(ProceedingJoinPoint proceedingJoinPoint, Transactional transactional) {
            if (transactional.readOnly())
             //do something
    }

However the above code will not work if @transactional applied at class level as :-

@transactional(readOnly= true)
class A {

    public void someMethod(){
      // some code...
     }
}

Now in order to Intercept the @transactional annotation which is applied at class level I have following code :-

@Pointcut("@within(org.springframework.transaction.annotation.Transactional *)")
public void beanAnnotatedWithTransactional() {}

@Pointcut("execution(public * *(..))")
public void publicMethod() {}

@Pointcut("publicMethod() && beanAnnotatedWithTransactional()")
public void publicMethodInsideAClassMarkedWithATransactional() {}

My actual problem here is I am unable to check the value of readOnly flag if @transactional is applied at class level.

For type level annotation :

@Around("@within(transactional)")
public Object myMethod(ProceedingJoinPoint pjp, Transactional transactional) throws Throwable {
    boolean readOnly = transactional.readOnly();
    ...
    return pjp.proceed();
}

For method level annotation:

  @Around("execution(public * *(..)) && @annotation(org.springframework.transaction.annotation.Transactional)")
    public Object myMethod(ProceedingJoinPoint pjp) throws Throwable {
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();

        Transactional annotation = method.getAnnotation(org.springframework.transaction.annotation.Transactional.class);
        boolean value = annotation. readOnly();
        ...
        return pjp.proceed();
    }

Another (cleaner) option for method level :

@Around("@annotation(transactional)")
 public Object myMethod(ProceedingJoinPoint pjp, Transactional transactional) throws Throwable {

}

or

With more control over tager :

    @Around("execution(@org.springframework.transaction.annotation.Transactional public * *(..)) && @annotation("transactional")
     public Object myMethod(ProceedingJoinPoint pjp, Transactional transactional) throws Throwable {

}

Spring Transactional pitfalls, 5 common Spring @Transactional pitfalls [Spring & JPA pitfalls series] - Codete blog. The @Transactional annotation is probably the most randomly used If @Transactional annotation wasn't on the method-level, but on the reason why you can't make your spring bean classes final) and overrides its� If applied at class level, all the methods will be by default transactional. Let's try to understand how the annotation works with a simple example: Assume we have a sample service lass

You may use the TransactionSynchronizationManager to get reference to the transaction details.

Following code provides the readonly details of the current active transaction.

import org.springframework.transaction.support.TransactionSynchronizationManager;

@Component
@Aspect
public class TestTransactionalAspect {


    @Pointcut("@within(org.springframework.transaction.annotation.Transactional)")
    public void beanAnnotatedWithTransactional() {}
    @Pointcut("execution(public * *(..))")
    public void publicMethod() {}


    @Around("publicMethod() && beanAnnotatedWithTransactional()")
    public void publicMethodInsideAClassMarkedWithATransactional(ProceedingJoinPoint pjp) {

        try {
            System.out.println("Intercepted "+pjp.toShortString());
            if (TransactionSynchronizationManager.isActualTransactionActive()) {
                System.out.println("Is transaction readonly : "+TransactionSynchronizationManager.isCurrentTransactionReadOnly());
            }
            pjp.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
        }

    }
}

Update:

Assuming this is not a Spring Boot project , @EnableTransactionManagement is mandatory for your spring application to work with transactions. Data will get persisted to database without transactions as well.

@EnableTransactionManagement annotation has to be used on @Configuration classes.

Following is a sample code to enable transaction management in spring

@Configuration
@EnableTransactionManagement
public class AppConfig {

         @Bean
         public FooRepository fooRepository() {
             // configure and return a class having @Transactional methods
             return new JdbcFooRepository(dataSource());
         }

         @Bean
         public DataSource dataSource() {
             // configure and return the necessary JDBC DataSource
         }

         @Bean
         public PlatformTransactionManager txManager() {
             return new DataSourceTransactionManager(dataSource());
         }
     }

Effective Spring Transaction Management, Take a look at this explanation of what transactional management is and what A lot of boilerplate needs to be written and if you want to call another method < bean id="dataSource" class="org.springframework.jdbc.datasource. The reason for this is if you put an annotation on the Interface Level and if� At a high level, Spring creates proxies for all the classes annotated with @Transactional – either on the class or on any of the methods. The proxy allows the framework to inject transactional logic before and after the running method – mainly for starting and committing the transaction.

Based on the answers above I tried with the following code it is very simple.

 /**
     * This Aspect advice will be called only if transactional applied at method level
     */
    @Around("@annotation(transactional)")
    public Object proceedWithMethodLevelAnnotation(ProceedingJoinPoint proceedingJoinPoint,
                                                   Transactional transactional)  {
                                      if( transactional.readOnly() ) {
                //do something
                  }

    /**
     * This Aspect advice will be called only if transactional annotation applied at class level
     */
    @Around("@within(transactional)")
    public Object proceedWithClassLevelAnnotation(ProceedingJoinPoint proceedingJoinPoint,
                                                  Transactional transactional) 
            if( transactional.readOnly() ) {
            //do something
    }
}

16. Transaction Management - Project Metadata API Guide, Transactional code can be tested much more easily than if it used JTA directly. returns a TransactionStatus object, depending on a TransactionDefinition parameter. At the very lowest level exists the TransactionAwareDataSourceProxy class. only external method calls coming in through the proxy are intercepted. If @Transactional annotation wasn’t on the method-level, but on the class-level, then it would be even more difficult to catch that there’s something wrong with this code. Pitfall #2: @Transactional ignored? Have you ever annotated a method with @Transactional (or e.g. @Async) and it didn’t work? As if it was totally ignored by Spring?

Chapter 9. Interceptor bindings, Interceptor bindings may be used to associate interceptors with any managed bean that is not An interceptor instance is a dependent object of the object it intercepts. @Transactional @Interceptor public class TransactionInterceptor If a managed bean has a class-level interceptor binding and is declared final or has a� 4. Understanding @Transactional annotation. At a high level, when a class declares @Transactional on itself or its members, Spring creates a proxy that implements the same interface(s) as the class you’re annotating. In other words, Spring wraps the bean in the proxy and the bean itself has no knowledge of it.

Transactions with Spring and JPA, At a high level, Spring creates proxies for all the classes annotated What's important to keep in mind is that, if the transactional bean is This means that only external method calls that come in through the proxy will be intercepted. Before i work with hibernate There i used Transaction rollback() and� In the case of the following example, the DefaultFooService class is annotated at the class level with the settings for a read-only transaction, but the @Transactional annotation on the updateFoo(Foo) method in the same class takes precedence over the transactional settings defined at the class level. @Transactional(readOnly = true) public

Transaction Propagation and Isolation in Spring @Transactional , Spring applies the class-level annotation to all public methods of this class that we did not annotate with @Transactional. However, if we put the� The Transactional Aspect The Transaction Manager Let’s go over each one and see how they interact. The Transactional Aspect. The Transactional Aspect is an ‘around’ aspect that gets called both before and after the annotated business method. The concrete class for implementing the aspect is TransactionInterceptor.

Comments
  • Couple of observations .1. Second set of code will not work for Type level annotation as mentioned. 2. Consider we have DeptService Interface and DeptServiceImpl class implementing the only method updateDept() in it .The method.getAnnotation() to work without NPE , the interface method should be annotated with Transactional .Only annotating the implementing class method will result in NPE . The annotation details will be always from the interface method and not from the implementing class method , so any override to the behaviour ( eg: readonly ) will not be captured correctly.
  • Just FYI fg78nc your code was Intercepting the type level as well as a method-level annotation. The only challenge I faced was method.getAnnotation(org.springframework.transaction.annotation.Transactional.class) was resulting null even if @transactional(readonly=true) annotation applied at method level(I do not know the reason) whereas declaringClass.getAnnotation(Transactional.class) was returning the correct result if the annotation applied to class level.
  • I tried with this approach but this is not working if @Transactional applied at class level. Both static method i.e. isActualTransactionActive & isCurrentTransactionReadOnlyis returning false.
  • Please confirm if a configuration class is annotated with @EnableTransactionManagement to enable transactions ? isActualTransactionActive false means no transaction is active . The code shared is tested and working as expected
  • configuration class is not annotated with @EnableTransactionManagement. It is only annotated with @transactional(readOnly=true). I am getting isActualTransactionActive false in my aspect advice but able to get the data from the DB. Should I change my service class A like this :-
  • @EnableTransaction @transactional(readOnly=true) class daoImpl { //methods }
  • I am using @Repository annotation on my DaoImpl class hence JPA configuration is not required in my case. I tried your approach but it does not work for method level as well.