01. 영속성 전이: CASCADE
사용 목적
특정 엔티티를 영속 상태로 만들 때 연관된 엔티티도 함께 영속 상태로 만들고 싶을 때 사용한다.
예시로 부모 엔티티를 저장할 때 자식 엔티티도 함께 저장하고 싶을 때가 있다.
영속성 전이(CASCADE)를 사용하지 않는다면?
위의 Parent와 Child에 대한 예시로 코드를 작성해보겠다. Child와 Parent는 다대일 양방향 연관관계를 갖는다.
다음은 Child 클래스 코드이다.
@Entity
public class Child {
@Id
@GeneratedValue
private Long id;
private String name;
@ManyToOne
@JoinColumn(name = "parent_id")
private Parent parent;
// 생성자, getter/setter
}
다음은 Parent 클래스 코드이다.
@Entity
public class Parent {
@Id
@GeneratedValue
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
}
다음은 JpaMain 코드이다. Parent 중심으로 개발을 했지만, Parent와 Child를 따로 persist 해야한다.
Child child1 = new Child();
Child child2 = new Child();
Parent parent = new Parent();
parent.addChild(child1);
parent.addChild(child2);
em.persist(parent);
em.persist(child1);
em.persist(child2);
tx.commit();
실행 결과로 쿼리가 세 번 나가는 것을 볼 수 있다.
영속성 전이(CASCADE)를 사용한다면?
Parent를 persist하면 Child도 자동으로 persist 되었으면 좋겠다를 반영하기 위해 CASCADE를 사용해보자.
Parent를 클래스의 childList에 cascade 속성(cascade = CascadeType.ALL)
)을 추가하자.
@Entity
public class Parent {
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
private List<Child> childList = new ArrayList<>();
}
JpaMain에서 Child를 persist 하는 코드를 다음과 같이 제거하자. 코드 상으로는 마치 Parent만 persist 하는 것 처럼 보인다.
Child child1 = new Child();
Child child2 = new Child();
Parent parent = new Parent();
parent.addChild(child1);
parent.addChild(child2);
em.persist(parent);
tx.commit();
실행결과로 Parent만 persist 했는데 child도 persist 된 것을 볼 수 있다.
아래 그램은 @OneToMany(mappedBy="parent",
cascade=CascadeType.PERSIST
) 인 경우를 그림으로 나타낸 것이다.
CASCADE의 종류
ALL : CascadeType.PERSIST
와 CascadeType.REMOVE
의 기능을 모두 수행한다.
PERSIST : 부모를 영속화하면 자식도 같이 영속화 하고 싶을 때 사용한다.
REMOVE : PERSIST로 함께 저장했던 부모와 자식의 엔티티를 모두 제거할 경우에 사용한다.
그 외 MERGE, REFRESH, DETACH 가 있다.
CASCADE의 주의점
영속성 전이는 연관관계를 매핑하는 것과 아무 관련이 없다.
엔티티를 영속화 할 때 연관된 엔티티도 함께 영속화하는 편리함을 제공할 뿐이다.
단일 엔티티에 완전히 종속적일 때, lifeCycle이 같을 때 사용하면 된다.
그렇지 않고 다른 엔티티도 자식 엔티티와 연관관계가 있다면 사용하면 안된다.
02. 고아 객체
고아 객체란?
부모 엔티티와 연관관계가 끊어진 자식 엔티티를 말한다.
고아 객체를 제거하려면?orphanRemoval = true
를 사용하면 된다. 예시로 보자.
Parent 클래스의 @OneToMany
속성에 orphanRemoval = true
를 추가한다.
@Entity
class Parent {
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true)
private List childList = new ArrayList<>();
}
다음은 JpaMain의 try문 코드이다. childList의 0번째 요소를 제거한다.
Child child1 = new Child();
Child child2 = new Child();
Parent parent = new Parent();
parent.addChild(child1);
parent.addChild(child2);
em.persist(parent);
em.flush();
em.clear();
Parent findParent = em.find(Parent.class, parent.getId());
findParent.getChildList().remove(0); // 자식 엔티티를 컬렉션에서 제거한다.
tx.commit();
실행 결과로 delete 쿼리가 나가는 것과 자식의 0번째 요소가 지워진 것을 확인할 수 있다.
고아 객체의 주의점
고아 객체를 제거하는 기능은, 참조가 제거된 엔티티는 다른 곳에서 참조하지 않는 고아 객체로 간주하여 삭제하는 기능이라고 정의할 수 있다.
참조하는 곳이 하나일 때, 특정 엔티티가 개인 소유일 때 사용하면 된다.@OneToOne
과 @OneToMany
만 가능하다.
03. 영속성 전이 + 고아 객체, 생명주기
CasecadeType.ALL
+ orphanRemoval = true
스스로 생명주기를 관리하는 엔티티는 em.persist
로 영속화하고 em.remove()
로 제거한다.
두 옵션을 모두 활성화 하면 부모 엔티티를 통해 자식의 생명 주기를 관리할 수 있다.
Parent는 JPA의 EntityManager를 통해 생명주기가 관리되고, Child는 생명주기를 Parent가 관리한다.
이것은 도메인 주도 설계(DDD)의 Aggregate Root 개념을 구현할 때 유용하다.
'JPA' 카테고리의 다른 글
임베디드 타입 (0) | 2024.01.15 |
---|---|
기본값 타입 (2) | 2024.01.14 |
즉시 로딩과 지연 로딩 (2) | 2024.01.12 |
프록시 (0) | 2024.01.12 |
@MappedSuperclass - 매핑 정보 상속 (0) | 2024.01.11 |