👩🏻‍💻 Programming/SpringBoot

Cascade와 고아제거속성

한국의 메타몽 2021. 9. 26. 19:02

 

Cascade란?

 

영속성의 전이를 제공해준다.

(*영속 : 영원히 계속함)

 

 

먼저 Cascade에는 크게 6가지의 종류가 있다.

 

  • ALL : 모두 적용
  • PERSIST : 영속 (저장)
  • MERGE : 병합
  • REMOVE : 삭제
  • REFRESH : Refresh
  • DETACH : DETACH

각 옵션에 따라 행동이 이루어지는데, 예를들어 PERSIST로 설정할 경우, 자식 엔티티까지 영속화해서 저장한다.

또다른 예로 REMOVE로 설정할 경우, 부모 엔티티만 삭제해도 자식 엔티티까지 함께 삭제된다.

REFRESH의 경우 연결된 하위 엔티티까지 인스턴스의 값을 새로 고침하며, DETACH는 연결된 하위 엔티티까지 영속성을 제거한다.

 

 

예시

 

[BookRepositoryTest.java]

    @Transactional
    @Test
    void bookCascadeTest(){
        Book book = new Book();
        book.setName("JPA 초격차 패키지");
        bookRepository.save(book);

        Publisher publisher = new Publisher();
        publisher.setName("패스트캠퍼스");

        //publisherRepository.save(publisher);

        book.setPublisher(publisher);
        bookRepository.save(book);

        //  publisher.addBook(book);
        //publisherRepository.save(publisher);

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

 

위 코드에서 book은 저장이 이루어지지만 publisher는 저장이 이루어지지 않는다.

그 상태로 저장된 book과 publisher을 모두 출력하려고 하는데, 결과는 어떻게 나올까?

 

[Book.java]

// 윗부분 생략
    @ManyToOne(cascade = CascadeType.PERSIST)
    @ToString.Exclude
    private Publisher publisher;

 

보다시피 Book은 publisher를 CacadeType.PERSIST로 영속화했다.

부모가 Book이고 자식이 publisher인 상황에서, Book을 저장할경우 publisher도 같이 저장이된다.

 

 

[결과]

books : [Book(super=BaseEntity(createdAt=2021-09-26T18:59:18.982341, updatedAt=2021-09-26T18:59:19.125792), id=1, name=JPA 초격차 패키지, category=null, authorId=null)]
publisher : [Publisher(super=BaseEntity(createdAt=2021-09-26T18:59:19.118118, updatedAt=2021-09-26T18:59:19.118118), id=1, name=패스트캠퍼스, books=[])]

 

보다시피 book과 publisher모두 잘 저장되었다.

 

 

고아 객체 (Orphan) - OrphanRemoval

 

JPA에서는 부모 엔티티와 연관관계가 끊어진 자식 엔티티를 자동으로 삭제하는 기능을 제공하는데, 이를 고아 객체 제거라고 한다.

OrphanRemoval을 이용하면 부모 엔티티의 컬렉션에서 자식 엔티티의 참조만 제거하면 자식 엔티티가 자동으로 삭제된다.

 

@Entity
public class Parent{
	@Id @GeneratedValue
    private Long id;
    
    @OneToMany(mappedBy = "parent", orphanRemoval = true)
    private List<child> children = new ArrayList<child>();
    
    ...
}
Parent parent = em.find(Parent.class, 10L);
parent.getChildren().remove(0); // 자식 엔티티를 컬렉션에서 제거 -> 고아 객체가 되어 삭제됨

 

 

영속성 전이 + 고아 객체

 

일반적으로 엔티티는 EntityManager.persist()를 통해 영속화하고 remove()를 통해 제거되며, 엔티티 스스로 생명주기를 관리한다.

하지만 CacadeType.All + orphanRemoval = true를 동시에 사용하면 부모 엔티티를 통해서 자식의 생명주기를 관리할 수 있다.

 

Parent parent = em.find(Parent.class, 10L);
parent.addChild(child); // 자식을 저장

Parent parent = em.find(Parent.class, 10);
parent.getChildren().remove(child); // 자식을 삭제