Spring boot transaction management with Requires_New Propagation level

Propagation level in spring decides if the spring has to create a new transaction or the same transaction can be used to perform a database operation.
A transaction is a single unit of work. In a transaction, there can be multiple steps and we have to make sure either all the steps are successful or none should be successful. We know @Transactional annotation helps to achieve transaction management in spring.

Please follow this article before moving further spring boot transaction management here you will understand what is a transaction and how transaction management works in spring-boot with a proper example. This article will be an extension of the above-mentioned article.

REQUIRES_NEW Propagation

  • If the transaction is not available then create a new transaction.
  • If the transaction is available then suspend the previous transaction and create a new transaction.

In our previous example, we have created 2 controllers: EmployeeController and ManagerController.

ManagerController saves only Manager. The below service method is called from ManagerController.

@Override
public String saveManager() {

  String response = "Manager Saved";

  Manager manager = new Manager("Suraj");
  managerDao.save(manager);

  System.out.println(response);
  return response;
}

We will update the above method with REQUIRES_NEW propagation.

package com.getinputs.h2database.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.getinputs.h2database.dao.ManagerDao;
import com.getinputs.h2database.model.Manager;
import com.getinputs.h2database.service.ManagerService;

@Service
public class ManagerServiceImpl implements ManagerService {

  @Autowired
  private ManagerDao managerDao;

  @Override
  @Transactional(propagation = Propagation.REQUIRES_NEW)
  public String saveManager() {

    String response = "Manager Saved";

    Manager manager = new Manager("Suraj");
    managerDao.save(manager);

    System.out.println(response);
    return response;
  }
}

We will update the application.properties with log4j properties, so that we will get a better insight on whether a transaction is created or not.

application.properties

# DB Properties
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect

# Logging Properties
logging.level.ROOT=INFO
logging.level.org.springframework.orm.jpa=DEBUG
logging.level.org.springframework.transaction=DEBUG

Once the above changes are done we will build and run the application.

-> mvn clean install -DskipTests

-> mvn spring-boot:run

Trigger http://localhost:8080/manager

Output: Manager Saved

From the above logs, we can conclude that a new transaction gets created whenever we are defining REQUIRES_NEW propagation level.

Steps after we trigger the Manager Url

1 ) Request reaches the ManagerController and saveManager method gets triggered.

2 ) saveManager method in ManagerController makes a call to saveManager method of ManagerServiceImpl.

3 ) Spring realizes the saveManager method is annotated with @Transactional annotation and Propagation.REQUIRES_NEW. Here Spring checks if there is an existing transaction available but as of now, there is no transaction available hence the saveManager method creates a new transaction.

Now we will check a case when the transaction already exists. For that, we will make the changes in the EmployeeServiceImpl.

EmployeeController saves Employee as well as Manager. The below service method is called from EmployeeController.

@Override
public String saveEmployeeAndManager() {

  String response = "Employee and Manager Saved.";

  Employee employee = new Employee("Surya", 50000);

  employeeDao.save(employee);

  managerService.saveManager(); // This code calls Manager service method

  return response;
}

We will update the above method with the Required propagation.

package com.getinputs.h2database.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.getinputs.h2database.dao.EmployeeDao;
import com.getinputs.h2database.model.Employee;
import com.getinputs.h2database.service.ManagerService;
import com.getinputs.h2database.service.EmployeeService;

@Service
public class EmployeeServiceImpl implements EmployeeService {

  @Autowired
  private EmployeeDao employeeDao;

  @Autowired
  private ManagerService managerService;

  @Override
  @Transactional(propagation = Propagation.REQUIRED)
  public String saveEmployeeAndManager() {

    String response = "Employee and Manager Saved.";

    Employee employee = new Employee("Surya", 50000);
    employeeDao.save(employee);
    managerService.saveManager();

    return response;
  }
}

Once the above changes are done we will build and run the application

-> mvn clean install -DskipTests

-> mvn spring-boot:run

Trigger http://localhost:8080/employee

Output: Employee and Manager Saved.

From the above logs, we can conclude that both employee and manager get saved in the database under 2 different transactions.

Steps after we trigger the Employee Url

1 ) Request reaches the EmployeeController and saveEmployeeAndManager method gets triggered.

2 ) saveEmployeeAndManager method in EmployeeController makes a call to saveEmployeeAndManager method of EmployeeServiceImpl.

3 ) Spring realizes the saveEmployeeAndManager method is annotated with @Transactional annotation and Propagation.REQUIRED. Here Spring checks if there is any existing transaction available but as of now there is no transaction available hence spring creates a new transaction [ TRANSACTION-1 ]

4 ) Employee object is saved in the database under TRANSACTION-1.

5 ) saveEmployeeAndManager method of EmployeeServiceImpl makes a call to saveManager method of ManagerServiceImpl.

6 ) Spring realizes the saveManager method is also annotated with @Transactional annotation and Propagation.REQUIRES_NEW. Here Spring checks if there is an existing transaction available. Now spring comes to know there is already a transaction available TRANSACTION-1 hence it suspends the TRANSACTION-1 and creates a new transaction TRANSACTION-2 to save the Manager object.

7 ) Manager object is saved in the database under TRANSACTION-2.

If you want to get information on the REQUIRED propagation level please check this article: Spring boot transaction management with required propagation level.

https://github.com/getinputs/samples/tree/main/transaction-management-requires-new-propagation

In this article, we have covered a Sample application on how the spring boot transaction management works with the REQUIRES_NEW Propagation level. I hope you found this article interesting and valuable. Please share this article with your friends and help me grow. If you are having any concerns or questions about this article please comment below. If you want to get in touch with me please visit the Contact Me page and send an email.

Leave a Comment