JPA 프록시와 연관관계 - 영속성 전이(CASCADE)와 고아 객체

2022. 7. 11. 21:52·공부/JPA
반응형

출처 : 자바 ORM 표준 JPA 프로그래밍 인프런 강의

 

JPA 게시글은 대부분 인프런의 김영한님의 강의인 '자바 ORM 표준 JPA 프로그래밍' 기반으로 내용을 정리했습니다.

 

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

영속성 전이 : CASCADE

  • 특정 엔티티를 영속 상태로 만들 때 연관된 엔티티도 함께 영속 상태로 만들고 싶을 때

  • 부모가 em.persist()를 날리면 자식까지 모두 자동으로 함께 저장되는 기능
  • 부모와 연관된 자식들을 한 번에 영속성 컨텍스트에 저장하고 싶은 경우 사용한다.

 

예제

Parent.class

@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);
    }
    
    ... 생략
}

Child.class

@Entity
public class Child {

    @Id
    @GeneratedValue
    private Long id;

    private String name;

    @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);

위의 코드는 Parent 객체를 중심으로 코드가 작성됐다. 그런데 객체들을 모두 영속성 컨텍스트에 올리려다 보니 Child 덕분에 배보다 배꼽이 더 커져버린 경우가 되어버렸다. 이런 귀찮음을 해결해 줄 수 있는 게 CASCADE이다.

 

CASCADE 적용

// Parent.class 중
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
private List<Child> childList = new ArrayList<>();

Parent.class에 연관 매핑 뒷부분에 cascade 옵션을 달아주면 적용은 끝이다. 실행 클래스에서 em.persist(ChildX); 코드 부분을 삭제해주어야 한다.

// 출력 로그

Hibernate: 
    /* insert hellojpa.Parent
        */ insert 
        into
            Parent
            (name, id) 
        values
            (?, ?)
Hibernate: 
    /* insert hellojpa.Child
        */ insert 
        into
            Child
            (name, parent_id, id) 
        values
            (?, ?, ?)
Hibernate: 
    /* insert hellojpa.Child
        */ insert 
        into
            Child
            (name, parent_id, id) 
        values
            (?, ?, ?)

cascade를 설정하고 애플리케이션을 실행시켜보면 em.persist()는 하나인데 총 세 개의 insert 쿼리가 전송된다. 즉 두 개의 자식도 함께 자동으로 전달되는 것을 볼 수 있다.

CASCADE 종류

  • ALL : 모두 적용
  • PERSIST : 영속
  • REMOVE : 삭제
  • MERGE : 병합
  • REFRESH : REFREESH
  • DETACH : SETACH

 

주의사항!

  • 영속성 전이는 연관관계를 매핑하는 것과 아무런 관련이 없다.
  • 엔티티를 영속화할 때 연관된 엔티티도 함께 영속화하는 편리함 제공
  • 하나의 부모(소유자가 하나)가 자식을 관리할 때는 의미가 있는 기능이다.
  • 관계가 복잡하게 부모, 자식 외 다른 엔티티와 얽혀있는 경우 사용하지 않는 것이 좋다.

 


 

고아 객체

  • 고아 객체 : 부모 엔티티와 연관관계가 끊어진 자식 엔티티
  • 고아 객체 제거 : 고아 객체(자식 엔티티)를 자동으로 삭제

 

예제

//Parent.class 중 일부
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Child> childList = new ArrayList<>();

orphanRemoval = true 속성을 주면 고아 객체 제거 기능이 활성화 된다.

실행 클래스

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); // 자식 제거


// 출력 로그
Hibernate: 
    /* delete hellojpa.Child */ delete 
        from
            Child 
        where
            id=?

출력 로그에 보면 DELETE 쿼리를 날린 걸 볼 수 있다.

 

주의 사항!

  • 참조가 제거된 엔티티는 다른 곳에서 참조하지 않는 고아 객체로 보고 삭제하는 기능이다.
  • 참조하는 곳이 하나일 때 사용해야 한다.
  • 특정 엔티티가 개인 소유할 때 사용해야 한다.
  • @OneToOne, @OneToMany에만 사용이 가능하다.
  • 개념적으로 부모를 제거하면 자식은 고아가 된다. 따라서 고아 객체 제거 기능을 활성화하면 부모를 제거할 때 자식도 함께 제거된다. CASCADEType.REMOVE와 같은 동작이다.

예제

// Parent.class
// cascade 주석처리
@OneToMany(mappedBy = "parent", /*cascade = CascadeType.ALL,*/ orphanRemoval = true)
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);
em.persist(child1);
em.persist(child2);

em.flush();
em.clear();

Parent findParent = em.find(Parent.class, parent.getId());
//findParent.getChildList().remove(0);
em.remove(findParent);


// 출력 로그
Hibernate: 
    /* delete hellojpa.Child */ delete 
        from
            Child 
        where
            id=?
Hibernate: 
    /* delete hellojpa.Child */ delete 
        from
            Child 
        where
            id=?
Hibernate: 
    /* delete hellojpa.Parent */ delete 
        from
            Parent 
        where
            id=?

위의 코드에서 부모를 삭제했더니 Child에 대한 DELETE 쿼리도 발생해서 총 3개의 DELETE문이 날아간다.

 


 

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

  • CascadeType.ALL + orphanRemovel=true
  • 스스로 생명주기를 관리하는 엔티티는 em.persist()로 영속화, em.remove()로 제거
    • 라이프 사이클을 JPA의 영속성 컨텍스트를 통해서 관리함
  • 두 옵션을 모두 활성화하면 부모 엔티티를 통해서 자식의 생명 주기를 관리할 수 있다.
    • Child의 라이프 사이클을 Parent로 관리할 수 있다.
    • DB로 비유했을 때 DAO, Repository가 없어도 된다는 의미다.
  • 도메인 주도 설계(DDD)의 Aggregate Root 개념을 구현할 때 유용하다.
반응형
저작자표시 비영리 변경금지 (새창열림)
'공부/JPA' 카테고리의 다른 글
  • JPA 값 타입 - 임베디드 타입
  • JPA 값 타입 - 기본값 타입
  • JPA 프록시와 연관관계 - 즉시 로딩과 지연 로딩
  • JPA 프록시와 연관관계 - 프록시
데부한
데부한
어차피 할 거면 긍정적으로 하고 싶은 개발자
    반응형
  • 데부한
    동동이개발바닥
    데부한
  • 전체
    오늘
    어제
    • 분류 전체보기 (307)
      • 방통대 컴퓨터과학과 (27)
        • 잡담 (9)
        • 3학년1학기 (17)
      • 프로젝트 및 컨퍼런스 회고 (1)
        • 프로젝트 (4)
        • 한이음 프로젝트 (0)
        • 회고 (3)
      • 공부 (165)
        • Spring (37)
        • JPA (71)
        • 인프런 워밍업 클럽_BE (10)
        • Java (6)
        • React.js (27)
        • 넥사크로 (11)
        • 기타 (3)
      • 알고리즘 (85)
        • 알고리즘 유형 (10)
        • 알고리즘 풀이 (57)
        • SQL 풀이 (18)
      • 에러 해결 (13)
      • 잡담 (7)
        • 국비교육 (2)
        • 구매후기 (5)
        • 진짜 잡담 (0)
  • 블로그 메뉴

    • Github
    • Linkedin
    • 홈
    • 방명록
    • 글쓰기
    • 관리
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    SpringBoot를 이용한 RESTful Web Service 개발
    egov
    Java
    자바스크립트
    인프런
    react
    코딩테스트
    RESTful
    JPA
    운영체제
    프로그래머스
    알고리즘
    oracle
    스프링부트
    방통대
    넥사크로
    개발자
    springboot
    QueryDSL
    MSA
    토비의스프링부트
    기출문제
    에러해결
    Spring
    SQL
    프론트엔드
    IT
    백준
    전자정부프레임워크
    토이프로젝트
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
데부한
JPA 프록시와 연관관계 - 영속성 전이(CASCADE)와 고아 객체
상단으로

티스토리툴바