Spring Transaction Management Example Using Spring Boot

In this tutorial, we will see Spring transaction management example using spring boot. First, we will see some basics about Spring Transaction Management then we will see a complete example.

We will see how to use @Transactional annotation, what will happen if we don’t use @Transaction annotation. We will also cover what are attributes defined for @Transactional annotation and what it does.

Spring Transaction management basics.

The transaction can be defined with ACID properties.

Atomicity – All success or none.
Consistency – Database constraints should not be violated.
Isolation – One transaction should not affect another one.
Durability – It should in Database after commit.

Different ways of transaction management in Spring.

Three are two ways of define Trasaction management.

Programmatic transaction management.
Declarative transaction management.

Programmatic transaction management – Here we need to write some extra code for transaction management. When we say some extra code what does it mean? We need to take care of –

Creating Transaction reference.
Begin transaction.
Commit or rollback of the transaction.

Let’s see sample code.


Transaction transactionRef = entityManager.getTransaction()                  
try {  
   transactionRef.begin();                   
   // business logic                   
   transactionRef .commit();  
}                  
catch(Exception e) {                     
   transactionRef.rollback();  
   e.printStackTrace();                 
}

Declarative transaction management – No need to write extra code for getting a transaction, we can use annotations or XML-based approach to manage the transactions and we can avoid unnecessary code. If we use annotation based approach we can use @Transactional and if we use the XML-based approach we need to configure DataSourceTransactionManager or any other transaction manager in XML as a bean. In this article and the next upcoming article, we will see the annotation based approach.

Sample code for annotation based declarative Spring transaction management.

    @Transactional
    public Book findOneString objectId) {
        return repository.findOne(objectId);
    }

Sample code for XML-based declarative Spring transaction management.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
	
	<bean id="transactionManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource" />
	</bean>	

</beans>

We have seen basics about Spring transaction management example using spring boot.

Understanding Spring @Transactional annotation with example.

Till now we have seen ACID properties and different approaches to implement Spring transaction management. Now we will see how to implement declarative transaction management using annotation.

@Transactional is the key annotation that is responsible for declarative spring transaction management. Apart from this annotation, we use @EnableTransactionManagement.

Let’s see what is the advantage of using @Transactional annotation what happens if we don’t use @Transactional annotation. We will also see how to use @Transactional annotation.

@Transactional annotation used with class, interface or method.

Let’s see a small code snippet which would explain the effect of @Transactional annotation.

Consider you have class BookServiceImpl.java where you have logic to save Book entity.

@Service("bookServiceImpl")
public class BookServiceImpl implements BookService{
	
@Autowired
private BookRepository bookRepository;

public Book saveBook(Book book) {
	Book book1 = bookRepository.save(book);
	int a = 10/0;
	System.out.println(a);
	return book1;
    } 
}

Observe the saveBook() method. In the above code snippet line number 9 will throw ArithmeticException but before that bookRepository.save(book)will get exected. Now the question comes would we have records in the database or not? In the above scenario even we have an exception in our code, we would have a record in Database.

That we might don’t want this to happen. If we have something wrong with our code, data should not persist in the database. Now just modify the code snippet as below, use @Transactional annotation with saveBook() method.


@Service("bookServiceImpl")
public class BookServiceImpl implements BookService{
	
@Autowired
private BookRepository bookRepository;

@Transactional
public Book saveBook(Book book) {
	Book book1 = bookRepository.save(book);
	int a = 10/0;
	System.out.println(a);
	return book1;
 } 
}

Since we are using @Transactional annotation with saveBook() method and we have ArithmeticException at code int a = 10/0, our transaction will get rollbacked and we will not have any record in the database.

This is an example that explains what is the benefit of @Transactional annotation. Although @Transactional annotation does a lot of other stuff which we will cover later in separate tutorials.

Understanding Spring @Transactional Attributes.

So far we have seen effect of using @Transactional annotation. Let’s see what properties have been defined inside @Transactional annotation. We have different properties/attributes with @Transactional annotation so that we can have more control over our transaction. When we say more control over the transaction, what does it mean?

The @Transactional annotation has a different attribute and corresponding value. We can use those attributes and customized our transactions. For example, if we use @Transactional(readOnly=true) with the method, we will not able to update records. We use @Transactinal(readOnly = true) if our method performs an only search or retrieve operation. Similarly, we have @Transactinal(timeout = 100), which means if any transaction doesn’t complete in 100 seconds then it will throw time out error(see an example here).

Let’s see the possible attributes of @Transactional annotation.

@Transactional(isolation = Isolation.DEFAULT,
propagation=Propagation.REQUIRES_NEW,
readOnly=true,
noRollbackFor =ArithmeticException.class,
timeout = 30000,
value="txManager2",
rollbackFor = { Exception.class },
rollbackForClassName = {"Exception"},
noRollbackForClassName={"Exception"})

So here we have a sample code which elaborates what are the possible attributes for @Transactional. Another point here isolation and propagation attribute has different values. Now it seems a little bit tricky. Yes until unless we are not sure about attributes names and possible values(and what it does exactly, it is not easy to implement in real time development).

We will cover more points about Spring transaction management example using spring boot.

Let’s see all attributes/properties one by one.

Propagation – propagation can have different possible value as below.

Propagation.REQUIRED – Support a current transaction, create a new one if none exists.
Propagation.REQUIRES_NEW – Always create a new transaction and suspend the current transaction if already exist.
Propagation.MANDATORY – Support a current transaction, throw an exception if none exists.
Propagation.NESTED – Execute within a nested transaction if a current transaction exists.
Propagation.NEVER – Execute non-transactionally, throw an exception if a transaction exists.
Propagation.NOT_SUPPORTED – Execute non-transactionally, suspend the current transaction if one exists.
Propagation.SUPPORTS – Support a current transaction, execute non-transactionally if none exists.

Propagation.REQUIRED and Propagation.REQUIRES_NEW is frequently used in real-time development. See an example here.

Note – Default Propagation value is Propagation.REQUIRED

Isolation – isolation can have different possible value as below.

Isolation.READ_UNCOMMITTED – It allows dirty reads, non-repeatable reads, and phantom reads.
Isolation.READ_COMMITTED – Dirty reads are prevented, allows non-repeatable and phantom reads.
Isolation.REPEATABLE_READ – Dirty reads and non-repeatable prevented, phantom reads allowed.
Isolation.SERIALIZABLE – Dirty reads, non-repeatable reads, and phantom reads are prevented.

Note – Default isolation value is Isolation.DEFAULT.

rollbackFor – We can define zero, one or multiple exceptions for which we want our transaction to be rollbacked. We have a separate post where we can find more details.

@Transactional(rollbackFor = {RuntimeException.class})

noRollbackFor – We can define zero, one or multiple exceptions for which we don’t want our transaction to be rollbacked. We have a separate post where we can find more details.

@Transactional(noRollbackFor = {RuntimeException.class})

rollbackForClassName – We can define zero, one or multiple exceptions as String for which we want our transaction to be rollbacked. We have a separate post where we can find more details.

@Transactional(rollbackForClassName = {“NullPointerException”})

noRollbackForClassName – We can define zero, one or multiple exceptions as String for which we don’t want our transaction to be rollbacked. We have a separate post where we can find more details.

@Transactional(noRollbackForClassName = {“NullPointerException”})

readOnly – Its value can be true or false. Go through this post for more details.

@Transactional(readOnly = false)

Spring Transaction Management example using spring boot and Oracle.

Prerequisites.

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

We will use the spring boot library (will provide dependency in pom.xml) to make sure all necessary jar/library is easily available for our application. Spring boot will take care of all jar. Let’ s start.

Create maven project, Don’t forget to check ‘Create a simple project (skip)’click on next. Fill all details(GroupId – restcontrollerexample, ArtifactId – restcontrollerexample and name – restcontrollerexample) 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>
        <!-- <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency> -->
        
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.0</version>
        </dependency>
   </dependencies>                  
   <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <start-class>com.springtransaction.springtransaction.SpringMainExample</start-class>
        <java.version>1.8</java.version>
    </properties>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <mainClass>com.springtransaction.springtransaction.SpringMainExample</mainClass>
                </configuration>
            </plugin>
			<plugin>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.1</version>
            <configuration>
                <fork>true</fork>
                <executable>C:\Program Files\Java\jdk1.8.0_131\bin\javac.exe</executable>
            </configuration>
        </plugin>
        </plugins>
    </build>   
</project>

Note – In pom.xml we have defined javac.exe path in configuration tag. You need to change accordingly i.e where you have installed JDK.


Let maven download all necessary jar. Once it is done we will able to see the maven dependency folder which contains different jar files.

We can start writing our controller classes, ServiceImpl and Repository. The directory structure of the application.

Spring transaction management example using spring boot

Define main class SpringMainExample.java

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);				
	}	
	@Bean
	public SessionFactory sessionFactory(final HibernateEntityManagerFactory hemf) {
		return hemf.getSessionFactory();
	}						
}

Define entity class i.e Book.java

Book.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 = "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;
	 
	 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;
	}

	@Column(name="price")
	 private int 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;
	}		 	 
}

Define repository interface extending CrudRepository.

BookRepository.java

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> {
	public Book findByBookId(int bookId);
			
}


Define service interface i.e BookService.java

BookService.java

package com.springtransaction.service;

import org.springframework.stereotype.Component;
import com.springtransaction.entity.Book;

@Component
public interface BookService {
	public Book findByBookId(int bookId);
	public Book saveBook(Book book);
	
	
}

Define service implementation 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("bookServiceImpl")
public class BookServiceImpl implements BookService{
	
@Autowired
private BookRepository bookRepository;

public Book findByBookId(int bookId) {
	Book book = bookRepository.findByBookId(bookId);
	return book;
}
@Transactional
public Book saveBook(Book book) {
	Book book1 = bookRepository.save(book);
	return book1;
    }
}

Define the controller class or endpoint.

BookController.java

package com.springtransaction.controller;

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 = "/getBook",method = RequestMethod.GET)
    @ResponseBody
    public Book getBookDetails(int bookId) {
		Book bookResponse = bookService.findByBookId(bookId);		
		return bookResponse;
	}
	
    @RequestMapping(value = "/savebook",method = RequestMethod.POST)
    @ResponseBody
    public Book saveBook(@RequestBody Book book) {
		Book bookResponse = bookService.saveBook(book);		
		return bookResponse;
	}		
}

Define JpaConfig class.

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

Step 13 – Define application.properties file

application.properties

# Connection url for the database
spring.datasource.url=jdbc:oracle:thin:@localhost:1521:XE
spring.datasource.username=SYSTEM
spring.datasource.password=oracle1
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
 
spring.datasource.testWhileIdle = true
spring.datasource.validationQuery = SELECT 1
 
# Show or not log for each sql query
spring.jpa.show-sql = true
spring.jpa.hibernate.ddl-auto =update
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.Oracle10gDialect
server.port = 9091
useBuiltinConverters: true
 mapNulls: true
logging.level.org.springframework.transaction.interceptor=TRACE

First, we will save one book record using postman.

Spring transaction management example using spring boot

Now we have one record in DB. Let’s perform get operation

Although we have already discussed, just for reminding In the above example in BookServiceImpl.java, we are using @Transactional annotation with saveBook() method. If we don’t use @Transactional annotation it will work fine we will have a record in DB. But if we modify the BookServiceImpl.java something like below, then we would not have any record in the database. Our transaction will get rollbacked because of ArithmeticException

@Transactional
public Book saveBook(Book book) {
	Book book1 = bookRepository.save(book);
	int a = 10/0;
	System.out.println(a);
	return book1;
}

Note – It can be ArithmeticException, NullPointerException or any RuntimeException.

Tha’s all about Spring transaction management example using spring boot.

You may like.

Spring Data JPA tutorials.

See Docs here.

Summary – We have seen Spring Transaction managment example using Spring Boot. We covered @Transactinal attributes as well as.

We have all points about Spring transaction management example using spring boot.