@Transactional rollbackFor example using spring boot

In this post, we will see @Transactional rollbackFor example using spring boot. We will also see what is the effect of using rollbackFor, how to use in the real-time scenario.

Basic points related to @Transactional rollbackFor attribute.

We can use rollbackFor attribute as a parameter with @Transnational annotation as follows.

@Transactional(rollbackFor = { RuntimeException.class })
public void saveBook(Book book) {
	// some logic
}

The rollbackFor attribute indicates Spring to rollback the transaction for the given exception(the exceptions which we have provided as the value of rollbackFor).

If we don’t provide any value for rollbackFor or we don’t mention rollbackFor as a parameter with @Transnational annotation, spring provides default rollback for RuntimeException/unchecked exception and Error. Let’s see default values for rollbackFor.

What we write.

@Transactional
public void saveBook(Book book) {
	// some logic
}

What spring understand.


@Transactional(rollbackFor = { RuntimeException.class, Error.class })
public void saveBook(Book book) {
	// some logic
}

In fact, Spring adds some more attributes and corresponding default values, but here we are going to talk about rollbackFor only.

Spring doesn’t provide default rollback for Checked exception and its child exception(for example Exception or any custom exception which extends Exception class).

We can have multiple Exception class values for rollbackFor.

Let’s see some sample code snippet which will behave the same.

First One.

@Service
public class BookServiceImpl implements BookService {

	@Autowired
	private BookRepository bookRepository;

	@Autowired
	private StoryService storyService;

	@Transactional
	public void saveBook(Book book) {
		Book book1 = null;
		// save book
		book1 = bookRepository.save(book);
	}
}

Second One.


@Service
public class BookServiceImpl implements BookService {

	@Autowired
	private BookRepository bookRepository;

	@Autowired
	private StoryService storyService;

	@Transactional(rollbackFor = {RuntimeException.class})
	public void saveBook(Book book) {
		Book book1 = null;
		// save book
		book1 = bookRepository.save(book);
	}

}

Note – Observe the difference in above code snippets.

@Transactional rollbackFor example using spring boot.

Prerequisites.

  • 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 rollbackFor example using spring boot

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

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

}

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> {
		
}

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.repository.BookRepository;
import com.springtransaction.service.BookService;


@Service
public class BookServiceImpl implements BookService {

	@Autowired
	private BookRepository bookRepository;

	@Transactional(rollbackFor = {RuntimeException.class})
	public void saveBook(Book book) {
		Book book1 = null;
		// save book
		book1 = bookRepository.save(book);
	}
}

BookController.java

package com.springtransaction.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
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);				
	}							
}

We are good now. Let’s run the application.

Testing of example using postman.

Let’s check the DB.


@Transactional rollbackFor example using spring boot

We have the book 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 with saveBook()? Suppose we have some logic which throws NullpointerException after saving the book. Let’s modify the 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.repository.BookRepository;
import com.springtransaction.service.BookService;


@Service
public class BookServiceImpl implements BookService {

	@Autowired
	private BookRepository bookRepository;


	@Transactional(rollbackFor = {RuntimeException.class})
	public void saveBook(Book book) {

		Book book1 = null;
		// save book
		book1 = bookRepository.save(book);
		
		String s1 = null;
		System.out.println(s1.length());

	}

}

Test from the postman.

@Transactional rollbackFor example using spring boot

Think! In DB we will have a record for book or not? The answer is no. Since we are using @Transactional annotation and rollbackFor = {RuntimeException.class}, our transaction will rollback because we have NullpointerException in saveBook () method. Again one question may come in mind, we have defined RuntimeException.class for rollbackFor, why the transaction is getting rollback even for NullpointerException? Don’t forget NullpointerException is a child of RuntimeException. Spring will take care transaction rollback for RuntimeExeception as well as its all child exception.

Again if we don’t provide rollbackFor = {RuntimeException.class} with @Transactional annotation what will happen? We have already discussed by default we will have  rollbackFor = {RuntimeException.class}, spring will take care.

Real-time use of rollbackFor.

So far we have learned, Spring provides a default value for rollbackFor i.e RuntimeException and Error. Even we don’t use rollbackFor attribute our transaction will be rollbacked for any RuntimeException.

So what is the use of rollbackFor ? Don’t forget spring is taking care of the only RuntimeException, not for Checked Exception. What will happen is some checked exception comes in our application.

If we do not use rollbackFor attribute for the checked exception, the transaction will not be rollbacked for the checked exception, which we may don’t want. If we want to rollback the transaction for all kind of exception including checked and unchecked we need to specify the rollbackFor attribute something like below.

rollbackFor = {Exception.class}

Now we are saying to spring, hey spring if you see any exception checked or unchecked please rollback the transaction(don’t save the record in DB).

Let’s see an example where we will use rolbackFor attribute with a value RuntimeException.class and will throw checked exception intentionally. Let’s see what happens.

Modify the classes and interface as below.

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.repository.BookRepository;
import com.springtransaction.service.BookService;


@Service
public class BookServiceImpl implements BookService {

	@Autowired
	private BookRepository bookRepository;

	@Transactional(rollbackFor = {RuntimeException.class})
	public void saveBook(Book book) throws Exception{
		Book book1 = null;
		// save book
		book1 = bookRepository.save(book);		
		throw new Exception("this is checked exception throwing intentinally");
	}
}

BookService.java

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) throws Exception;

}

BookController.java

package com.springtransaction.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
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) throws Exception{
		bookService.saveBook(book);
		return book;
	}		
}

If you run above changes and test from the postman, even we are using @Transactional annotation with rollbackFor = RuntimeException.class, we will have one book record in DB. Why? Because we are throwing checked exception in BookServiceImpl.java but we are saying to spring hey spring rollback the transaction for only RuntimeException/unchecked exception not for the checked exception.

@Transactional rollbackFor example using spring boot

Let’s check the DB.

Let’s modify the BookServiceImpl.java something like below.

rollbackFor = {Exception.class}

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.repository.BookRepository;
import com.springtransaction.service.BookService;


@Service
public class BookServiceImpl implements BookService {

	@Autowired
	private BookRepository bookRepository;

	@Transactional(rollbackFor = {Exception.class})
	public void saveBook(Book book) throws Exception{

		Book book1 = null;
		// save book
		book1 = bookRepository.save(book);		
		throw new Exception("this is checked exception throwing intentinally");
	}
}

test from the postman and see the DB we will not any record.

That is the expected result. We are telling to spring just rollback the transaction for any exception.

That’s all about @Transactional rollbackFor example using spring boot.

You may like –

Spring Transaction Management Docs.