@Transactional REQUIRED vs REQUIRES_NEW example in spring boot

In this tutorial, we will see @Transactional REQUIRED vs REQUIRES_NEW example in spring boot and also will discuss what is the difference between Propagation.REQUIRED and Propagation.REQUIRES_NEW, when we should use Propagation.REQUIRED and when we can go for Propagation.REQUIRES_NEW. Let’s see brief about these attribute.

Understanding @Transactional REQUIRED vs REQUIRES_NEW.

@Transactional(propagation=Propagation.REQUIRED) – If there is not an existing transaction it will create a new transaction. In case if there is already an existing transaction it will not create a new transaction.

@Transactional(propagation=Propagation.REQUIRES_NEW) – Even if there is already an existing transaction it will create a new transaction i.e it will always create a new transaction.

To understand Propagation.REQUIRED  and Propagation.REQUIRES_NEW just assume following scenario. Suppose we want to save two entity Book.java and Stroy.java(both are independent entity means they don’t have either inheritance relationship or association relationship). We have two separate methods to save the book and story entity. The method which saving story is getting called from saveBook() method. Is it confusing? Let’s see in below code snippet.

We have two separate implementations classes BookServiceImpl.java and StoryServiceImpl.java with saveBook() and saveStory() method respectively. saveStory() is getting called from saveBook().

BookServiceImpl.java


package com.springtransaction.serviceimpl;

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

import com.springtransaction.entity.Book;
import com.springtransaction.entity.Story;
import com.springtransaction.repository.BookRepository;
import com.springtransaction.service.BookService;
import com.springtransaction.service.StoryService;

@Service
public class BookServiceImpl implements BookService {

	@Autowired
	private BookRepository bookRepository;

	@Autowired
	private StoryService storyService;

	@Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED)
	public void saveBook(Book book) {

		Book book1 = null;
		// save book
		book1 = bookRepository.save(book);
		

		Story story = new Story();
		story.setStoryName("story name1");
		// save story
		storyService.saveStory(story);
		

		
	}

}

StoryServiceImpl.java

package com.springtransaction.serviceimpl;

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

import com.springtransaction.entity.Story;
import com.springtransaction.repository.StoryRepository;
import com.springtransaction.service.StoryService;

@Service
public class StoryServiceImpl implements StoryService {
	@Autowired
	private StoryRepository storyRepository;
	
	//Saving a story is in a new transaction.
	@Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRES_NEW)
	public void saveStory(Story story) {
		storyRepository.save(story);
	}

}

Did you notice we are using propagation = Propagation.REQUIRED with saveBook() method and propagation = Propagation.REQUIRES_NEW with saveStory() method and calling saveStory() from saveBook() method(We are doing intentionally we can call saveStory() separately). The above code will run fine and we will have one entry for book and story in the database if we insert some book record from the postman(later we will see in the example).

When we say propagation = Propagation.REQUIRED it will check is any transaction is there or not. As of now for saveBook() we don’t have any transaction. It will create a new transaction for saveBook(). So what will happen if we say propagation = Propagation.REQUIRES_NEW, even we have an existing transaction it will create a new transaction(for example in this case saveStory() considered as a separate transaction).

Again question comes why we are doing these stuff? what benefit we will get?

Let’s modify BookServiceImple.java something like below –

package com.springtransaction.serviceimpl;

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

import com.springtransaction.entity.Book;
import com.springtransaction.entity.Story;
import com.springtransaction.repository.BookRepository;
import com.springtransaction.service.BookService;
import com.springtransaction.service.StoryService;

@Service
public class BookServiceImpl implements BookService {

	@Autowired
	private BookRepository bookRepository;

	@Autowired
	private StoryService storyService;

	@Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED)
	public void saveBook(Book book) {

		Book book1 = null;
		// save book
		book1 = bookRepository.save(book);
		

		Story story = new Story();
		story.setStoryName("story name1");
		// save story
		storyService.saveStory(story);
		
		//thorw exception so that book will not save in DB
		throw new RuntimeException("this exception in throwing intentionally");
		
	}

}

In the above example, we are intentionally throwing RuntimeException, since we have an exception in our code for this transaction i.e saveBook() method(something wrong happened), spring will not save the book record in the database. Now, what about the Story entity? It will save in Database or not? 


Yes, we will have a record for Story entity in the database. Why? Don’t forget we are using propagation = Propagation.REQUIRES_NEW with saveStory() which means we have a new transaction for saveStory() which doesn’t depends on previous one i.e saveBook(). Hope it make sense. Let’s see an example for both cases.

@Transactional REQUIRED vs REQUIRES_NEW example in spring boot and Oracle.

  • JDK 1.8
  • Oracle 10g
  • Eclipse
  • maven
  • postman

Create maven project, Don’t forget to check ‘Create a simple project (skip)’click on next. Fill all details(GroupId – springtransactionexample, ArtifactId – springtransactionexample and name – springtransactionexample) and click on finish. Keep packaging as the jar.

Modify the pom.xml with below code.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>springtransactionexample</groupId>
  <artifactId>springtransactionexample</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>springtransactionexample</name>
  
  <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.2.RELEASE</version>
 </parent>
  <dependencies>
	<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
      
   </dependencies>
   
   
    
   
</project>

Create the below directory structure.

@Transactional REQUIRED vs REQUIRES_NEW example in spring boot

Step 5 – Define the following classes and interfaces.

Book.java

package com.springtransaction.entity;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name = "book")
public class Book {
	 
	 @Id
	 @GeneratedValue(strategy = GenerationType.AUTO)
	 private int bookId;
	 
	 @Column(name="book_name")
	 private String bookName;
	 
	 @Column(name="auther_name")
	 private String autherName;
	 
	 @Column(name="price")
	 private int price;
	 
	

	public String getAutherName() {
		return autherName;
	}

	public void setAutherName(String autherName) {
		this.autherName = autherName;
	}

	public int getPrice() {
		return price;
	}

	public void setPrice(int price) {
		this.price = price;
	}

	

	public String getBookName() {
		return bookName;
	}

	public void setBookName(String bookName) {
		this.bookName = bookName;
	}

	public int getBookId() {
		return bookId;
	}

	public void setBookId(int bookId) {
		this.bookId = bookId;
	}

	
	 
	 
}

Story.java

package com.springtransaction.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "story")
public class Story {
	 @Id
	 @GeneratedValue(strategy = GenerationType.AUTO)
	 private int storyId;
	 
	 @Column(name="story_name")
	 private String storyName;

	public int getStoryId() {
		return storyId;
	}

	public void setStoryId(int storyId) {
		this.storyId = storyId;
	}

	public String getStoryName() {
		return storyName;
	}

	public void setStoryName(String storyName) {
		this.storyName = storyName;
	}
	 
	 
}

BookService.java – interface

package com.springtransaction.service;



import java.util.List;

import org.springframework.stereotype.Component;

import com.springtransaction.entity.Book;


@Component
public interface BookService {
	public void saveBook(Book book);

	
}

StoryService.java – interface


package com.springtransaction.service;

import org.springframework.stereotype.Component;

import com.springtransaction.entity.Story;

@Component
public interface StoryService {
public void saveStory(Story story);
}

BookRepository.java – interface


package com.springtransaction.repository;

import java.io.Serializable;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.data.repository.CrudRepository;

import org.springframework.stereotype.Repository;

import com.springtransaction.entity.Book;

@Repository
public interface BookRepository extends CrudRepository<Book,Serializable> {
		
}


StoryRepository.java – interface

package com.springtransaction.repository;

import java.io.Serializable;

import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

import com.springtransaction.entity.Story;

@Repository
public interface StoryRepository extends CrudRepository<Story,Serializable>{

}

BookServiceImpl.java

package com.springtransaction.serviceimpl;

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

import com.springtransaction.entity.Book;
import com.springtransaction.entity.Story;
import com.springtransaction.repository.BookRepository;
import com.springtransaction.service.BookService;
import com.springtransaction.service.StoryService;

@Service
public class BookServiceImpl implements BookService {

	@Autowired
	private BookRepository bookRepository;

	@Autowired
	private StoryService storyService;

	@Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED)
	public void saveBook(Book book) {

		Book book1 = null;
		// save book
		book1 = bookRepository.save(book);
		

		Story story = new Story();
		story.setStoryName("story name1");
		// save story
		storyService.saveStory(story);
		

		
	}

}

StoryServiceImpl.java

package com.springtransaction.serviceimpl;

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

import com.springtransaction.entity.Story;
import com.springtransaction.repository.StoryRepository;
import com.springtransaction.service.StoryService;

@Service
public class StoryServiceImpl implements StoryService {
	@Autowired
	private StoryRepository storyRepository;
	
	//Saving a story is in a new transaction.
	@Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRES_NEW)
	public void saveStory(Story story) {
		storyRepository.save(story);
	}

}

BookController.java

package com.springtransaction.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import com.springtransaction.entity.Book;
import com.springtransaction.service.BookService;

@RestController
@RequestMapping("/book")
public class BookController {
	@Autowired
	private BookService bookService;

	
	@RequestMapping(value = "/savebook",method = RequestMethod.POST)
        @ResponseBody
        public Book saveBook(@RequestBody Book book) {
		bookService.saveBook(book);
		
		return book;
	}
	
	
}

JpaConfig.java

package com.springtransaction.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@Configuration
@EnableJpaRepositories(basePackages = "com.springtransaction.repository")
public class JpaConfig {

}

SpringMainExample.java

package com.springtransaction.springtransaction;



import org.hibernate.SessionFactory;
import org.hibernate.jpa.HibernateEntityManagerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;

import com.springtransaction.entity.Book;
import com.springtransaction.service.BookService;

@SpringBootApplication(scanBasePackages={"com.springtransaction"})
@EntityScan("com.springtransaction.*") 
public class SpringMainExample {
	

	
	
	public static void main(final String[] args) {
		final ConfigurableApplicationContext configurableApplicationContext = SpringApplication
				.run(SpringMainExample.class, args);
		
		
	}		
	
	
}

Testing using postman.

We are done now. Let’s run the application and test using postman.

Let’s check the database.

We have the book as well as story record in the database. Yes, we don’t have any problem in code, no exception occurs, so the record is inserted in database and transaction completed. What will happen if we have some problem in saveBook() and saveStory() logic is fine? Let’s see.

Let’s modify BookServiceImpl.java something like below.

package com.springtransaction.serviceimpl;

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

import com.springtransaction.entity.Book;
import com.springtransaction.entity.Story;
import com.springtransaction.repository.BookRepository;
import com.springtransaction.service.BookService;
import com.springtransaction.service.StoryService;

@Service
public class BookServiceImpl implements BookService {

	@Autowired
	private BookRepository bookRepository;

	@Autowired
	private StoryService storyService;

	@Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED)
	public void saveBook(Book book) {

		Book book1 = null;
		// save book
		book1 = bookRepository.save(book);
		

		Story story = new Story();
		story.setStoryName("story name1");
		// save story
		storyService.saveStory(story);
		
		//thorw exception so that book will not save in DB
		throw new RuntimeException("this exception in throwing intentionally");
		
	}

}

Let’s run the application once again.

@Transactional REQUIRED vs REQUIRES_NEW example in spring boot

We have an exception in response which is expected behavior. In this case book entity will not save in the database but the story will save in the database.

@Transactional REQUIRED vs REQUIRES_NEW example in spring boot

That’s all about @Transactional REQUIRED vs REQUIRES_NEW example in spring boot. If you have any doubt or feel any difficulty to run the code, leave a comment.

You may like.

Spring Transaction Management Docs.