JPA를 사용하다 보면, mappedBy
와 cascade
를 사용할 때가 있습니다.
두개는 엄연히 다른 역할임에도 불구하고,
연관관계가 있는 두 엔티티 사이에 벌어지는 일이라서 개념이 헷갈립니다.
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
입니다.
그리고 Woman
에 CascadeType.PERSIST
를 붙여줬습니다.
테스트에서 확인할 부분은 아래와 같습니다.
1. woman
만 영속화 했는데, man
도 같이 영속화가 되는가? (Cascade.PERSIST
테스트)
2. woman
은 연관관계의 주인이 아니므로, man
을 db에서 가져왔을 때, man
이 woman
의 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);
을 따로 수행하지 않았음에도, man
의 INSERT
문이 나가는 것이 보입니다.
이것은 CascadeType.PERSIST
때문입니다.
man
을 db에서 가져왔을 때, woman
에 대한 정보가 null
인 것을 확인할 수 있습니다.
이것은 Woman
이 연관관계의 주인이 아니기 때문에 그렇습니다.
Cascade
는 부모 엔티티 필드(메모리)에 저장된 자식 엔티티를 영속화할 뿐,
FK 연결과는 전혀 무관합니다.
요즘 바쁘신가요
글이 뜸하네요