Understanding Transaction Propagation in Spring Boot

Understanding Transaction Propagation in Spring Boot

In this tutorial we will be Understanding Transaction Propagation in Spring Boot and understanding what is propagation, its different types

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.

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

  • Call the Employee Service directly.

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)

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:

EmployeeService called directly:

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:

 EmployeeService called directly:

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:

 EmployeeService called directly:

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:

 EmployeeService called directly:

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 :

 EmployeeService called directly:

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:

EmployeeService called directly:

So, the summary will be as follows:

Thanks for reading. If you liked this post, share it with all of your programming buddies!

Further reading

☞ Spring & Hibernate for Beginners (includes Spring Boot)

☞ Spring Framework Master Class - Learn Spring the Modern Way!

☞ Master Microservices with Spring Boot and Spring Cloud

☞ Spring Boot and OAuth2: Getting the Authorization Code

☞ An Introduction to Spring Boot

☞ How to build GraphQL APIs with Kotlin, Spring Boot, and MongoDB?

☞ Build a Rest API with Spring Boot using MySQL and JPA

☞ Angular 8 + Spring Boot 2.2: Build a CRUD App Today!

☞ Spring Boot vs. Spring MVC vs. Spring: How Do They Compare?

☞ Top 4 Spring Annotations for Java Developer in 2019

spring-boot java web-development

Bootstrap 5 Complete Course with Examples

Bootstrap 5 Tutorial - Bootstrap 5 Crash Course for Beginners

Nest.JS Tutorial for Beginners

Hello Vue 3: A First Look at Vue 3 and the Composition API

Building a simple Applications with Vue 3

Deno Crash Course: Explore Deno and Create a full REST API with Deno

How to Build a Real-time Chat App with Deno and WebSockets

Convert HTML to Markdown Online

HTML entity encoder decoder Online

Productive Web Development in Java with IntelliJ IDEA, Spring Boot and Vaadin

We cover tips and tricks to help you become a more productive web app developer. We build a small web app using Vaadin's Java API and Spring Boot. We show you how to set up IntelliJ IDEA to automatically reload your app and browser as you develop and cover our favorite shortcuts and hacks for productive and enjoyable web app development in Java.

Top 10 Advanced Java and Spring Boot Courses for Full-Stack Java Developers

These are best online courses to learn Spring Boot, Advanced Java, Docker, React, Microservices, DEvops, and Angular to become full stack Java developer.

How to Install OpenJDK 11 on CentOS 8

What is OpenJDK? OpenJDk or Open Java Development Kit is a free, open-source framework of the Java Platform, Standard Edition (or Java SE).

Java Spring Boot First App

Step by Step to your First Spring App

Top 10 Java Frameworks For Web App Development: 2020 Edition

Java Frameworks are the pre-written code body through which you are enabled to add your own code. There are numerous Java frameworks that have different leading features, on the basis of the same here, I will discuss Top 10 JAVA Frameworks For Web App Development 2020-2021.