728x90
영속성 전이
- 특정 Entity를 영속 상태로 만들 때 연관된 Entity도 함께 영속 상태로 만드는 것을 의미한다. 가령 부모 Entity를 저장할 때 자식 Entity도 함께 저장하는 것이다.
- 영속성 전이는 즉시 로딩, 지연 로딩, 연관 관계 설정과 관계없는 개념이다.
@Entity
public class Parent {
@Id
@GeneratedValue
private Long id;
@OneToMany(mappedBy = "parent")
private List<Child> childList = new ArrayList<>();
public void addChild(Child child) {
childList.add(child);
child.setParent(this);
}
}
@Entity
public class Child {
@Id
@GeneratedValue
private Long id;
@ManyToOne
@JoinColumn(name = "parent_id")
private Parent parent;
}
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();
- Parent Entity의 addChild는 양방향 연관 관계를 만들어주기 위해 편의로 작성한 메소드이다.
- 위의 코드를 보면 parent를 저장할 때 자식들이 함께 저장되지 않고 각각 persist()하여 저장해야 한다.
- 만약 자식이 수백 명이라면 이 과정을 수백 번 반복해야 하기 번거롭다. 이를 위해 parent가 저장될 때 child도 함께 저장되도록 만들 수 있다.
@Entity
public class Parent {
@Id
@GeneratedValue
private Long id;
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
private List<Child> childList = new ArrayList<>();
public void addChild(Child child) {
childList.add(child);
child.setParent(this);
}
}
@Entity
public class Child {
@Id
@GeneratedValue
private Long id;
@ManyToOne
@JoinColumn(name = "parent_id")
private Parent parent;
}
Child child1 = new Child();
Child child2 = new Child();
Parent parent = new Parent();
parent.addChild(child1);
parent.addChild(child2);
em.persist(parent);
tx.commit();
- 이전 코드와 다른 점은 Parent의 @OneToMany에 cascade 옵션을 준 것이다. cascade 옵션을 주게 되면 영속화가 자식에게도 적용된다.
- 위의 결과와 같이 em.persist(parent)로 parent만 영속화했지만 쿼리는 총 3번 날아가며 모두 저장한 것을 볼 수 있다.
- 이처럼 부모를 영속화할 때 자식도 함께 영속화시키는 것이 cascade이다.
주의 사항
- 영속성 전이는 연관 관계를 매핑하는 것과는 전혀 관계가 없다. 관계 매핑 어노테이션에 cascade옵션을 준다고 해당 관계가 기존과 달라지는 것이 아니다.
- Entity를 영속화할 때 연관된 Entity도 함께 영속화하는 편리함을 제공할 뿐이다.
- 자식의 부모가 하나일 때, 단일 Entity에 완전히 종속적이고 라이프 사이클이 같을 때 사용한다.
- 게시판, 첨부파일 경로 등에 사용한다. 글쓴이 계정 없어지면 해당 게시글도 사라지는 것 마냥
- 다른 Entity에서도 관리하는 자식이라면 사용하면 안 된다.
- 삭제했다가 다른 Entity도 함께 삭제될 수도 있기 때문이다.
종류
- CascadeType.ALL
- 모두 적용
- 라이프 사이클이 완전히 일치할 때 사용해야 한다.
- CasecadeType.PERSIST
- 영속화
- CascadeType.REMOVE
- 삭제
- CascadeType.MERGE
- 병합
- CascadeType.REFRESH
- CAscadeType.DETACH
ALL, PERSIST, MERGE를 많이 사용한다고 한다.
고아 객체 제거
부모 Entity와 연관관계가 끊어진 자식 Entity를 자동으로 삭제한다.
- orphanRemoval = true
@Entity
public class Parent {
@Id
@GeneratedValue
private Long id;
@OneToMany(mappedBy = "parent", orphanRemoval = true)
private List<Child> childList = new ArrayList<>();
public void addChild(Child child) {
childList.add(child);
child.setParent(this);
}
}
Parent parent = em.find(Parent.class, id);
parent.getChildren().remove(0);
tx.commit();
-
- 고아 객체 옵션인 orphanRemoval = true를 주게 되면 부모가 삭제되었을 때 자식 Entity를 컬렉션에서 삭제한다.
- parent.getChildren().remove(0)으로 자식 Entity를 컬렉션에서 제거하면 연관 관계를 끊는 것인데 이때 해당 Entity를 삭제하는 것이다.
- Entity의 참조가 제거되면 그 객체를 다른 곳에서 참조하지 않는 고아 객체로 보고 삭제하는 것이다. 따라서 참조하는 곳이 하나일 때 사용해야 한다.
- 연관 관계를 끊으면 delete from child where id = ? 쿼리가 자동으로 발생한다.
- @OneToOne, @OneToMany만 가능하다.
- cascadeType.REMOVE
@Entity
public class Parent {
@Id
@GeneratedValue
private Long id;
@OneToMany(mappedBy = "parent", cascade = CascadeType.REMOVE)
private List<Child> childList = new ArrayList<>();
public void addChild(Child child) {
childList.add(child);
child.setParent(this);
}
}
public class JpaMain {
public static void main(String[] args) {
Parent parent = em.find(Parent.class, id);
em.remove(parent);
tx.commit();
}
}
- cascadeType.REMOVE는 부모 객체를 제거하면 자식도 같이 제거하는 것이다. MySQL의 on delete cascade와 같아 보인다.
- 부모 객체를 제거했을 때 자식도 함께 제거되는 것을 볼 수 있다.
- orphanRemoval = true도 마찬가지로 부모 객체를 제거하면 자식 객체가 고아이므로 cascade = cascadeType.REMOVE처럼 동작한다.
CascadeType.ALL과 orphanRemoval = true 동시에 사용
- Entity는 스스로 생명 주기를 관리한다.
- JPA 영속성 컨텍스트(EntityManager)를 통해 라이프 사이클을 관리한다.
- em.persist()로 영속화하고 em.remove()로 제거한다.
- JPA 영속성 컨텍스트(EntityManager)를 통해 라이프 사이클을 관리한다.
- 두 옵션을 모두 활성화하면 부모 Entity를 통해서 자식의 생명 주기를 관리할 수 있다.
- 부모만 JPA로 영속화하거나 제거하고 자식은 부모가 관리하게 된다.
- DB로 따지면 자식의 DAO나 Repository가 없어도 된다.
- 도메인 주도 설계의 Aggregate Root 개념을 구현할 때 유용하다.
- repository는 Aggregate Root만 컨택하고 나머지는 Repository를 만들지 않는 방법이다.
- Aggregate Root를 통해서 생명주기를 관리한다.
- Aggregate Root가 부모이고 자식은 Aggregate Root가 관리한다.
내 개인적인 생각으로는 혼자서 코드를 작성하면 상관없지만 팀 단위로 작성한다면 내가 Entity 삭제 코드를 작성하지 않아도 쿼리가 날아가는 것을 보면 혼동이 올 수도 있을 것 같다는 생각이 든다. 그리고 실수로 삭제했는데 관련된 거 전부 사라지는 대참사도 벌어질 위험이 있으니 사용해도 정말 신중하게 고민하고 사용해야 할 것 같다.
728x90
'Java > JPA' 카테고리의 다른 글
[JPA] 즉시 로딩과 지연 로딩 (0) | 2023.06.25 |
---|---|
[JPA] 프록시 (0) | 2023.06.25 |
[JPA] 다양한 연관 관계 매핑(다대일, 일대다, 일대일, 다대다) (0) | 2023.06.25 |
[JPA] 연관 관계 (0) | 2023.06.24 |
[JPA] Entity 매핑 (0) | 2023.06.24 |