[JPA] OneToOne 연관관계 (mappedby / fetchType)
매일같이 사용해도 매일같이 헷갈리는 연관관계를 다시 정리해봤다.
OneToOne 연관관계는 오직 서로가 하나뿐인 1:1 관계를 말하고 이 둘은 FK로 연결되어 있다.
OneToOne 연관관계의 특징을 알아보기 위해 엔티티를 만들어줬다.
하나는 Student 엔티티이고
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
|
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToOne;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
public class Student {
@Id
@GeneratedValue
private Long id;
private String name;
@OneToOne
private Passport passport;
public Student(String name) {
this.name = name;
}
@Override
public String toString() {
return String.format("Student[%s]", name);
}
}
|
cs |
또 다른 하나는 Passport 엔티티이다.
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
|
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToOne;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
public class Passport {
@Id
@GeneratedValue
private Long id;
private String number;
@OneToOne
private Student student;
public Passport(String number) {
this.number = number;
}
@Override
public String toString() {
return String.format("Passport[%s]", number);
}
}
|
cs |
실습을 위한 간단한 데이터도 미리 넣어준다.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
insert into passport(id,number)
values(40001,'E123456');
insert into passport(id,number)
values(40002,'N123457');
insert into passport(id,number)
values(40003,'L123890');
insert into student(id,name, passport_id)
values(20001,'한소희', 40001);
insert into student(id,name, passport_id)
values(20002,'아이유', 40002);
insert into student(id,name, passport_id)
values(20003,'김태리', 40003);
|
cs |
연관관계 주인 설정
이대로 서버를 실행하면
엔티티 둘다 관계를 가지는 FK 가 생성되는 것을 볼 수 있다.
하지만 이렇게 되면 데이터가 중복되기 때문에 별로 좋아보이지 않는다.
이 중복을 제거하기 위해 연관관계의 주인을 정의해준다.
mappedBy = "연관관계의 주인"
그리고 서버를 시작하면 연관관계의 주인인 passport의 id가 FK로 정의되어 생성된 걸 확인할 수 있다.
Fetch.Type
OneToOne 연관관계에서는 fetch join의 default가 Eager이다.
즉 한쪽의 데이터를 조회해도 다른 쪽의 데이터가 같이 가져와진다는 것이다.
간단히 findById를 통해 가져온 Student를 로그로 찍어보는 테스트를 진행했다.
🛑 둘다 정의 안함 (Default Eager)
둘다 정의 안한 디폴트 상황일때 OneToOne 연관관계는 fetch.Type을 Eager를 갖는다.
테스트를 실행해보면 left outer join을 통해 student와 passport를 조인해와 해당 학생의 passport 정보를 뿌려준다.
학생 id 20001 의 passport number은 E123456 이다.
잘 가져온 걸 볼 수 있다.
🛑 Passport 연관관계의 주인, FetchType.LAZY
위에 모두 Eager일때와 같다.
🛑 Student FetchType.LAZY
이렇게 되면 select 쿼리를 두번 날린다.
Student에서 Passport와의 관계를 LAZY로 정의했기때문에
Student에서 select할 때 join하지 않는다.
하지만 Passport에서는 default = Eager 상태이기 때문에
뒤이어 Passport에서 Student로 조인해 해당 student의 FK로 연결된 passport_id를 가져온다.
🛑 둘다 Lazy
이 경우에는 각각의 테이블로 두번 select 요청을 보내어
학생 id에 해당하는 학생의 passport id를 가져와 passport 정보를 가져온다.
각각 설정만 다르게 해줘도 요청의 결과가 달라진다.
코드
참고