Backend/JPA

[JPA] ManyToMany 연관관계 (mappedby / fetchType)

비전공자 기록광 2022. 10. 26. 23:00
반응형

🎫 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를 불러오기도 했다.

 

불필요한 요청이 여러번 일어났다.

 

 

이 작은 설정 하나로 요청 과정이 달라지니 유의해서 사용해야되겠다.


코드

TIL/Spring_Boot/jpa_study/src/main/java/com/udemy/jpa_study/relationships at main · recordbuffer/TIL · GitHub

 

GitHub - recordbuffer/TIL: Today I Learned

Today I Learned. Contribute to recordbuffer/TIL development by creating an account on GitHub.

github.com

참고

Master Hibernate and JPA with Spring Boot in 100 Steps | Udemy

 

Master Hibernate and JPA with Spring Boot in 100 Steps

Learn Hibernate, JPA (Java Persistence API) and Spring Data JPA using Spring and Spring Boot

www.udemy.com

자바 ORM 표준 JPA 프로그래밍 | 김영한 - 교보문고 (kyobobook.co.kr)

 

자바 ORM 표준 JPA 프로그래밍 | 김영한 - 교보문고

자바 ORM 표준 JPA 프로그래밍 | 자바 ORM 표준 JPA는 SQL 작성 없이 객체를 데이터베이스에 직접 저장할 수 있게 도와주고, 객체와 관계형 데이터베이스의 차이도 중간에서 해결해준다. 이 책은 JPA

product.kyobobook.co.kr

 

반응형