In this tutorial, we will see a 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 are going to discuss the below points.
- What is a transaction?
- Different ways to define Transaction management in Spring.
- Use of @Transactional annotation.
- @Transactional Attributes.
- Transaction Management example using spring boot.
We will see how to use @Transactional
annotation, and what will happen if we don’t use @Transactional
annotation. We will also cover what are attributes defined for @Transactional
annotation and what it does.
Note – We should not @Transactional
annotation with the private method.
See Hibernate Transaction Management example.
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 the Database after commit.
Three are two ways to define Transaction management.
- Programmatic transaction management.
- Declarative transaction management.
Programmatic transaction management – The Spring Framework provides two ways of programmatic transaction management.
- Using the TransactionTemplate
- Using PlatformTransactionManager implementation directly
Using TransactionTemplate –
@Configuration
@EnableJpaRepositories(basePackages = "com.javatute.repository")
public class JpaConfig {
private final TransactionTemplate transactionTemplate;
public JpaConfig(PlatformTransactionManager transactionManager) {
Assert.notNull(transactionManager, "The 'transactionManager' argument must not be null.");
this.transactionTemplate = new TransactionTemplate(transactionManager);
this.transactionTemplate.setIsolationLevel(TransactionDefinition.PROPAGATION_REQUIRED);
this.transactionTemplate.setTimeout(30);
}
}
Using PlateFormTransactionManager directly –
public void someMethod(PlatformTransactionManager transactionManager){
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setName("SomeTxName");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = transactionManager.getTransaction(def);
try {
// business logic
}
catch (Exception ex) {
transactionManager.rollback(status);
throw ex;
}
transactionManager.commit(status);
}
Note – It is recommended to use TransactionTemplate
for programmatic transaction management. The second approach is similar to using the JTA UserTransaction
API. See more details in docs.
In the case of Programmatic transaction management, we need to write some extra code to manage the transaction. For example, we need to write code to create a transaction, begin the transaction and commit/rollback the transaction.
Let’s see the 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 need to 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>
Use of Spring @Transactional annotation
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
annotation.
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 that would explain the effect of @Transactional
annotation.
Consider you have a 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 if we have an exception in our code, we would have a record in the 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 the very 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 the benefits of using @Transactional
annotation. Let’s see what properties have been defined for the @Transactional
annotation. We have different properties/attributes with @Transactional
annotation so that we can have more control over our transactions. 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 customize our transactions. For example –
- if we use
@Transactional(readOnly=true)
with the method, we will not able to update records in the same transaction.
- 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, it will throw a time-out error(see an example here).
Let’s see the 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 that 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 attribute names and possible values(and what exactly it does, it is not easy to implement in real time development).
Let’s see all attributes/properties one by one.
Propagation – propagation can have different possible values as below.
Propagation.REQUIRED – Support a current transaction, and create a new one if none exists.
Propagation.REQUIRES_NEW – Always create a new transaction and suspend the current transaction if it already exists.
Propagation.MANDATORY – Support a current transaction, and 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.
Note – Propagation.REQUIRED and Propagation.REQUIRES_NEW is frequently used in real-time development. See an example here. Default Propagation value is Propagation.REQUIRED
Isolation – isolation can have different possible values 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 MySql.
Create a maven project using eclipse or Intellij idea and add the dependency.
<?xml version="1.0" encoding="UTF-8"?>
<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>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.1.RELEASE</version>
</parent>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.batch</groupId>
<artifactId>spring-batch-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch</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>
</dependencies>
</project>
Let maven download all necessary jars. Once it is done we will be able to see the maven dependency folder which contains different jar files.
Directory structure
Define entity class i.e Book.java
Book.java
package com.javatute.entity;
import javax.persistence.*;
@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;
}
}
Define repository interface extending CrudRepository.
BookRepository.java
package com.javatute.repository;
import com.javatute.entity.Book;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import java.io.Serializable;
@Repository
public interface BookRepository extends CrudRepository<Book, Serializable> {
public Book findByBookId(int bookId);
}
Define service interface i.e BookService.java
BookService.java
package com.javatute.service;
import com.javatute.entity.Book;
import org.springframework.stereotype.Component;
@Component
public interface BookService {
public Book findByBookId(int bookId);
public Book saveBook(Book book);
}
Define service implementation class.
BookServiceImpl.java
package com.javatute.serviceimpl;
import com.javatute.entity.Book;
import com.javatute.repository.BookRepository;
import com.javatute.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@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.javatute.controller;
import com.javatute.entity.Book;
import com.javatute.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@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.javatute.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@Configuration
@EnableJpaRepositories(basePackages = "com.javatute.repository")
public class JpaConfig {
}
Define application.properties file
application.properties for Oracle DataBase
# 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
application.properties for MySql database
spring.datasource.url=jdbc:mysql://localhost:3306/springbootcrudexample
spring.datasource.username=root
spring.datasource.password=root
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
server.port = 9091
Define SpringMain.java
package com.javatute.main;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@ComponentScan(basePackages = "com.javatute.*")
@EntityScan("com.javatute.*")
public class SpringMain {
public static void main(String[] args) {
SpringApplication.run(SpringMain.class, args);
}
}
Run the main class and test the example.
First, we will save one book record using postman.
Now we have one record in DB. Let’s perform get operation
Although we have already discussed this, just for reminding In the above example in BookServiceImpl.java
, we are using the @Transactional
annotation with the saveBook()
method. If we don’t use @Transactional
annotation it will work fine we will have a record in DB. But if we modify BookServiceImpl.java
something like the 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.
That’s all about Spring transaction management example using spring boot.
Few basic questions related to spring transaction management.
1. Can we use @Transactional
annotation in the controller?
We can. But we should avoid using @Transactional
annotation in the controller, instead we should use it in Service classes.
2. What are the default @Transactional
settings?
The propagation setting is PROPAGATION_REQUIRED. The isolation level is ISOLATION_DEFAULT. The transaction is read/write.
Download the source code from Github.
Other @Transactional
related tutorials.
- @Transactional REQUIRED vs REQUIRES_NEW example in spring boot.
- @Transactional rollbackFor example using spring boot.
- @Transactional noRollbackFor example using spring boot.
- @Transactional readonly true example in spring boot.
- @Transactional noRollbackForClassName example using spring boot.
- @Transactional rollbackForClassName example using spring boot.
- @Transactional timeout Using Spring Boot.
Spring Data JPA tutorials.
- Spring Data JPA greater than Example
- Spring Data JPA less than Example
- Spring Data JPA IsNull Example Using Spring Boot
- Spring Data findById() Vs getOne()
- Spring Data JPA CrudRepository findById()
- Spring Data JPA JpaRepository getOne()
- Spring Data CrudRepository saveAll() and findAll().
- Spring Data CrudRepository existsById()
- Spring Data JPA delete() vs deleteInBatch()
- Spring Data JPA deleteAll() Vs deleteAllInBatch()
- Spring Data JPA JpaRepository deleteAllInBatch()
- Spring Data JPA deleteInBatch() Example
- Spring Data JPA JpaRepository saveAndFlush() Example
- Spring Data JPA CrudRepository count() Example
- Spring Data JPA CrudRepository delete() and deleteAll()
- Spring Data JPA CrudRepository deleteById() Example
- CrudRepository findAllById() Example Using Spring Boot
- Spring Data CrudRepository save() Method.
- Sorting in Spring Data JPA using Spring Boot.
- Spring Data JPA example using spring boot.
- Spring Data JPA and its benefit.
See Docs here.
Summary – We have seen the Spring Transaction management example using Spring Boot. We covered @Transactinal attributes as well.