Spring boot transaction management with Required Propagation level

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.

As we know 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 also 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.

Required Propagation

If we are not declaring any propagation level then it is REQUIRED by default.

REQUIRED propagation means if the transaction is already available then make use of the same transaction else create a new transaction.

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

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

@Override
@Transactional
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;
}

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 methods with 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;
  }
}
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.REQUIRED)
  public String saveManager() {

    String response = "Manager Saved";

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

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

When we build and run the above application and trigger http://localhost:8080/employee from the browser then what happens?

  • The request reaches the EmployeeController and the saveEmployeeAndManager method gets triggered.
  • saveEmployeeAndManager method in EmployeeController makes a call to the saveEmployeeAndManager method of EmployeeServiceImpl.
  • Spring realizes the saveEmployeeAndManager method is annotated with @Transactional annotation and Propagation.REQUIRED. Here Spring checks if there is an existing transaction available but as of now there is no transaction available hence spring creates a new transaction [ TRANSACTION-1 ]
  • Employee object is saved in the database under TRANSACTION-1.
  • saveEmployeeAndManager method of EmployeeServiceImpl makes a call to the saveManager method of ManagerServiceImpl.
  • Spring realizes the saveManager method is also annotated with @Transactional annotation and Propagation.REQUIRED. Here Spring checks if there is an existing transaction available. Now spring comes to know there is already a transaction available TRANSACTION-1. Now since the current saveManager method is having a Required propagation so spring does not create a new transaction.
  • Spring saves the Manager object under TRANSACTION-1.

From the above explanation, we understood the same transaction is used to save Employee as well as Manager. But how can we prove that?

It’s simple we can check the logs. For checking the spring-framework-related logs we will add a few log-related properties in our application.properties file.

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

Build and run the application :

-> mvn clean install -DskipTests

-> mvn spring-boot:run

Trigger http://localhost:8080/employee

Output: Employee and Manager Saved.

Now we will trigger http://localhost:8080/manager

  • The request reaches the ManagerController and saveManager method gets triggered.
  • The saveManager method in ManagerController makes a call to the saveManager method of ManagerServiceImpl.
  • Spring realizes the saveManager method is annotated with @Transactional annotation and Propagation.REQUIRED. Here Spring checks if there is an existing transaction available but as of now there is no transaction available hence spring creates a new transaction [ TRANSACTION-1 ]
  • Manager object is saved in the database under TRANSACTION-1.

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

In this article, we have covered a Sample application on how the spring boot transaction management works with the Required 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.