Posts [자바 ORM 표준 JPA 프로그래밍-기본편] 영속성 전이: CASCADE
Post
Cancel

[자바 ORM 표준 JPA 프로그래밍-기본편] 영속성 전이: CASCADE

본 포스팅은 인프러의 JPA 기본편을 수강하고 정리하는 내용입니다.


영속성 전이: CASCADE

  • 특정 엔티티를 영속 상태로 만들 때 연관된 엔티티도 함께 영속 상태로 만들도 싶을 때
    • 예: 부모 엔티티를 저장할 때 자식 엔티티도 함께 저장.

image

영속성 전이: 저장

1
@OneToMany(mappedBy="parent", cascade=CascadeType.PERSIST)

image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
@Entity
public class Parent {
    @Id
    @GeneratedValue
    private Long id;

    private String name;

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

    private String name;

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

    ...
}

public class JpaMain {
    public static void main(String[] args) {
        ...

        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) {
            tx.rollback();
            e.printStackTrace();
        } finally {
            em.close();
        }

        emf.close();
    }
}

영속성 전이: CASCADE - 주의!

  • 영속성 전이는 연관관계를 매핑하는 것과 아무 관련이 없음
    • 절대 오해하면 안됨
  • 엔티티를 영속화할 때 연관된 엔티티도 함께 영속화하는 편리함을 제공할 뿐

CASCADE의 종류

  • ALL: 모두 적용
  • PERSIST: 영속
    • 저장할때만 Lifecycle 맞추기 위해 사용
  • REMOVE: 삭제
  • MERGE: 병합
  • REFRESH: REFRESH
  • DETACH: DETACH

언제 써야하나?

하나의 부모가 자식들을 관리할땐 의미가 있다. 게시물과 첨부파일들 경로의 경우엔 쓸 수 있다. 왜냐하면 한 게시물에서만 관리하기 때문이다. 쓰면 안되는 케이스는 첨부파일의 경로를 여러(다른) 엔티티에서 관리하는 경우이다.

정리하자면 소유자가 하나일때는 써도 된다. 하지만 다른 엔티티에서도 연관관계가 존재한다면 쓰면 안된다. 그러면 운영이 너무 힘들어진다. (단일 엔티티에 완전히 종속적일때, 보통 라이프사이클이 똑같기 때문에)

1) Parent와 Child의 라이플사이클이 똑같을때

2) 소유자가 하나일 때(Parent엔티티만 Child객체를 소유할때)

고아 객체

  • 고아 객체 제거: 부모 엔티티와 연관관계가 끊어진 자식 엔티티를 자동으로 삭제
  • orphanRemoval = true
1
2
3
4
Parent parent1 = em.find(Parent.class, id);
parent1.getChildren().remove(0); //자식 엔티티를 컬렉션에서 제거

SQL: DELETE FROM CHILD WHERE ID=?

예제 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
@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<>();

    ...
}

@Entity
public class Child {
    @Id
    @GeneratedValue
    private Long id;

    private String name;

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

   ...
}

public class JpaMain {
    public static void main(String[] args) {
        ...

        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();
        } catch (Exception e) {
            tx.rollback();
            e.printStackTrace();
        } finally {
            em.close();
        }

        emf.close();
    }

}

결과적으로 DB에 하나의 데이터만 남게된다.

image

고아 객체 - 주의

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

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

  • CascadeType.ALL + orphanRemovel=true
  • 스스로 생명주기를 관리하는 엔티티는 em.persist()로 영속화, em.remove()로 제거
  • 두 옵션을 모두 활성화 하면 부모 엔티티를 통해서 자식의 생명주기를 관리할 수 있음
    • DB로 따지면 child객체의 DAO나 레포지토리가 없어도 된다 ~~~ 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()); // em.remove(findParent); findParent.getChildList().remove(0);

    tx.commit(); ~~~

  • 도메인 주도 설계(DDD)의 Aggregate Root개념을 구현할 때 유용
    • 레포지토리는 Aggregate Root만 컨택하고 나머지는 레포지토리를 만들지 않는 것이 더 낫다라는 설명이 있는데 이때 유용하다.
This post is licensed under CC BY 4.0 by the author.

[자바 ORM 표준 JPA 프로그래밍-기본편] 즉시 로딩과 지연 로딩

[자바 ORM 표준 JPA 프로그래밍-기본편] 실전 예제5 - 연관관계 관리