2022. 5. 25. 15:08ㆍJava/JPA
영속성 전이에 대해 많은 오해들이 있습니다. 연관관계 매핑이나 즉시, 지연 로딩과 어떠한 관계가 있을 것이라고 생각하는데요.
이번 시간에 영속성 전이에 대해 알아보면서 왜 쓰이고 언제 쓰이는지 그리고 주의점은 어떤 게 있는지 정리해보겠습니다.
영속성 전이
언제 쓰이는가?
특정 Entity를 영속 상태로 만들 때, 연관된 Entity도 함께 영속 상태로 만들고 싶을 때 사용합니다.
예) 부모 Entity 저장 시 연관된 자식 Entity도 함께 저장할 때 쓰임
코드로 이해해 보겠습니다.
@Entity
@Getter
@Setter
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);
}
}
@Entity
@Getter
@Setter
public class Child {
@Id
@GeneratedValue
private Long id;
private String name;
@ManyToOne
@JoinColumn(name = "parent_id") // 연관관계 주인
private Parent parent;
}
위 경우 Child를 Parent에 호출할 때, EntityManager에서 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);
이렇게 persist를 3번 하면 귀찮기도 하고
parent중심으로 코드를 짜고 parent가 persist 될 때 자식들도 persist가 동시에 되었으면 좋겠다!
이때 사용하는 게 Cascade입니다.
Parent Class에서 Cascade 설정 시, Child 모두 같이 저장이 가능합니다.
public class Parent {
...
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
private List<Child> childList = new ArrayList<>();
...
}
Child child1 = new Child();
Child child2 = new Child();
Parent parent = new Parent();
parent.addChild(child1);
parent.addChild(child2);
em.persist(parent); // 셋 다 persist됨
CASCADE 종류
ALL: 모두 적용
PERSIST: 영속
REMOVE: 삭제
MERGE: 병합
REFRESH: REFRESH
DETACH: DETACH
All, Persist를 주로 사용합니다.
- All은 라이프사이클을 다 맞출 때
- Persist는 저장할 때만 맞추고 나머지는 따로 쓸 때
영속성 전이 오해
- 영속성 전이는 연관관계를 매핑하는 것과 아무 관련이 없음
- 엔티티를 영속화할 때 연관된 엔티티도 함께 영속화하는 편리함을 제공할 뿐입니다.
그럼 영속성 전이는 언제 사용하는가?
하나의 부모가 자식들을 관리할 때 의미 있습니다. == 소유자 하나 일 때
만약, 다른 엔티티와 자식이 관계가 있을 땐 쓰면 안 되고 이땐 따로 관리하는 게 맞습니다.
조건 두 가지 모두 만족할 때 사용합니다.
1. 부모와 자식의 라이프 사이클 거의 유사할 때
2. 단일 소유자 일 때
고아 객체
부모 Entity와 연관관계가 끊어진 자식 Entity를 자동으로 삭제합니다.
orphanRemoval=true 시 해당 고아 객체를 제거
- 연관 관계를 제거하거나, 부모 객체가 제거되는 경우 자식 객체가 제거됩니다.
@Entity
@Getter
@Setter
public class Parent {
@Id
@GeneratedValue
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);
}
}
Parent findParent = em.find(Parent.class, parent.getId());
findParent.getChildList().remove(0); // 0번 Child의 Parent 연관 관계 제거, 자식 엔티티를 컬렉션에서 제거
//이렇게 하면 delete 쿼리 나가서 지워짐
고아 객체 주의사항
참조하는 곳이 하나일 때 사용해야 하며, 특정 Entity가 자식 Entity를 개인 소유할 때만 사용
- @OneToOne, @OneToMany만 가능
[참고]
고아 객체 제거 기능을 활성화하면, 부모를 제거할 때 자식도 함께 제거된다.
이것은 CascadeType.REMOVE처럼 동작합니다.
- 개념적으로 부모를 제거하면 자식은 고아가 됩니다.
영속성 전이 + 고아 객체
CascadeType.ALL + orphanRemoval=true 의미
JPA를 통해 스스로 생명주기를 관리하는 Entity는 em.persist()로 영속화, em.remove()로 제거하는데요,
만약, 상기 두 옵션을 모두 활성화하면 부모 Entity를 통해 자식 Entity의 생명주기를 관리할 수 있습니다.
💡 언제 유용하게 사용될까?
도메인 주도 설계(DDD)의 Aggregate Root 개념 구현 시 유용합니다.
정리
- 글로벌 Fetch 전략 설정
- 모든 연관관계를 지연 로딩으로 설정
- @ManyToOne, @OneToOne은 기본이 즉시 로딩이므로 지연 로딩으로 변경
- 영속성 전이 설정
- 단일 부모 Entity에 종속되는 자식 Entity는 Cascade Type을 All로 설정
JPA기본편 스터디 함께한 Jarry, Matt 항상 감사합니다.
참고 :
https://www.inflearn.com/course/ORM-JPA-Basic
'Java > JPA' 카테고리의 다른 글
[API 개발 고급] 지연 로딩과 조회 성능 최적화(V2) 2/4 (0) | 2022.05.25 |
---|---|
[API 개발 고급] 지연 로딩과 조회 성능 최적화(V1) 1/4 (0) | 2022.05.25 |
즉시 로딩과 지연 로딩 (0) | 2022.05.25 |
프록시(Proxy) (0) | 2022.05.25 |
[API 개발 고급] 조회용 샘플 데이터 입력 (0) | 2022.05.24 |