🎫 OneToOne 연관관계
2022.10.14 - [Backend/JPA] - [JPA] OneToOne 연관관계 (mappedby / fetchType)
🎫 OneToMany, ManyToOne 연관관계
[JPA] ManyToOne, OneToMany 연관관계 (mappedby / fetchType) (tistory.com)
이번에는 다대다 관계 ManyToMany이다.
이번에는 Course와 Student 엔티티를 만들어줬다.
하나의 강의에는 여러명의 학생들이 들을 수 있고
한 학생은 또 여러개의 강의를 들을 수 있다.
Course
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
|
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import javax.persistence.*;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
public class Course {
@Id
@GeneratedValue
private Long id;
private String name;
private String author;
@OneToMany
private List<Review> reviews = new ArrayList<>();
@ManyToMany
private List<Student> students = new ArrayList<>();
@CreationTimestamp
private Timestamp creationTimestamp;
@UpdateTimestamp
private Timestamp updateTimestamp;
public Course(String name, String author) {
this.name = name;
this.author =author;
}
// 연관관계 편의 메소드
public void addReview(Review review) {
this.reviews.add(review);
}
public void addStudent(Student student) {
this.students.add(student);
}
@Override
public String toString() {
return String.format("Course[%s]", name);
}
}
|
cs |
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
37
38
39
40
41
|
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import javax.persistence.*;
import java.util.List;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
public class Student {
@Id
@GeneratedValue
private Long id;
private String name;
@OneToOne(fetch = FetchType.LAZY)
private Passport passport;
@ManyToMany
private List<Course> courses = new ArrayList<>();
public Student(String name) {
this.name = name;
}
public void addCourse(Course course) {
this.courses.add(course);
}
@Override
public String toString() {
return String.format("Student[%s]", name);
}
}
|
cs |
실습을 위해 기본 데이터도 넣어줬다.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
insert into course(id, name, author, creation_timestamp, update_timestamp)
values(10001,'JPA in 50 Steps', '허균', CURRENT_DATE(), CURRENT_DATE());
insert into course(id, name, author, creation_timestamp, update_timestamp)
values(10002,'Spring in 50 Steps', '홍길동', CURRENT_DATE(), CURRENT_DATE());
insert into course(id, name, author, creation_timestamp, update_timestamp)
values(10003,'Spring Boot in 100 Steps', '허난설헌', CURRENT_DATE(), CURRENT_DATE());
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 |
이대로 서버를 실행하면 course 테이블과 student 테이블이 각각 create되는데
게다가 둘 사이의 관계 테이블이 자동으로 생성되는데 이게 각각이 주인인 경우로 총 2개가 생성된다.
하지만 우리는 테이블이 둘다 필요하진 않다. 그래서 연관관계의 주인을 설정해준다.
연관관계 주인 설정
mappedBy = "연관관계의 주인"
연관관계의 주인을 student 쪽으로 잡아줬다.
이대로 서버를 실행하면 하나의 테이블로만 생성이 된다.
만약 이 테이블을 custom하게 정의하고 싶다면 @JoinTable 로 테이블이름, 컬럼명, 반대컬럼명을 정의해줄 수 있다.
관계 테이블의 이름을 student_and_course로, 두 컬럼을 course_id, student_id로 정의해줬다.
서버 실행시 정의한대로 테이블이 생성되는 것을 볼 수 있다.
Fetch.Type
ManyToMany 연관관계에서의 default는 LAZY이다.
테스트에 앞서 결과가 보기 좋게 미리 관계 테이블에 데이터를 넣어줬다.
1
2
3
4
5
6
7
8
|
insert into student_and_course(student_id, course_id)
values(20001, 10001);
insert into student_and_course(student_id, course_id)
values(20002, 10001);
insert into student_and_course(student_id, course_id)
values(20003, 10001);
insert into student_and_course(student_id, course_id)
values(20001, 10003);
|
cs |
간단히 findById를 통해 가져온 Course와 Student의 로그를 찍어보는 테스트를 진행했다.
student_id 가 20001인 한소희가 듣는 course 정보를 가져오겠다.
🛑 둘다 정의 안함 (Default LAZY)
둘다 정의 안한 디폴트 상황일때 ManyToMany 연관관계는 fetch.Type을 LAZY를 갖는다.
student 테이블에서 한번, 관계 테이블에서 한번 쿼리해오는 아주 간단한 모습을 보인다.
🛑 반대 쪽 (course) EAGER
위와 같이 student 테이블에서 한번, 관계 테이블에서 한번 쿼리해 온 후
해당 학생 ID에 딸린 course의 id를 가지고 또 관계 테이블에 쿼리를 보낸다.
🛑 연관관계 주인 쪽 (student) EAGER
하나의 쿼리로 끝나긴 하지만...
student 테이블에서 관계 테이블과 course 테이블을 join 해 오는 쿼리를 날려 그리 좋은 쿼리 같진 않아 보인다.
🛑 둘다 EAGER
제일 피해야할 상황이다.
처음 요청부터 길다.
student_id로 student에서 select하는데 left outer join을 두개나 했다.
첫번째 쿼리에서 가져오는 결과만 이만큼이다.
그리고 관계 테이블에서 course_id에 따라 student를 불러오기도 하고
student_id에 따라 course를 불러오기도 했다.
불필요한 요청이 여러번 일어났다.
이 작은 설정 하나로 요청 과정이 달라지니 유의해서 사용해야되겠다.
코드
참고
Master Hibernate and JPA with Spring Boot in 100 Steps | Udemy
자바 ORM 표준 JPA 프로그래밍 | 김영한 - 교보문고 (kyobobook.co.kr)
'Backend > JPA' 카테고리의 다른 글
[JPA] 다중 Datasource 구성하기 (Spring boot Multi Datasource / DB 여러개 설정) (0) | 2023.06.19 |
---|---|
[JPA] 상속 관계 매핑 전략 (조인 전략 / 단일 테이블 전략 / 구현 클래스마다 테이블 전략 / MappedSuperClass) (0) | 2022.12.01 |
[JPA] ManyToOne, OneToMany 연관관계 (mappedby / fetchType) (0) | 2022.10.18 |
[JPA] OneToOne 연관관계 (mappedby / fetchType) (0) | 2022.10.14 |
[JPA] Entity 생성일시 수정일시 자동 입력 방법 2가지 (@CreatedDate vs @CreationTimestamp) (0) | 2022.10.04 |
댓글