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