본문 바로가기

Spring/JPA

[JPA] 프록시와 연관관계 - 2

728x90

4) 영속성 전이 :CASCADE (생각보다 많이 씀) 

 

CASCADE 란? 즉시 로딩 레이지 로딩과 아무 관계도 아니다!

(헷갈릴 수는 있음) 

특정 엔티티를 영속화 시킬 때 연관 엔티티도 함께 영속화 시키는 것을 말한다. 예시를 다음과 같이 보자. 

@Entity
@Data
public class Parent {

    @Id
    @GeneratedValue
    private Long id;
    private String name;
    @OneToMany(mappedBy = "parent")
    private List<Child> children = new ArrayList<>();

    public void addChild(Child child) {

        children.add(child);
        child.setParent(this);

    }
}

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

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

//

//

@Test
@DisplayName("CASCADE")
void test1() {

    tx.begin();
    try {

        Child child1 = new Child();
        Child child2 = new Child();


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

        // Parent 중심으로 할 것이기 때문에, Child 는 Parent 영속화되면 좀 자동으로 되었으면 좋겠음
        em.persist(parent);
        em.persist(child1);
        em.persist(child2);
        
        tx.commit();
    } catch (Exception e) {
        e.printStackTrace();
        tx.rollback();
    } finally {
        em.close();
    }
    emf.close();
}

 

persist 가 저렇게 점점 복잡해질 수도 있으니, 다음과 같이 해줄 수 있다. 

 

@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)\

 private List<Child> children = new ArrayList<>();

 

로 바꿀 수 있다. 

이제는 저 위에 child persist  들을 하지 않더라도, em.persist(parent) 만 하더라도 child 두개 모두 영속성 컨텍스트에 등록된다. Parent 안에 있는 Child(?) 또 상속관계는 아님. 그런 상황에서 한꺼번에 하는걸 cascade 라고 함. 

 

>>> 영속성 전이는 매핑이랑 아무 상관 없다! 

>>> ALL : PERSIST : REMOVE (ALL: 은 다 맞춰서 라이프 사이클 // Persist : 저장에 대해서만 맞춤, 그 이후로는 안맞춤) 

 

그렇다고 1:N에는 다 걸면 되는거냐?? 그건 ㄴㄴㄴㄴ

만약에 N의 대상이 되는 애들이 그 1만이 관리한다. 그러면 CASCADE 쓰면 좋음. 하지만 그 N 대상을 관리하는 객체들이 여러개, 

 

가령, 

Teacher 라는 엔티티가 생기고, Teacher 가 Child 와 또다른 1:N 관계면 Cascade 안하는게 좋음. 

왜냐면 그러면 알게 모르게 Query 들이 더 생길 수 있기 때문.

 

그냥 문제의 여지가 있는것은 안쓰는게 제~~일 깔끔함. 보통 이렇게 간단한 관계들이 아니라 진짜 개복잡함 엔티티들. 

 

 

5) 고아 객체

 

> 부모 엔티티와 연관관계가 끊어진 자식 엔티티를 자동으로 삭제. 

다음과 같이 고아 객체 제거를 활성화 시킬 수 있다. 

 

@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true)

 private List<Child> children = new ArrayList<>();

 

Parent 는 이 컬렉션을 관리하고 있는데, 이 컬렉션에서 빠지는 Child 객체들은 자동으로 삭제되어 DELETE 쿼리가 나가게 된다. 위에 예시에서 조금 더 연장을 해보자. 

 

            // Parent 중심으로 할 것이기 때문에, Child 는 Parent 영속화되면 좀 자동으로 되었으면 좋겠음
            em.persist(parent);
//            em.persist(child1);
//            em.persist(child2);

            // 그냥 제대로 쿼리 확인하기 위함
            em.flush();
            em.clear();

            Parent parent1 = em.find(Parent.class, parent.getId());
            parent.getChildren().remove(0);

            tx.commit();

이와 같이 연장후 실행할 경우, child 중 하나가 db에서 삭제됨. 

이거 조심해야하지만, 정말 쓰기도 함. 

참조하는 곳이 하나일 때 사용하는 것, 특정 엔티티가 완전히 소유 대상일때만 사용해야 함. Cascade 와 비슷. 

 

orphanRemoval 의 또하나의 특징은, 

자식들을 모두 삭제함. 

(약간 특정 객체들 삭제할 때 사용하기 좋을 듯??) 

>> 가령, 위에서 parent1 을 삭제하면, parent1.getChildren 해서 조회가 가능한 모든 Child 객체들에 대한 DELETE 쿼리가 날라감. 위에서 em.remove(parent1) 을 하면 Parent 객체를 삭제하면서, 조회되는 Child 에 대한 DELETE 쿼리들도 다 날라간다. 

 

 

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

 

둘다 All, True 로 각각 켜주게 되면, 부모 엔티티를 통해서 자식의 생명 주기를 완벽히 통제할 수 있다. (다시 한번 말하지만 Parent 로 삼는 객체가 하나일 때만 써야함!! 안그러면 겁나 꼬임) 

 

em.persist(parent);

 

em.remove(parent);

 

>> child 에 대해서는 하나도 제어하지 않았는데 DB에 저장되었다가, 삭제되었다가 함. 

>> Child의 생명주기는 온전히 Parent 가 관리하게 되는 것임. 

>> 이거가 나중에 도메인 주도 설계 (DDD) 의 Aggregate Root  개념 구현할 때 용이함. (아예 모르는 내용) 

>> Repository의 제한적인 생성에 대한 내용임. Child 에 대한 Repo 를 하나도 생성하지 않고, Aggregate Root 가 Child를 관리하는 그런 거임. (즉, Parent 가 이 설계를 주도하는 것) 

 

이상, 연관관계 및 프록시 끝~!~!

728x90

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

[JPA] JPQL 쿼리  (0) 2022.12.29
[JPA] - 값 타입  (0) 2022.12.29
[JPA] 프록시와 연관관계 정리  (0) 2022.12.26
[JPA] 연관관계 매핑 -2  (0) 2022.12.26
[JPA] - 엔티티 매핑  (1) 2022.12.21