Prerequisites
Before we start, ensure you have the following:
- Java Development Kit (JDK) installed
- A Spring Boot project set up (you can create one using Spring Initializr)
- Basic knowledge of Spring Boot, JPA, and Hibernate
Step 1: Setting Up the Project
First, create a Spring Boot project using Spring Initializr with the following dependencies:
- Spring Web
- Spring Data JPA
- H2 Database (for simplicity, but you can use any database of your choice)
Project Structure
Your project structure should look like this:
src/main/java
└── com
└── example
└── cascade
├── CascadeDemoApplication.java
├── model
│ ├── Author.java
│ └── Book.java
├── repository
│ ├── AuthorRepository.java
│ └── BookRepository.java
└── service
├── AuthorService.java
└── BookService.java
Step 2: Creating Entity Classes
Let's create two entities: Author
and Book
. An author can have multiple books, so we need a one-to-many relationship.
Author Entity
package com.example.cascade.model;
import jakarta.persistence.*;
import java.util.HashSet;
import java.util.Set;
@Entity
public class Author {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy = "author", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Book> books = new HashSet<>();
// Getters and Setters
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<Book> getBooks() {
return books;
}
public void setBooks(Set<Book> books) {
this.books = books;
}
public void addBook(Book book) {
books.add(book);
book.setAuthor(this);
}
public void removeBook(Book book) {
books.remove(book);
book.setAuthor(null);
}
}
Book Entity
package com.example.cascade.model;
import jakarta.persistence.*;
@Entity
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "author_id")
private Author author;
// Getters and Setters
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Author getAuthor() {
return author;
}
public void setAuthor(Author author) {
this.author = author;
}
}
Step 3: Creating Repository Interfaces
Next, create repository interfaces for the Author
and Book
entities.
AuthorRepository
package com.example.cascade.repository;
import com.example.cascade.model.Author;
import org.springframework.data.jpa.repository.JpaRepository;
public interface AuthorRepository extends JpaRepository<Author, Long> {
}
BookRepository
package com.example.cascade.repository;
import com.example.cascade.model.Book;
import org.springframework.data.jpa.repository.JpaRepository;
public interface BookRepository extends JpaRepository<Book, Long> {
}
Step 4: Creating Service Classes
Create service classes to handle business logic.
AuthorService
package com.example.cascade.service;
import com.example.cascade.model.Author;
import com.example.cascade.repository.AuthorRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class AuthorService {
@Autowired
private AuthorRepository authorRepository;
public List<Author> getAllAuthors() {
return authorRepository.findAll();
}
public Author getAuthorById(Long id) {
return authorRepository.findById(id).orElse(null);
}
public Author saveAuthor(Author author) {
return authorRepository.save(author);
}
public void deleteAuthor(Long id) {
authorRepository.deleteById(id);
}
}
BookService
package com.example.cascade.service;
import com.example.cascade.model.Book;
import com.example.cascade.repository.BookRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class BookService {
@Autowired
private BookRepository bookRepository;
public List<Book> getAllBooks() {
return bookRepository.findAll();
}
public Book getBookById(Long id) {
return bookRepository.findById(id).orElse(null);
}
public Book saveBook(Book book) {
return bookRepository.save(book);
}
public void deleteBook(Long id) {
bookRepository.deleteById(id);
}
}
Step 5: Understanding Cascade Types
JPA defines several cascade types:
CascadeType.ALL
: Propagates all operations (including remove) from the parent to the child.CascadeType.PERSIST
: Propagates the persist operation from the parent to the child.CascadeType.MERGE
: Propagates the merge operation from the parent to the child.CascadeType.REMOVE
: Propagates the remove operation from the parent to the child.CascadeType.REFRESH
: Propagates the refresh operation from the parent to the child.CascadeType.DETACH
: Propagates the detach operation from the parent to the child.
In the Author
entity, we used CascadeType.ALL
and orphanRemoval = true
:
@OneToMany(mappedBy = "author", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Book> books = new HashSet<>();
CascadeType.ALL
This cascade type ensures that all operations performed on the Author
entity (e.g., persist, merge, remove) are also applied to the associated Book
entities. This means if you save or delete an Author
, all associated Books
will be saved or deleted as well.
Orphan Removal
The orphanRemoval
attribute set to true
ensures that when a Book
is removed from the books
set in the Author
entity, it is also removed from the database. This is useful to keep the database in sync with the state of your Java objects.
Step 6: Creating the Application Class
Create the main application class to run your Spring Boot application.
package com.example.cascade;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class CascadeDemoApplication {
public static void main(String[] args) {
SpringApplication.run(CascadeDemoApplication.class, args);
}
}
Step 7: Testing the Application
Now that everything is set up, you can run your Spring Boot application and test the cascade types. You can use tools like Postman or cURL to interact with the RESTful API endpoints.
Example Operations
-
Create a new author with books:
POST /authors Content-Type: application/json { "name": "Jane Austen", "books": [ { "title": "Pride and Prejudice" }, { "title": "Sense and Sensibility" } ] }
-
Get all authors:
GET /authors
-
Get an author by ID:
GET /authors/{id}
-
Delete an author by ID:
DELETE /authors/{id}
Verifying Cascade Types
- When you create an
Author
with books, the books should be persisted automatically due to theCascadeType.PERSIST
. - When you delete an
Author
, the associated books should also be deleted due to theCascadeType.REMOVE
. - Removing a book from the
books
set of anAuthor
and then saving theAuthor
should also remove the book from the database due toorphanRemoval = true
.
Conclusion
In this tutorial, we have explored the different cascade types provided by JPA and Hibernate, and how to use them in a Spring Boot application. Understanding cascade types is crucial for managing relationships in a JPA/Hibernate-based application. By using the appropriate cascade types, you can ensure that operations on parent entities are correctly propagated to related child entities, maintaining the integrity of your data.
Comments
Post a Comment