@Transactional readonly true example in spring boot

In this post, we will see @Transactional(readOnly = true) example using spring boot.

Points related to @Transactional(readOnly = true/false).

If we don’t provide any value for readOnly in @Transactional, then the default value will be false.

What we write –

	@Transactional
	public Student getStudent(int id) {
		Optional<Student> studentResponse = studentRepository.findById(id);
		Student student = studentResponse.get();
		return student;
	}

What Spring understands –

	@Transactional(readOnly = false)
	public Student getStudent(int id) {
		Optional<Student> studentResponse = studentRepository.findById(id);
		Student student = studentResponse.get();
		return student;
	}

Generally, we use @Transactional(readOnly = true) for search or retrieval operation to make sure we can only perform the read-only operation.

	@Transactional(readOnly = true)
	public List<Student> getAllStudents() {
		List<Student> studentResponse = (List<Student>) studentRepository.findAll();
		return studentResponse;
	}

We can override readOnly behavior using @Modifying  annotation. For example, suppose @Transactional annotation has been used with class level or interface level as below and we want to override readOnly behavior for one method(we don’t want to apply readOnly true for deleteOldBooks() method).


@Repository
@Transactional(readOnly = true)
public interface BookRepository extends CrudRepository<Book,Serializable> {

  List<Book> findByBookName(String bookName);

  @Modifying
  @Transactional
  @Query("delete from Book b where b.old= true")
  void deleteOldBooks();
		
}

In the above example, we will able to delete record since we are overriding @Transactional(readOnly = true) nature using @Modifying annotation.

  • If we use @Transactional(readOnly = true) to a method which is performing create or update operation then we will not have newly created or updated record into the database but we will have the response data.

Below code will not give any exception but using @Transactional(readOnly = true) while create or update operation doesn’t make any sense. Right!

	@Transactional(readOnly = true)
	public Student updateStudent(Student entity) {
		Student studentResponse = studentRepository.save(entity);
		return studentResponse;
	}

An example for @Transactional(readOnly = false) and @Transactional(readOnly = true).

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 readonly true example in 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(readOnly = false)
	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);
			
	}
	
}

And finally, we have application.properties

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
 
spring.jpa.show-sql = true
spring.jpa.hibernate.ddl-auto =create
 
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.Oracle10gDialect
 
server.port = 9091
 

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

Testing of example using postman.

Test from the postman with below request data.


@Transactional readonly true example in spring boot

Let’s check the DB.

@Transactional readonly true example in spring boot

Yes, we have one record in the database since we have mentioned in BookServiceImpl.java @Transactional(readOnly = false), we can perform read/write operation.

Note – Even we write @Transactional only with saveBook() method, it will behave the same because by default we have readOnly = false.

So what will happen if we do something like @Transactional(readOnly = true)? Let’s modify BookServiceImpl.java as 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(readOnly = true)
	public void saveBook(Book book){

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

}

Let’s rerun the application and test from the postman. We have the same response as earlier but Does any record persist in DB? No there will not any record in DB. Since we are using @Transactional(readOnly = true), means we can perform the read-only operation, we can’t save any record in DB.

Let’s see the DB.

Note – we are using spring.jpa.hibernate.ddl-auto =create. Every time whenever we will rerun the application, we will have a new table.

That’s all about @Transactional readonly true example in spring boot.

Other examples related to Spring Transaction management.

Spring Data JPA Examples.

Spring Transaction Management Docs.