JPA CascadeType MERGE example using Spring Boot

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.MERGEwith @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.

JPA CascadeType MERGE Example Using Spring Boot

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

JPA CascadeType MERGE Example Using Spring Boot

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

Hibernate JPA CascadeType MERGE Example Using Spring Boot

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.