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.
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.
Let’s check the DB.
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 transaction management example using spring boot.
- @Transactional REQUIRED vs REQUIRES_NEW example in spring boot.
- @Transactional rollbackFor example using spring boot.
- @Transactional noRollbackForClassName example using spring boot
Spring Data JPA Examples.
- 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()
Spring Transaction Management Docs.