Close

Spring - Understanding the correct use of Declarative Transaction

[Last Updated: Nov 16, 2018]

To use Spring declarative transaction we need to use @EnableTransactionManagement on the configuration class and @Transactional annotation on the classes/methods where we want to enable the transaction, but that is not enough to enable Spring's declarative transactions correctly.

Spring's declarative transaction is enabled with AOP proxies so when calling a bean method we have to make sure that the call goes through the proxy. Assuming we are using a Spring managed bean which is not annotated with @Transactional itself, calling its method (say A) which is also not annotated with @Transactional will not be in a transaction. Now if this method calls another method B (in the same bean class) which is annotated with @Transactional, will still not enable transaction in B, that's because bean first call is not made via its transactional AOP proxy.

Example

JavaConfig

@Configuration
@ComponentScan
@EnableTransactionManagement
public class AppConfig {

    @Bean
    public DataSource h2DataSource() {
        return new EmbeddedDatabaseBuilder()
                .setType(EmbeddedDatabaseType.H2)
                .build();
    }

    @Bean
    public PlatformTransactionManager transactionManager() {
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(h2DataSource());
        return transactionManager;
    }
}

Example Service bean

In following bean we are going to use TransactionAspectSupport which is a way to know whether transaction within a method is enabled or not.

@Service
public class MyServiceBean {

    public void doSomething(){
        doSomething3();
    }

    @Transactional
    public void doSomething2(){
        doSomething3();
    }

    @Transactional
    public void doSomething3() {
        TransactionStatus status=null;
        try {
           status = TransactionAspectSupport.currentTransactionStatus();
        } catch (NoTransactionException e) {
            System.err.println(e);
        }
        System.out.println(status!=null? "active transaction": "no transaction");

    }
}

Main class

public class ExampleMain {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(AppConfig.class);

        MyServiceBean bean = context.getBean(MyServiceBean.class);
        System.out.println("-- calling doSomething() --");
        bean.doSomething();
        System.out.println("-- calling doSomething2() --");
        bean.doSomething2();
        System.out.println("-- calling doSomething3() --");
        bean.doSomething3();
    }
}
-- calling doSomething() --
no transaction
-- calling doSomething2() --
active transaction
-- calling doSomething3() --
active transaction

Example Project

Dependencies and Technologies Used:

  • spring-context 5.1.2.RELEASE: Spring Context.
  • spring-jdbc 5.1.2.RELEASE: Spring JDBC.
  • h2 1.4.197: H2 Database Engine.
  • JDK 1.8
  • Maven 3.5.4

Spring - Correct use of @Transactional Select All Download
  • transactional-annotation-correct-use
    • src
      • main
        • java
          • com
            • logicbig
              • example
                • MyServiceBean.java

    See Also