Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Spring Boot Transactions: Understanding Transaction Propagation

DZone 's Guide to

Spring Boot Transactions: Understanding Transaction Propagation

Need help understanding transaction propagation?

· Java Zone ·
Free Resource

In my previous tutorial, Spring Boot Transaction Management Example, we looked at transactions and implemented declarative transaction management. In this tutorial, we look at propagation and its different types. In the next tutorial, we will be looking at Spring Boot Transaction Rollback and Spring Boot Transaction Isolation.

What Is Transaction Propagation?

Any application involves a number of services or components making a call to other services or components. Transaction propagation indicates if any component or service will or will not participate in a transaction and how will it behave if the calling component/service already has or does not have a transaction created already.
Spring Boot Microservices Transaction Propagation

This tutorial is explained in the following YouTube video.

Getting Started

We will be making use of the Spring Boot transaction project developed in the previous chapter. It had the Organization Service, which makes a call to the Employee Service and the Health Insurance Service.

Also, in the previous example, we added the transaction annotation only to the Organization Service.
But suppose the user wants to call the Employee Service both ways, i.e.:

  • Call using the Organization Service Transaction Propagation Tutorial
  • Call the Employee Service directly.

Transaction Propagation Example

As the Employee Service may also be called directly, we will need to use the transaction annotation with the Employee Service. So, both services — Organization Service and Employee Service — will be using Transaction annotation.

We will be looking at the various propagation scenarios by observing the behavior of the Organization and Employee Service. There are six types of Transaction Propagations:

  • REQUIRED
  • SUPPORTS
  • NOT_SUPPORTED
  • REQUIRES_NEW
  • NEVER
  • MANDATORY

Transaction Propagation — REQUIRED (Default Transaction Propagation)


Transaction Propagation - REQUIRED

Here, both the Organization Service and the Employee Service have the transaction propagation defined as Required. This is the default transaction propagation.

Code

The Organization Service will be as follows:

 package com.javainuse.service.impl;

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

import com.javainuse.model.Employee;
import com.javainuse.model.EmployeeHealthInsurance;
import com.javainuse.service.EmployeeService;
import com.javainuse.service.HealthInsuranceService;
import com.javainuse.service.OrganizationService;

@Service
@Transactional
public class OrganzationServiceImpl implements OrganizationService {

@Autowired
EmployeeService employeeService;

@Autowired
HealthInsuranceService healthInsuranceService;

@Override
public void joinOrganization(Employee employee, EmployeeHealthInsurance employeeHealthInsurance) {
employeeService.insertEmployee(employee);
if (employee.getEmpId().equals("emp1")) {
throw new RuntimeException("thowing exception to test transaction rollback");
}
healthInsuranceService.registerEmployeeHealthInsurance(employeeHealthInsurance);
}

@Override
public void leaveOrganization(Employee employee, EmployeeHealthInsurance employeeHealthInsurance) {
employeeService.deleteEmployeeById(employee.getEmpId());
healthInsuranceService.deleteEmployeeHealthInsuranceById(employeeHealthInsurance.getEmpId());
}
}


The Employee Service will be as follows:

 package com.javainuse.service.impl;

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

import com.javainuse.dao.EmployeeDao;
import com.javainuse.model.Employee;
import com.javainuse.service.EmployeeService;

@Service
@Transactional
public class EmployeeServiceImpl implements EmployeeService {

@Autowired
EmployeeDao employeeDao;

@Override
public void insertEmployee(Employee employee) {
employeeDao.insertEmployee(employee);
}

@Override
public void deleteEmployeeById(String empid) {
employeeDao.deleteEmployeeById(empid);
}

}


Output

EmployeeService called using OrganizationService:

Transaction Propagation - REQUIRED - Output

EmployeeService called directly:

Transaction Propagation - REQUIRED - Call

Transaction Propagation — SUPPORTS


Transaction Propagation - SUPPORTS

Here, both the Organization Service has the transaction propagation defined as Required, while the Employee Service for the transaction propagation is defined as Supports.

Code

The Organization Service will be as follows:

 package com.javainuse.service.impl;

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

import com.javainuse.model.Employee;
import com.javainuse.model.EmployeeHealthInsurance;
import com.javainuse.service.EmployeeService;
import com.javainuse.service.HealthInsuranceService;
import com.javainuse.service.OrganizationService;

@Service
@Transactional
public class OrganzationServiceImpl implements OrganizationService {

@Autowired
EmployeeService employeeService;

@Autowired
HealthInsuranceService healthInsuranceService;

@Override
public void joinOrganization(Employee employee, EmployeeHealthInsurance employeeHealthInsurance) {
employeeService.insertEmployee(employee);
if (employee.getEmpId().equals("emp1")) {
throw new RuntimeException("thowing exception to test transaction rollback");
}
healthInsuranceService.registerEmployeeHealthInsurance(employeeHealthInsurance);
}

@Override
public void leaveOrganization(Employee employee, EmployeeHealthInsurance employeeHealthInsurance) {
employeeService.deleteEmployeeById(employee.getEmpId());
healthInsuranceService.deleteEmployeeHealthInsuranceById(employeeHealthInsurance.getEmpId());
}
}


The Employee Service will be as follows:

 package com.javainuse.service.impl;

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

import com.javainuse.dao.EmployeeDao;
import com.javainuse.model.Employee;
import com.javainuse.service.EmployeeService;

@Service
@Transactional(propagation=Propagation.SUPPORTS)
public class EmployeeServiceImpl implements EmployeeService {

@Autowired
EmployeeDao employeeDao;

@Override
public void insertEmployee(Employee employee) {
employeeDao.insertEmployee(employee);
}

@Override
public void deleteEmployeeById(String empid) {
employeeDao.deleteEmployeeById(empid);
}

}


Output

 EmployeeService called using OrganizationService:Transaction Propagation - SUPPORTS-Code


 EmployeeService called directly:Transaction Propagation - SUPPORTS-Call

Transaction Propagation — NOT_SUPPORTED


Transaction Propagation - NOT_SUPPORTED

Here, for the Organization Service, we have defined the transaction propagation as REQUIRED, and the Employee Service has the transaction propagation defined as NOT_SUPPORTED

Code

The Organization Service will be as follows:

 package com.javainuse.service.impl;

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

import com.javainuse.model.Employee;
import com.javainuse.model.EmployeeHealthInsurance;
import com.javainuse.service.EmployeeService;
import com.javainuse.service.HealthInsuranceService;
import com.javainuse.service.OrganizationService;

@Service
@Transactional
public class OrganzationServiceImpl implements OrganizationService {

@Autowired
EmployeeService employeeService;

@Autowired
HealthInsuranceService healthInsuranceService;

@Override
public void joinOrganization(Employee employee, EmployeeHealthInsurance employeeHealthInsurance) {
employeeService.insertEmployee(employee);
if (employee.getEmpId().equals("emp1")) {
throw new RuntimeException("thowing exception to test transaction rollback");
}
healthInsuranceService.registerEmployeeHealthInsurance(employeeHealthInsurance);
}

@Override
public void leaveOrganization(Employee employee, EmployeeHealthInsurance employeeHealthInsurance) {
employeeService.deleteEmployeeById(employee.getEmpId());
healthInsuranceService.deleteEmployeeHealthInsuranceById(employeeHealthInsurance.getEmpId());
}
}


The Employee Service will be as follows:

 package com.javainuse.service.impl;

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

import com.javainuse.dao.EmployeeDao;
import com.javainuse.model.Employee;
import com.javainuse.service.EmployeeService;

@Service
@Transactional(propagation=Propagation.NOT_SUPPORTED)
public class EmployeeServiceImpl implements EmployeeService {

@Autowired
EmployeeDao employeeDao;

@Override
public void insertEmployee(Employee employee) {
employeeDao.insertEmployee(employee);
}

@Override
public void deleteEmployeeById(String empid) {
employeeDao.deleteEmployeeById(empid);
}

}


Output

EmployeeService called using OrganizationService:

Transaction Propagation - NOT_SUPPORTED Code

 EmployeeService called directly:

Transaction Propagation - NOT_SUPPORTED Call

Transaction Propagation — REQUIRES_NEW


Transaction Propagation - REQUIRES_NEW

Here, for the Organization Service, we have defined the transaction propagation as REQUIRED, and the Employee Service has the transaction propagation defined as REQUIRES_NEW.

Code

The Organization Service will be as follows:

 package com.javainuse.service.impl;

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

import com.javainuse.model.Employee;
import com.javainuse.model.EmployeeHealthInsurance;
import com.javainuse.service.EmployeeService;
import com.javainuse.service.HealthInsuranceService;
import com.javainuse.service.OrganizationService;

@Service
@Transactional
public class OrganzationServiceImpl implements OrganizationService {

@Autowired
EmployeeService employeeService;

@Autowired
HealthInsuranceService healthInsuranceService;

@Override
public void joinOrganization(Employee employee, EmployeeHealthInsurance employeeHealthInsurance) {
employeeService.insertEmployee(employee);
if (employee.getEmpId().equals("emp1")) {
throw new RuntimeException("thowing exception to test transaction rollback");
}
healthInsuranceService.registerEmployeeHealthInsurance(employeeHealthInsurance);
}

@Override
public void leaveOrganization(Employee employee, EmployeeHealthInsurance employeeHealthInsurance) {
employeeService.deleteEmployeeById(employee.getEmpId());
healthInsuranceService.deleteEmployeeHealthInsuranceById(employeeHealthInsurance.getEmpId());
}
}


The Employee Service will be as follows:

 package com.javainuse.service.impl;

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

import com.javainuse.dao.EmployeeDao;
import com.javainuse.model.Employee;
import com.javainuse.service.EmployeeService;

@Service
@Transactional(propagation=Propagation.REQUIRES_NEW)
public class EmployeeServiceImpl implements EmployeeService {

@Autowired
EmployeeDao employeeDao;

@Override
public void insertEmployee(Employee employee) {
employeeDao.insertEmployee(employee);
}

@Override
public void deleteEmployeeById(String empid) {
employeeDao.deleteEmployeeById(empid);
}

}


Output

 EmployeeService called using OrganizationService:

Transaction Propagation - REQUIRES_NEW Call


 EmployeeService called directly:

Transaction Propagation - REQUIRES_NEW Code

Transaction Propagation — NEVER

Transaction Propagation - NEVER

Here, for the Organization Service, we have defined the transaction propagation as REQUIRED, and the Employee Service has the transaction propagation defined as NEVERs.

Code

The Organization Service will be as follows:

 package com.javainuse.service.impl;

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

import com.javainuse.model.Employee;
import com.javainuse.model.EmployeeHealthInsurance;
import com.javainuse.service.EmployeeService;
import com.javainuse.service.HealthInsuranceService;
import com.javainuse.service.OrganizationService;

@Service
@Transactional
public class OrganzationServiceImpl implements OrganizationService {

@Autowired
EmployeeService employeeService;

@Autowired
HealthInsuranceService healthInsuranceService;

@Override
public void joinOrganization(Employee employee, EmployeeHealthInsurance employeeHealthInsurance) {
employeeService.insertEmployee(employee);
if (employee.getEmpId().equals("emp1")) {
throw new RuntimeException("thowing exception to test transaction rollback");
}
healthInsuranceService.registerEmployeeHealthInsurance(employeeHealthInsurance);
}

@Override
public void leaveOrganization(Employee employee, EmployeeHealthInsurance employeeHealthInsurance) {
employeeService.deleteEmployeeById(employee.getEmpId());
healthInsuranceService.deleteEmployeeHealthInsuranceById(employeeHealthInsurance.getEmpId());
}
}


The Employee Service will be as follows:

 package com.javainuse.service.impl;

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

import com.javainuse.dao.EmployeeDao;
import com.javainuse.model.Employee;
import com.javainuse.service.EmployeeService;

@Service
@Transactional(propagation=Propagation.NEVER)
public class EmployeeServiceImpl implements EmployeeService {

@Autowired
EmployeeDao employeeDao;

@Override
public void insertEmployee(Employee employee) {
employeeDao.insertEmployee(employee);
}

@Override
public void deleteEmployeeById(String empid) {
employeeDao.deleteEmployeeById(empid);
}

}


Output

 EmployeeService called using OrganizationService :

Transaction Propagation - NEVER Call


 EmployeeService called directly:

Transaction Propagation - NEVER Code

Transaction Propagation — MANDATORY


Transaction Propagation - MANDATORY

Here, for the Organization Service, we have defined the transaction propagation as REQUIRED, and the Employee Service has the transaction propagation defined as MANDATORY.

Code

The Organization Service will be as follows:

 package com.javainuse.service.impl;

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

import com.javainuse.model.Employee;
import com.javainuse.model.EmployeeHealthInsurance;
import com.javainuse.service.EmployeeService;
import com.javainuse.service.HealthInsuranceService;
import com.javainuse.service.OrganizationService;

@Service
@Transactional
public class OrganzationServiceImpl implements OrganizationService {

@Autowired
EmployeeService employeeService;

@Autowired
HealthInsuranceService healthInsuranceService;

@Override
public void joinOrganization(Employee employee, EmployeeHealthInsurance employeeHealthInsurance) {
employeeService.insertEmployee(employee);
if (employee.getEmpId().equals("emp1")) {
throw new RuntimeException("thowing exception to test transaction rollback");
}
healthInsuranceService.registerEmployeeHealthInsurance(employeeHealthInsurance);
}

@Override
public void leaveOrganization(Employee employee, EmployeeHealthInsurance employeeHealthInsurance) {
employeeService.deleteEmployeeById(employee.getEmpId());
healthInsuranceService.deleteEmployeeHealthInsuranceById(employeeHealthInsurance.getEmpId());
}
}


The Employee Service will be as follows:

 package com.javainuse.service.impl;

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

import com.javainuse.dao.EmployeeDao;
import com.javainuse.model.Employee;
import com.javainuse.service.EmployeeService;

@Service
@Transactional(propagation=Propagation.MANDATORY)
public class EmployeeServiceImpl implements EmployeeService {

@Autowired
EmployeeDao employeeDao;

@Override
public void insertEmployee(Employee employee) {
employeeDao.insertEmployee(employee);
}

@Override
public void deleteEmployeeById(String empid) {
employeeDao.deleteEmployeeById(empid);
}

}


Output

 EmployeeService called using OrganizationService:

Transaction Propagation - MANDATORY Call

 EmployeeService called directly:

Transaction Propagation - MANDATORY Code

So, the summary will be as follows:

Propagation Behavior
REQUIRED Always executes in a transaction. If there is an existing transaction, it uses it. If none exists, then only a new one is created.
SUPPORTS It may or may not run in a transaction. If the current transaction exists, then it is supported. If none exists, then it gets executed without a transaction.
NOT_SUPPORTED Always executes without a transaction. If there is an existing transaction, it gets suspended.
REQUIRES_NEW Always executes in a new transaction. If there is an existing transaction, it gets suspended.
NEVER Always executes without any transaction. It throws an exception if there is an existing transaction
MANDATORY

Always executes in a transaction. If there is an existing transaction, it is used. If there is no existing transaction, it will throw an exception.

Topics:
spring boot ,transaction management ,spring transaction manager ,spring transactional ,spring boot tutorial ,java

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}