프레임워크(Framework)/JPA

[JPA] 영속성 전이(CASCADE)와 고아 객체

잇트루 2022. 12. 7. 01:35
반응형
본 내용은 온라인 강의 사이트 인프런의 김영한 님의 강의 내용이 포함되어 있습니다.
'자바 ORM 표준 JPA 프로그래밍 - 기본편'
 

자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런 | 강의

JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자신있게 JPA를 사용할 수 있습니다., - 강의 소개 | 인프런

www.inflearn.com

 

영속성 전이(CASCADE)

특정 엔티티를 영속 상태로 만들 때, 연관된 엔티티도 함께 영속 상태로 만들고 싶으면 영속성 전이 기능을 사용한다.

 

JPA는 CASCADE 옵션으로 영속성 전이를 제공한다.

  • 영속성 전이를 사용하면 부모 엔티티를 저장할 때 자식 엔티티도 함께 저장할 수 있다.

  • 한 부모는 여러 명의 아이를 가질 수 있다.
  • 여러 명의 자식은 한 부모를 가질 수 있다.

 

Parent

@Entity
public class Parent {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @OneToMany(mappedBy = "parent")
    private List<Child> childList = new ArrayList<>();

    public void addChild(Child child) {
        childList.add(child);
        child.setParent(this);
    }

    // Getter
    // Setter
}

 

Child

@Entity
public class Child {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long Id;

    private String name;

    @ManyToOne
    @JoinColumn(name = "PARENT_ID")
    private Parent parent;

    // Getter
    // Setter
}

 

영속성 전이를 사용하지 않는 경우

Child child1 = new Child();
Child child2 = new Child();

Parent parent = new Parent();
parent.addChild(child1);
parent.addChild(child2);

// 모두 persist 해야함
em.persist(parent);
em.persist(child1);
em.persist(child2);

 

영속성 전이를 사용하는 경우

Parent

@Entity
public class Parent {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
    private List<Child> childList = new ArrayList<>();

    public void addChild(Child child) {
        childList.add(child);
        child.setParent(this);
    }

    // Getter
    // Setter
}
Child child1 = new Child();
Child child2 = new Child();

Parent parent = new Parent();
parent.addChild(child1);
parent.addChild(child2);

em.persist(parent);
  • Parent와 연관된 엔티티도 모두 persist()

 

영속성 전이 주의 사항

  • 영속성 전이는 연관관계를 매핑하는 것과 아무 관련이 없다.
  • 엔티티를 영속화할 때 연관된 엔티티도 함께 영속화하는 편리함을 제공하는 것뿐이다.
  • 소유자가 1개일 때에는 사용할 수 있다
    • 위 예시의 경우 Child의 소유자는 Parent 1개
  • 만약, 소유자가 2개 이상일 경우 영속성 전이를 사용하면 안 된다.
    • Member나 Team 등 Parent 외의 엔티티가 Child를 알고 있는 경우

 

 

CASCADE의 종류

  • ALL : 모두 적용
  • PERSIST : 영속
  • REMOVE : 삭제
  • MERGE : 병합
  • REFRESH : REFRESH
  • DETACH : DETACH

 

 

고아 객체

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

 

고아 객체 제거

  • 부모 엔티티의 컬렉션에서 자식 엔티티의 참조만 제거하면 자식 엔티티가 자동으로 삭제
  • orphanRemoval = true

 

Parent

@Entity
public class Parent {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @OneToMany(mappedBy = "parent", orphanRemoval = true)
    private List<Child> childList = new ArrayList<>();

    public void addChild(Child child) {
        childList.add(child);
        child.setParent(this);
    }

    // Getter
    // Setter
}
Parent parent1 = em.find(Parent.class, id);
parent1.getChildren().remove(0); // 자식 엔티티를 컬렉션에서 제거
// DELETE SQL 쿼리 발생
DELETE FROM CHILD WHERE ID = ?

 

고아 객체 주의 사항

  • 참조가 제거된 엔티티는 다른 곳에서 참조하지 않는 고아 객체로 보고 삭제하는 기능
    • 참조하는 곳이 하나일 때만 사용해야 한다.
  • 특정 엔티티가 개인 소유할 때 사용
  • @OneToOne, @OneToMany만 사용 가능

 

 

참고

개념적으로 부모를 제거하면 자식은 고아가 된다.

  • 고아 객체 제거 기능을 활성화하면, 부모를 제거할 때 자식도 함께 제거된다.
  • cascade = CascadeType.REMOVE와 같은 동작을 한다.
// Parent cascade = CascadeType.ALL or CascadeType.REMOVE
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
private List<Child> childList = new ArrayList<>();

// Parent : orphanRemoval = true
@OneToMany(mappedBy = "parent", orphanRemoval = true)
private List<Child> childList = new ArrayList<>();

Parent findParent = em.find(Parent.class, parent.getId());
em.remove(findParent);
  • Parent cascade = CascadeType.ALL or CascadeType.REMOVE
    • 부모를 지우면 자식들도 모두 함께 제거된다.
  • Parent : orphanRemoval = true
    • 부모를 지우면 자식들도 모두 함께 제거된다.

 

 

영속성 전이 + 고아 객체, 생명주기

  • cascade = CascadeType.ALL + orphanRemoval = true
  • 일반적으로 엔티티는 em.persist()로 영속화, em.remove()로 제거한다.
    • 엔티티 스스로 생명주기를 관리한다는 의미
  • 두 옵션을 모두 활성화하면 부모 엔티티를 통해서 자식의 생명주기를 관리할 수 있다.
    • 자식을 저장하려면 부모에서 등록하기만 해도 된다.(CASCADE)
    • 자식을 삭제하려면 부모에서 삭제하기만 해도 된다.(orphanRemovel)
@Entity
public class Parent {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Child> childList = new ArrayList<>();

    public void addChild(Child child) {
        childList.add(child);
        child.setParent(this);
    }

    // Getter
    // Setter
}

 

자식을 저장하려면 부모에서 등록하기만 해도 된다.(CASCADE)

Child child1 = new Child();
Child child2 = new Child();

Parent parent = new Parent();
parent.addChild(child1);
parent.addChild(child2);

em.persist(parent);

 

자식을 삭제하려면 부모에서 삭제하기만 해도 된다.(orphanRemovel)

Parent findParent = em.find(Parent.class, parent.getId());
findParent.getChildList().remove(0);
em.remove(findParent);

 

참고

영속성 전이 + 고아 객체 옵션을 모두 사용할 경우

  1. 부모 엔티티를 통해서 자식의 생명주기를 관리할 수 있다.
  2. 도메인 주도 설계(DDD)의 애그리거트 루트(Aggregate Root) 개념을 구현할 때 유용하다.
반응형