Spring boot transaction management with Mandatory 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.
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.

Mandatory Propagation

  • Mandatory Propagation means if a transaction is already available then make use of the same transaction.
  • If the transaction is not available then a exception is thrown.

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 Mandatory 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.MANDATORY)
  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: No existing transaction found for transaction marked with propagation ‘mandatory’
org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation ‘mandatory’

From the above logs, we can conclude that an exception is thrown if a transaction does not exist.

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.MANDATORY. Here Spring checks if there is an existing transaction available but as of now, there is no transaction available hence the saveManager method throws a IllegalTransactionStateException exception.

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 in a single transaction.

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 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.MANDATORY. Here Spring checks if there is any 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 Mandatory propagation hence spring uses the same TRANSACTION-1 to save the Manager object.

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

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

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

1 thought on “Spring boot transaction management with Mandatory Propagation level”

Leave a Comment