mappedBy vs Cascade

SuYeong·2023년 4월 5일
1

JPA를 사용하다 보면, mappedBycascade를 사용할 때가 있습니다.

두개는 엄연히 다른 역할임에도 불구하고,
연관관계가 있는 두 엔티티 사이에 벌어지는 일이라서 개념이 헷갈립니다.

선요약

mappedBy : 연관관계의 주인이 아님을 표시하는 것
cascade : 부모 엔티티의 행동을 자식 엔티티가 따라가는 것

두 개의 역할이 아예 다릅니다.

사용 엔티티 클래스

부모 엔티티와 자식 엔티티는 공식적으로 사용되는 단어인지는 잘 모르겠습니다.

1번 엔티티의 필드에 2번 엔티티 클래스 타입의 필드가 있다면,
1번 엔티티를 부모 엔티티, 2번 엔티티를 자식 엔티티라고 부릅니다.

아래 두개의 엔티티로 설명해보자면,

@Getter
@NoArgsConstructor
@Entity
public class Woman {

    @Id
    private Long id;

    @Column
    private String name;

    @Setter
    @OneToOne(mappedBy = "woman", cascade = CascadeType.PERSIST)
    private Man man;

    public Woman(Long id, String name) {
        this.id = id;
        this.name = name;
    }
}
@Getter
@NoArgsConstructor
@Entity
public class Man {

    @Id
    private Long id;

    @Column
    private String name;

    @Setter
    @OneToOne
    private Woman woman;

    public Man(Long id, String name) {
        this.id = id;
        this.name = name;
    }
}

Woman이 부모라고 한다면, Man은 자식입니다.
Man이 부모라고 한다면, Woman은 자식입니다.

부모 자식 관계가 절대적인 것이 아니라 상대적인 의미입니다.

연관관계의 주인은 Man입니다.
그리고 WomanCascadeType.PERSIST를 붙여줬습니다.

테스트 코드

테스트에서 확인할 부분은 아래와 같습니다.
1. woman만 영속화 했는데, man도 같이 영속화가 되는가? (Cascade.PERSIST 테스트)
2. woman은 연관관계의 주인이 아니므로, man을 db에서 가져왔을 때, manwoman의 FK를 가지고 있지 않아야 한다.

@DisplayName("CascadeType.PERSIST 테스트")
@Test
void given_when_then(){
    
    woman.setMan(man); // woman에 man 저장 
    em.persist(woman); // woman 영속화. 이때, man도 같이 영속화가 될 것임.
    em.flush();
    em.clear();

    Man findMan = em.find(Man.class, man.getId());
    assertThat(findMan.getWoman()).isNull(); 
    // man의 FK에 woman이 없는 경우 테스트 성공!
    }

테스트 결과

Hibernate: 
    insert 
    into
        woman
        (name, id) 
    values
        (?, ?)
====================================
insert 
    into
        man
        (name, woman_id, id) 
    values
        (?, ?, ?)
====================================
Hibernate: 
    select
        man0_.id as id1_4_0_,
        man0_.name as name2_4_0_,
        man0_.woman_id as woman_id3_4_0_,
        woman1_.id as id1_6_1_,
        woman1_.name as name2_6_1_ 
    from
        man man0_ 
    left outer join
        woman woman1_ 
            on man0_.woman_id=woman1_.id 
    where
        man0_.id=?

em.persist(man); 을 따로 수행하지 않았음에도, manINSERT문이 나가는 것이 보입니다.
이것은 CascadeType.PERSIST 때문입니다.

man을 db에서 가져왔을 때, woman에 대한 정보가 null인 것을 확인할 수 있습니다.
이것은 Woman이 연관관계의 주인이 아니기 때문에 그렇습니다.

결론

Cascade는 부모 엔티티 필드(메모리)에 저장된 자식 엔티티를 영속화할 뿐,
FK 연결과는 전혀 무관합니다.

profile
안녕하세요

1개의 댓글

comment-user-thumbnail
2023년 7월 21일

요즘 바쁘신가요
글이 뜸하네요

답글 달기