본문 바로가기
Study/JPA

프록시 ) 영속성 전이(CASCADE)와 고아 객체

by _비니_ 2024. 7. 26.
  • 특정 엔티티를 영속 상태로 만들 때 연관된 엔티티도 함께 영속 상태로 만들고 싶을 때
    • EX) 부모 엔티티를 저장할 때 자식 엔티티도 함께 저장.
    =⇒ 즉시로딩, 지연로딩과는 완전 무관
try {
    Child child1 = new Child();
    Child child2 = new Child();

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

    em.persist(parent); //위에랑 다르게 parent만 persist함
//            em.persist(child1);
//            em.persist(child2);

    tx.commit();
@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..
}
@Entity
public class Child {
    @Id
    @GeneratedValue
    private Long id;

    private String name;

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

    //Getter, Setter..
}
try {
	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();
	} catch (Exception e) {

 

persist를 3번 하고 싶은 게 아니라, 코드를 Parent 중심으로 짜고 싶은 것.

Parent를 persist할 때, Child는 자동으로 persist되면 좋겠어..!

⇒ 이 때 쓰는 것이 cascade

 

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

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

    em.persist(parent); //위에랑 다르게 parent만 persist함
//            em.persist(child1);
//            em.persist(child2);

    tx.commit();

 

그래도 child1, child2 둘 다 persist됨!!!

 

 

Cascade는 부모 엔티티를 특정 작업(예: persist)할 때,

연관된 자식 엔티티들에게도 동일한 작업을 자동으로 적용하는 기능

  • Cascade는 부모 엔티티 작업 시 자식 엔티티들도 자동으로 동일한 작업을 수행하게 함
  • Parent 엔티티만 persist해도 Child 엔티티들이 자동으로 데이터베이스에 저장됨

 

CASCADE의 종류

  • ALL: 모두 적용
  • PERSIST: 영속 (저장할 때만 lifecycle 맞출 때)
  • REMOVE: 삭제
  • MERGE: 병합
  • REFRESH: REFRESH
  • DETACH: DETACH

만약 Child가 다른 곳과도 관계가 있으면 cascade를 사용하면 안됨

 

📌 Parent랑 Child의 life cycle이 거의 동일할 때만 사용 (유사할 때)

📌 단일 소유자일 때만 사용

 

 

고아객체

고아 객체 제거

  • 부모 엔티티와 연관관계가 끊어진 자식 엔티티를 자동으로 삭제
  • orphanRemoval = true

<기존 테이블>

@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Child> childList = new ArrayList<>();
try {
  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();
}

 

 

0번째 행이 삭제된 것을 확인할 수 있음

 

orphanRemoval를 걸어둔 아이는 해당 컬렉션? 에서 제거된 아이는 삭제가 됨.

 

🚨 고아 객체 주의

  • 참조가 제거된 엔티티는 다른 곳에서 참조하지 않는 고아 객체로 보고 삭제하는 기능
  • 참조하는 곳이 하나일 때 사용해야함!
    • 즉, 자식 엔티티가 여러 곳에서 참조되지 않을 때 사용
  • 특정 엔티티가 개인 소유할 때 사용
    • 예를 들어, 특정 부모 엔티티에만 종속적인 자식 엔티티.
  • @OneToOne, @OneToMany만 가능
  • 참고: 개념적으로 부모를 제거하면 자식은 고아가 된다. 따라서 고아 객체 제거 기능을 활성화 하면, 부모를 제거할 때 자식도 함께 제거된다. 이것은 CascadeType.REMOVE처럼 동작한다.

 

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

CascadeType.ALL + orphanRemoval=true 두 옵션을 다 활성화하면?

  • CascadeType.ALL과 orphanRemoval=true를 동시에 사용하면 부모 엔티티를 통해 자식 엔티티의 생명주기를 완전하게 관리할 수 있음
  • 부모 엔티티가 persist되면 자식 엔티티도 persist되고, 부모 엔티티가 remove되면 자식 엔티티도 remove됨
    • 또한 부모 엔티티의 자식 리스트에서 자식 엔티티를 제거하면 해당 자식 엔티티도 자동으로 삭제됨
  • 스스로 생명주기를 관리하는 엔티티는 em.persist()로 영속화, em.remove()로 제거
  • 도메인 주도 설계(DDD)의 Aggregate Root개념을 구현할 때 유용
    • Aggregate Root: 관련된 모든 엔티티의 생명주기를 관리하는 엔티티
반응형

'Study > JPA' 카테고리의 다른 글

프로젝션과 페이징  (0) 2024.07.27
객체지향 쿼리 언어 소개  (0) 2024.07.27
값 타입 2  (0) 2024.07.27
값 타입 1  (0) 2024.07.27
프록시  (0) 2024.07.25