[Spring JPA] Relation Mapping의 Cascade

SeongWon Oh·2021년 10월 9일
0

Spring Framework

목록 보기
20/33
post-thumbnail

Cascade를 사용해야하는 이유

자바 코드의 코딩 스타일대로 하면 아래와 같이 book 엔티티와 publisher엔티티를 만들때 Save하고 연관관계를 설정한 뒤 save하는 등 save 메서드들을 불필요하게 반복해서 사용하지 않고 연관관계까지 모든 설정을 마치고 한번에 Save를 하며 DB에 올릴 것이다.

하지만 그렇게 하면 연관관계를 설정하는 코드에서 DB에 해당 entity들이 들어있지 않아 오류가 발생하게된다. 그래서 relation annotation의 cacade속성을 사용하여 설정해야한다.

    void BookCascadeTest() {
        Book book = new Book();
        book.setName("모든 순간이 너였다");
        //bookRepository.save(book);


        Publisher publisher = new Publisher();
        publisher.setName("패스트 캠퍼스");
        //publisherRepository.save(publisher);
        
        // 연관관계 추가
        book.setPublisher(publisher);
        bookRepository.save(book);

	// 연관관계 추가
        publisher.getBook().add(book);
        publisher.addBook(book);
        publisherRepository.save(publisher);

        System.out.println("books : "+bookRepository.findAll());
        System.out.println("publisher : "+publisherRepository.findAll());
    }

Relation Mapping의 Cascade설정하는 법

@Entity
@NoArgsConstructor
@Data
public class Book extends BaseEntity {
	.....
	
    @ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE})
    @ToString.Exclude
    private Publisher publisher;
    
	.....
}
  • Cascade속성은 @OneToOne, @ManyToOne등의 relation mapping어노테이션의 속성으로 위의 코드와 같이 설정해주면 된다.
  • cascade속성은 단일값, 또는 배열로 설정을 넣을 수 있으며 Default값은 {}와 같이 빈 배열이다.
  • 속성으로는 ALL, PERSIST, MERGE, REMOVE, REFRESH, DETACH가 있다.

CascadeType예제

    void BookCascadeTest() {
        Book book = new Book();
        book.setName("모든 순간이 너였다");
        //bookRepository.save(book);


        Publisher publisher = new Publisher();
        publisher.setName("패스트 캠퍼스");
        //publisherRepository.save(publisher);
        
        // 연관관계 추가
        book.setPublisher(publisher);
        bookRepository.save(book);

	// 연관관계 추가
        publisher.getBook().add(book);
        publisher.addBook(book);
        publisherRepository.save(publisher);

        System.out.println("books : "+bookRepository.findAll());
        System.out.println("publisher : "+publisherRepository.findAll());
    }

글의 시작에 처음 보여줬던 해당 코드의 경우는 연관관계를 mapping하는 과정에서 DB에 해당 entity들이 들어있지 않아 오류가 발생하게된다고 했었다.

이제 이러한 오류는 cascade설정을 통해 쉽게 해결 가능하다.

위의 예제의 경우는 CascadeType.PERSIST속성을 넣어주면 바로 해결할 수 있다. 해당 속성은 해당 엔티티가 persist될 때 연관관계에 있는 엔티티도 자동으로 persist시키라는 명령어로 해당 속성을 넣어주면 위의 코드로 DB에 엔티티가 들어있지 않다는 오류를 없앨 수 있을 것이다.

        
        Book book1 = bookRepository.findById(1L).get();
        book1.getPublisher().setName("성원 출판사");

        bookRepository.save(book1);

        System.out.println("books : "+bookRepository.findAll());
        System.out.println("publisher : "+publisherRepository.findAll());

연관관계에 있는 객체의 값 업데이트를 연속적으로 하기 위해서는 reloation annotation에 CascadeType.MERGE를 추가해줘야 한다.

        
        Book book2 = bookRepository.findById(2L).get();
        bookRepository.delete(book2);
//        bookRepository.deleteById(1L);
  • 하나의 entity를 삭제했을 때 연결되어있는 entity들도 Delete를 하기 위해서는 reloation annotation에 CascadeType.REMOVE가 필요하다.
  • 책을 삭제했을 때 출판사 데이터도 삭제를 하고 싶다면 Book.java에서 publisher의 reloation annotation에 CascadeType.REMOVE를 추가해주면 된다.
    -> 부모 엔티티가 자식 엔티티의 생명주기를 관리하며 부모 엔티티가 삭제되면 연관관계에 있는 자식 엔티티들은 모두 삭제된다❗❗

OrphanRemoval

  • reloation annotation의 고아제거속성(orphanRemoval)은 연관관계가 없는 entity를 제거하는 속성이다.
  • 부모 엔티티가 자식 엔티티의 생명주기를 관리하며 부모 엔티티가 삭제되면 연관관계에 있는 자식 엔티티들은 모두 삭제된다는 점에서 CascadeType.REMOVE와 같다고 생각할 수 있으나 다른 점이 존재한다.
  • cascade의 CascadeType.REMOVE은 부모 엔티티를 통해 자식 엔티티를 삭제하면 둘 사이의 연관관계가 끊어지고 자식 엔티티는 그대로 남아있다.
  • 하지만 orphanRemoval=true는 부모 엔티티를 통해 자식 엔티티를 삭제하였을때 자식 엔티티가 아무런 연관관계가 없는 고아가 된다면 연관관계뿐만 아니라 자식 엔티티 자체도 제거된다. (단, 자식 엔티티가 다른 연관관계가 존재하면 삭제되지 않는다.)
  • 연관관계를 끊었을 때 연결되어 있었던 객체들을 삭제하려면 orphanRemoval속성을 true로 설정해주면 된다.
  • 연관관계를 끊었을 때 연결되어 있었던 객체들을 유지하려면 orphanRemoval속석을 false(default값)로 설정해주면 된다.
    @OneToMany(orphanRemoval = true)
    @JoinColumn(name = "publisher_id")
    @ToString.Exclude
    private List<Book> book = new ArrayList<>();

🚨 CascadeType.REMOVE VS orphanRemoval

  • 부모 엔티티 삭제
    둘 다 부모 엔티티를 삭제하면 자식 엔티티도 삭제한다.
  • 부모 엔티티에서 자식 엔티티 제거
    CascadeType.REMOVE은 자식 엔티티가 그대로 존재하지만, orphanRemoval=true는 자식 엔티티를 제거한다.

Reference

profile
블로그 이전했습니다. -> https://seongwon.dev/

0개의 댓글