내가 공부한것을 올리며, 중요한 단원은 저 자신도 곱씹어 볼겸 상세히 기록하고 얕은부분들은 가겹게 포스팅 하겠습니다.
1. 영속성 전이
특정 엔티티를 영속 상태로 만들 때 연관된 엔티티도 함께 영속상태로 만들고 싶다면?
예를 들어 부모 엔티티를 저장하면 자식 엔티티도 함께 저장되도록 말이다!
우선 영속성 전이가 안되는 기본적인 엔티티의 저장방법부터 알아보자.
- parent, child
// parent
@Entity
public class Parent {
@Id @GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy = "parent")
private List<Child> childList = new ArrayList<>();
}
// child
@Entity
public class Child {
@Id @GeneratedValue
private Long id;
private String name;
@ManyToOne
@JoinColumn(name = "parent_id")
private Parent parent;
}
Parent 객체 1개와, child 객체 2개를 만들어 parent에 2개의 child를 등록하자.
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번이나 해야한다.
위와같이 persist를 3번이나 호출하면서 등록하는 코드는 매우 불편하다.
이를 영속성 전이를 통해 해결해 보자. => parent를 영속화 하면 나머지 2명의 자식도 함께 영속화 되도록
/*영속성 전이가 되는 엔티티 저장 방빕*/
@Entity
public class Parent{
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL) //영속성 전이 속성(CASCADE)사용
private List<Child> childList = new ArrayList<>();
public void addChild(Child child){
childList.add(child);
child.setParent(this);
}
...
}
이제 다시 코드를 실행시켜 보자.
Child child1 = new Child();
Child child2 = new Child();
Parent parent = new Parent();
parent.addChild(child1);
parent.addChild(child2);
em.persist(parent); // 부모만 영속화
실행 결과는 다음과 같다.
부모만 영속화 하였음에도 불구하고, 나머지 두 자식까지 영속화 되었다.
● CASCADE 주의할점!
- 영속성 전이는 연관관계 매핑과는 아무 관련이 없다, 단지 엔티티를 영속화 할때 연관된 엔티티도 함께 영속화하는 편리함을 제공할 뿐이다.
- Parent와 child의 생명주기가 거의 같을때 사용한다.
- 단일 소유자(Parent라는 엔티티만이 Child를 소유한 유일한 소유자 일때)일때 사용하는것이 좋다.
만약 해당 엔티티(Child)가 특정 엔티티(Parent)에 종속되지 않고 여러군데서 사용된다면 사용하지 않는게 좋다.
● CASCADE의 종류
ALL: 모두 적용(모든 곳에서 맞춰야 하면 해당 옵션)
PERSIST: 영속(저장할 때만 lifr cycle을 맞춘다)
REMOVE: 삭제
MERGE: 병합
REFRESH: REFRESH
DETACH: DETACH
2. 고아 객체
● 고아 객체 제거 : 부모 엔티티와 연관관계가 끊어진 자식 엔티티를 자동으로 삭제한다.
@Entity
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);
}
...
}
위 코드를 보면 orphanRemoval = true 가 추가되었다.
이제 코드를 만들어 실행해 보자.
Child child1 = new Child();
Child child2 = new Child();
Parent parent = new Parent();
parent.addChild(child1);
parent.addChild(child2);
em.persist(parent); // parent만 persist해도 child도 같이 persist된다
em.flush();
em.clear();
Parent findParent = em.find(Parent.class, parent.getId());
findParent.getChildList().remove(0); // 고아객체
transaction.commit();
위 코드는 em.find() 로 찾아온 findParent에서 ChildList를 얻은 후, 0번 객체를 삭제하는 내용이다.
0번 자식을 Parent의 컬렉션에서 제거했기 때문에(부모와의 연관관계가 끊어짐) 0번 자식은 자동 삭제된다.
실행된 결과는 다음과 같다.
delete문이 나가는것을 확인할수가 있다.
우리는 그저 Parent의 List에서 child를 지웠을 뿐인데, 아예 DELETE 쿼리까지 전송되어 DB에서 child를 지워버렸다.
● 고아 객체 - 주의
- 참조가 제거된 엔티티는 다른 곳에서 참조하지 않는 고아 객체로 인식하고 삭제하는 기능이다.
- 참조하는 곳이 하나일 때 사용해야 한다!
- 특정 엔티티가 개인 소유할 때 사용한다.
- @OneToOne, @OneToMany만 가능
참고: 개념적으로 부모를 제거하면 자식은 고아가 된다. 따라서 고아 객체 제거 기능을 활성화 하면, 부모를 제거할 때 자식도 함께 제거된다. 마치 CascadeType.REMOVE처럼 동작한다.
=> Parent객체를 지우게 되면 Parent가 소유하고있는 ChildList에 속한엔티티들이 전부 같이 삭제된다.
● 영속성 전이 + 고아 객체, 생명주기 (CascadeType.ALL + orphanRemoval = true)
스스로 생명주기를 관리하는 엔티티는 em.persist()로 영속화, em.remove()로 제거한다.
두 옵션을 모두 활성화 하면 부모 엔티티를 통해서 자식의 생명주기 관리가 가능하다.
Cascade로 인해 부모를 영속화 하면 자식들도 전부 영속화 되고, 부모를 삭제하면 해당되는 자식들도 전부 삭제 된다.
orphanRemoval로 인해 부모를 통해 일부 자식을 부모로부터 삭제하면 해당되는 자식이 삭제 된다.
도메인 주도 설계(DDD)의 Aggregate Root개념을 구현할 때 유용하다.
'BackEnd > JPA' 카테고리의 다른 글
[JPA] 값 타입 - 2 (0) | 2022.04.07 |
---|---|
[JPA] 값 타입 - 1 (0) | 2022.04.07 |
[JPA] 프록시와 연관관계 관리 (0) | 2022.04.06 |
[JPA] 고급 매핑 (0) | 2022.04.05 |
[JPA] 다양한 연관관계 매핑 (0) | 2022.04.04 |
댓글