DEV Community

JavaInUse
JavaInUse

Posted on

Spring Boot Transactions - Understanding Transaction Propagation

In previous tutorial - Spring Boot Transaction Management Example we saw what are transactions and implemented declarative transaction management. In this tutorial we will be understanding what is propagation and its different types. In next tutorial we will be looking at Transaction Rollbacks for checked exceptions using Spring Boot.

Video

This tutorial is explained in the below Youtube Video.

Lets Begin-

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 transaction and how will it behave if the calling calling component/service already has or does not have a transaction created already.

Spring Boot Microservices Transaction Propagation

We will be making use of the Spring Boot Transaction Project we developed in previous chapter. It had the Organization service which makes a call to the Employee Service and the Health Insurance Service.
Also previous example we had added transaction annotation only to the Organization service.
But suppose the user wants to call the Employee Service in both ways i.e.

  • Call using Organization service
    Transaction Propagation Tutorial

  • Call the the Employee Service directly.
    Transaction Propagation Example

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

We will be looking at the various propagation scenarios by observing the behaviour 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());
    }
}
Enter fullscreen mode Exit fullscreen mode

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);
    }
Enter fullscreen mode Exit fullscreen mode

}
Output
EmployeeService called using OrganizationService -
Transaction Propagation - REQUIRED - Output

EmployeeService called directly -
Transaction Propagation - REQUIRED - Call

Transaction Propagation - SUPPORTS

Transaction Propagation - SUPPORTS
Transaction Propagation - SUPPORTS

Here both the Organization Service has the transaction propagation defined as Required while Employee Service 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());
    }
}
Enter fullscreen mode Exit fullscreen mode

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);
    }

}
Enter fullscreen mode Exit fullscreen mode

Output
EmployeeService called using OrganizationService -
Transaction Propagation - SUPPORTS-Code

EmployeeService called directly -
Transaction Propagation - SUPPORTS-Call
Transaction Propagation - NOT_SUPPORTED

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 have 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());
    }
}
Enter fullscreen mode Exit fullscreen mode

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);
    }

}
Enter fullscreen mode Exit fullscreen mode

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 have 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());
    }
}
Enter fullscreen mode Exit fullscreen mode

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);
    }

}
Enter fullscreen mode Exit fullscreen mode

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 have 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());
    }
}
Enter fullscreen mode Exit fullscreen mode

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);
    }

}
Enter fullscreen mode Exit fullscreen mode

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 have 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());
    }
}
Enter fullscreen mode Exit fullscreen mode

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);
    }

}
Enter fullscreen mode Exit fullscreen mode

Output
EmployeeService called using OrganizationService -
Transaction Propagation - MANDATORY Call

EmployeeService called directly -
Transaction Propagation - MANDATORY Code
So the summary will be as follows-
Propagation Behaviour
REQUIRED Always executes in a transaction. If there is any 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 current transaction exists then it is supported. If none exists then gets executed with out transaction.
NOT_SUPPORTED Always executes without a transaction. If there is any existing transaction it gets suspended
REQUIRES_NEW Always executes in a new transaction. If there is any existing transaction it gets suspended
NEVER Always executes with out any transaction. It throws an exception if there is an existing transaction
MANDATORY Always executes in a transaction. If there is any existing transaction it is used. If there is no existing transaction it will throw an exception.

Top comments (0)