In this tutorial, we will see JPA CascadeType MERGE example usin Spring Boot and Oracle. We will see how to use JPA CascadeType MERGE and what will happen if we don’t use CascadeType MERGE?
The JPA CascadeType MERGE is used for association mapping, for example with @OneToOne, OneToMany, @ManyToOne, and @ManyToMany annotations. In this tutorial, we are going to see the JPA CascadeType Merge example with @OneToOne annotation. The same way we can use with other association mappings.
Consider we have two entities Book.java and Story in one to one relationship.
@Entity public class Book { @Id @GeneratedValue(strategy = GenerationType.AUTO) private int bookId; @Column(name = "book_name") private String bookName; @OneToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE }) @JoinColumn(name = "storyid") private Story story; }
Story.java
@Entity public class Story { @Id @GeneratedValue(strategy = GenerationType.AUTO) private int storyId; @Column(name = "story_name") private String storyName; }
In the Book.java entity we are using @OneToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE })
with story field. Here we are telling to hibernate while updating the Book entity and also update the Story entity as well if needed. If we don’t use cascade = CascadeType.MERGE
with @OneToOne annotation we will not able to update the Story entity until we update Story entity explicitly. It seems little confusing. Let’s see an example.
With @OneToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE }).
POST – http://localhost:9091/book/save
Request Data.
{ "bookName":"rich dad poor dad", "story":{ "storyName":"motivational story" } }
Response data.
{ "bookId": 1, "bookName": "rich dad poor dad", "story": { "storyId": 2, "storyName": "motivational story" } }
Now suppose we want to update the Book entity as well as Stroy entity using the below API. Since we are using @OneToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE })
with story field, we can update Book and Story entity. Obserb the below request data we have changed bookName and storyName.
PUT – http://localhost:9091/book/update
Request data.
{ "bookId": 1, "bookName": "Godan", "story": { "storyId": 2, "storyName": "story_test" } }
Response data.
{ "bookId": 1, "bookName": "Godan", "story": { "storyId": 2, "storyName": "story_test" } }
With @OneToOne(cascade = { CascadeType.PERSIST) Only.
Suppose we don’t use cascade = CascadeType.MERGE
with story field, we will be able to update only the Book entity, not the Story entity.
PUT – http://localhost:9091/book/update
Request data.
{ "bookId": 1, "bookName": "Godan", "story": { "storyId": 2, "storyName": "story_test" } }
Response data.
{ "bookId": 1, "bookName": "Godan", "story": { "storyId": 2, "storyName": "motivational story" } }
Observe the above response bookName has been changed but storyName is still the same.
Let’s see an example of JPA CascadeType MERGE example Using Spring Boot and Oracle.
Open eclipse and create maven project, Don’t forget to check ‘Create a simple project (skip)’click on next. Fill all details(GroupId – cascadetype, ArtifactId – cascadetype and name – cascadetype) and click on finish. Keep packaging as the jar.
Modify the pom.xml with the 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>cascadetype</groupId> <artifactId>cascadetype</artifactId> <version>0.0.1-SNAPSHOT</version> <name>cascadetype</name> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.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>com.oracle</groupId> <artifactId>ojdbc6</artifactId> <version>11.2.0.3</version> </dependency> </dependencies> <build> <finalName>${project.artifactId}</finalName> <plugins> <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.
If you see any error for oracle dependency then follow these steps.
The directory structure of the application looks as below.
Define entity class i.e Book.java and Story.java.
Book.java
package com.onetoonehibernatejpa.entity; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.OneToOne; @Entity public class Book { @Id @GeneratedValue(strategy = GenerationType.AUTO) private int bookId; @Column(name = "book_name") private String bookName; @OneToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE }) @JoinColumn(name = "storyid") private Story story; public Story getStory() { return story; } public void setStory(Story story) { this.story = story; } public int getBookId() { return bookId; } public void setBookId(int bookId) { this.bookId = bookId; } public String getBookName() { return bookName; } public void setBookName(String bookName) { this.bookName = bookName; } }
Story.java
package com.onetoonehibernatejpa.entity; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @Entity public class Story { @Id @GeneratedValue(strategy = GenerationType.AUTO) private int storyId; @Column(name = "story_name") private String storyName; public int getStoryId() { return storyId; } public void setStoryId(int storyId) { this.storyId = storyId; } public String getStoryName() { return storyName; } public void setStoryName(String storyName) { this.storyName = storyName; } }
Define the repository interface extending CrudRepository.
BookRepository.java
package com.onetoonehibernatejpa.repository; import java.io.Serializable; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Repository; import com.onetoonehibernatejpa.entity.Book; @Repository public interface BookRepository extends CrudRepository<Book,Serializable> { public Book findByBookId(int bookId); }
Define service interface i.e BookService.java
package com.onetoonehibernatejpa.service; import org.springframework.stereotype.Component; import com.onetoonehibernatejpa.entity.Book; @Component public interface BookService { public Book saveBook(Book book); public Book findByBookId(int bookId); }
Define service implementation class.
BookServiceImpl.java
package com.onetoonehibernatejpa.impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.onetoonehibernatejpa.entity.Book; import com.onetoonehibernatejpa.repository.BookRepository; import com.onetoonehibernatejpa.service.BookService; @Service("bookServiceImpl") public class BookServiceImpl implements BookService { @Autowired private BookRepository bookRepository; @Transactional public Book saveBook(Book book) { book = bookRepository.save(book); return book; } public Book findByBookId(int bookId) { Book book = bookRepository.findByBookId(bookId); return book; } }
Note – See here more about @Component, @Controller, @Service and @Repository annotations here. Also, we have a separate tutorial for Spring boot transaction management here.
Define the controller class or endpoint.
BookController.java
package com.onetoonehibernatejpa.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.onetoonehibernatejpa.entity.Book; import com.onetoonehibernatejpa.service.BookService; @RestController @RequestMapping(value = "/book") public class BookController { @Autowired private BookService bookService; @RequestMapping(value = "/savebook",method = RequestMethod.POST) @ResponseBody public Book saveBook(@RequestBody Book book) { Book bookResponse = bookService.saveBook(book); return bookResponse; } @RequestMapping(value = "/update", method = RequestMethod.PUT) @ResponseBody public Book updateBook(@RequestBody Book book) { Book bookResponse = (Book) bookService.saveBook(book); return bookResponse; } @RequestMapping(value = "/{bookId}",method = RequestMethod.GET) @ResponseBody public Book getBookDetails(@PathVariable int bookId) { Book bookResponse = bookService.findByBookId(bookId); return bookResponse; } }
Note – See more details about @Controller and RestController here.
Step 11 – Define the JpaConfig.java
package com.onetoonehibernatejpa.config; import org.springframework.context.annotation.Configuration; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; @Configuration @EnableJpaRepositories(basePackages = "com.onetoonehibernatejpa.repository") public class JpaConfig { }
Step 12 – Define the SpringMain.java
package com.onetoonehibernatejpa.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.onetoonehibernatejpa.*") @EntityScan("com.onetoonehibernatejpa.*") public class SpringMain { public static void main(String[] args) { SpringApplication.run(SpringMain.class, args); } }
Note – See more details about @ComponentScan here.
And finally, we have an application.properties file where we have database details.
application.properties
# Connection url for the database spring.datasource.url=jdbc:oracle:thin:@localhost:1521:XE spring.datasource.username=SYSTEM spring.datasource.password=oracle3 spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver # Show or not log for each sql query 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 almost done. Let’s deploy the application running SpringMain class as a java application.
Test the endpoints.
POST – http://localhost:9091/book/savebook
First, we will perform save/operation then we will perform update operation. We will update bookName as well storyName. Same operation we will do one more time after removing CascadeType.MERGE.
Sample request JSON data-
{ "bookName":"rich dad poor dad", "story":{ "storyName":"motivational story" } }
PUT- http://localhost:9091/book/update
Query Generated in the above case.
Hibernate: select book0_.book_id as book_id1_0_1_, book0_.book_name as book_name2_0_1_, book0_.storyid as storyid3_0_1_, story1_.story_id as story_id1_1_0_, story1_.story_name as story_name2_1_0_ from book book0_ left outer join story story1_ on book0_.storyid=story1_.story_id where book0_.book_id=?
Hibernate: update story set story_name=? where story_id=?
Hibernate: update book set book_name=?, storyid=? where book_id=?
[stextbox id=’info’]Did you notice? The bookName and storyName both has been updated. [/stextbox]
Now remove the CascadeType.MERGE from @OneToOne(cascade = { CascadeType.PERSIST}) and once again perform save and update operation.
POST – http://localhost:9091/book/savebook
Sample request JSON data-
{ "bookName":"rich dad poor dad", "story":{ "storyName":"motivational story" } }
PUT- http://localhost:9091/book/update
Request Data.
{ "bookId": 1, "bookName": "Godan", "story": { "storyId": 2, "storyName": "story_test" } }
Response data.
{ "bookId": 1, "bookName": "Godan", "story": { "storyId": 2, "storyName": "motivational story" } }
The query generated in above scenario.
Hibernate: select book0_.book_id as book_id1_0_1_, book0_.book_name as book_name2_0_1_, book0_.storyid as storyid3_0_1_, story1_.story_id as story_id1_1_0_, story1_.story_name as story_name2_1_0_ from book book0_ left outer join story story1_ on book0_.storyid=story1_.story_id where book0_.book_id=?
Hibernate: update book set book_name=?, storyid=? where book_id=?
This time the only bookName has been updated and the storyName has not been updated. Since we are not using CascadeType.MERGE.
If you don’t want to use JPA CascadeType.PERSIST/MERGE with @OneToOne modify Book.java and BookServiceImpl as below and Define new repository StoryRepository class.
If we look into @OneToOne annotation the cascade is an optional element. It means it’s not mandatory to use JPA CascadeType MERGE or any other CascadeType. If we don’t want to use JPA CascadeType MERGE it’s not mandatory but we need to save/update story entity explicitly before updating book entity. For example below code will work fine even if you don’t use CascadeType.PERSIST and CascadeType.MERGE along with @OneToOne.
BookServiceImpl.java
package com.onetoonehibernatejpa.impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.onetoonehibernatejpa.entity.Book; import com.onetoonehibernatejpa.repository.BookRepository; import com.onetoonehibernatejpa.repository.StoryRepository; import com.onetoonehibernatejpa.service.BookService; @Service("bookServiceImpl") public class BookServiceImpl implements BookService { @Autowired private BookRepository bookRepository; @Autowired private StoryRepository storyRepository; @Transactional public Book saveBook(Book book) { storyRepository.save(book.getStory()); book = bookRepository.save(book); return book; } public Book findByBookId(int bookId) { Book book = bookRepository.findByBookId(bookId); return book; } }
Book.java
package com.onetoonehibernatejpa.entity; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.OneToOne; @Entity public class Book { @Id @GeneratedValue(strategy = GenerationType.AUTO) private int bookId; @Column(name = "book_name") private String bookName; @OneToOne @JoinColumn(name = "storyid") private Story story; //getter and setter }
StoryRepository.java
package com.onetoonehibernatejpa.repository; import java.io.Serializable; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Repository; import com.onetoonehibernatejpa.entity.Story; @Repository public interface StoryRepository extends CrudRepository<Story, Serializable> { }
That’s all about JPA CascadeType MERGE Example Using Spring Boot.
Hibernate also provides different cascade types as below. The enum CascadeType is defined in org.hibernate.annotations package.
public enum CascadeType { ALL, PERSIST, MERGE, REMOVE, REFRESH, SAVE_UPDATE, EVICT, DETACH }
Here we will see how to use Hibernate cascade type with JPA @OneToOne.
Modify Book.java as below.
package com.onetoonehibernatejpa.entity; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.OneToOne; import org.hibernate.annotations.Cascade; @Entity public class Book { @Id @GeneratedValue(strategy = GenerationType.AUTO) private int bookId; @Column(name = "book_name") private String bookName; @OneToOne @Cascade(org.hibernate.annotations.CascadeType.PERSIST) @JoinColumn(name = "storyid") private Story story; public Story getStory() { return story; } public void setStory(Story story) { this.story = story; } public int getBookId() { return bookId; } public void setBookId(int bookId) { this.bookId = bookId; } public String getBookName() { return bookName; } public void setBookName(String bookName) { this.bookName = bookName; } }
Rest of code would be the same.
That’s all about JPA CascadeType MERGE Example Using Spring Boot.
Other Spring Data JPA tutorial.
Hibernate JPA CascadeType.MERGE Docs.
Summary – We have seen Hibernate JPA CascadeTypes MERGE example in details.