[JPA] 연관관계 매핑 - 다대다 ( @ManyToMany )
학생은 여러 강의를 들을 수 있고 강의는 여러 수강생을 가진다. 이런 다대다 관계는 한 가지 문제가 있다. 관계형DB는 외래키로 테이블에 접근하는데, 외래키는 다대일 관계에서 '다'쪽이 갖는 것이 '규칙'이다. 그럼 다대다 관계에서는 누가 외래키를 가져야 할까?
둘 다 못 갖는다. 학생은 수많은 강의의 외래키를 관리할 수 없고 강의 또한 수많은 학생을 하나하나 외래키로 관리할 수 없다. 그래서 관계형 DB에서는 연결테이블을 만든다.
연결테이블이 '다' 포지션이 되고 학생과 강의가 '일' 포지션이 되면 '다대다'관계를 구현할 수 있다. 자료구조 강의 course_id가 1이라면
SELECT student_id FROM 연결 WHERE course_id = 1
이런 간단한 SQL문으로 자료구조강의를 듣는 학생리스트를 출력할 수 있다. 여기에 JOIN을 사용하면 학생테이블에 접근하여 학생 정보도 출력할 수 있다.
@ManyToMany
JPA는 다대다 관계를 일대다, 다대일 관계로 표현하는 과정을 '자동화'한다. 학생 엔티티와 강의 엔티티를 @ManyToMany로 다대다관계임을 명시하면 JPA가 자동으로 연결테이블 하나를 새로 생성한다.
Student 엔티티
@Entity
@Data
public class Student {
@Id @GeneratedValue
@Column(name ="STUDENT_ID")
private Long id;
private String name;
@ManyToMany
@JoinTable(name = "STUDENT_COURSE") //연결테이블 이름지정
private List<Course> courses = new ArrayList<>();
}
Course 엔티티
@Entity
@Data
public class Course {
@Id
@GeneratedValue
@Column(name = "COURSE_ID")
private Long id;
private String name;
@ManyToMany(mappedBy = "courses") // 연결테이블과 연관있음을 표시
private List<Student> students = new ArrayList<>();
}
다대다 관계이므로 Student 엔티티, Course 엔티티 둘 다, List로 양쪽 엔티티를 참조한다. Student 엔티티의 @JoinTable 어노테이션에 생성할 연결테이블의 이름을 입력한다. Course 엔티티는 Student의 연결테이블에 본인도 연관되어 있음을 mappedby 속성으로 표시한다. 이제, 간단히 영속화하는 코드를 작성하고 실행하면 아래와 같은 결과가 나온다.
연결테이블 ( STUDENT_COURSE 테이블 )
학생테이블
강의테이블
이렇듯, @ManyToMany는 JPA가 다대다관계를 일대다, 다대일관계 테이블로 구현할 수 있도록 표시하는 어노테이션이다.
그러나 실무에서는 잘 사용되지 않는다.
JPA가 연결테이블을 자동으로 생성하면 컬럼은 외래키만 존재하게 된다. 외래키만 컬럼으로 갖는 연결테이블은 실무에서 거의 없다. 학생-강의 사이의 필요한 데이터가 존재한다. 예를들어, 연결테이블에 '수강날짜' 컬럼이 있으면 특정학생이 특정강의를 언제 수강했는지 데이터를 저장할 수 있다. 이렇듯 연결테이블에는 다양한 데이터가 저장 될 수 있는데 @ManyToMany는 오로지 외래키 컬럼만 가진 연결테이블을 생성한다.
그래서 실무에서는 JPA에게 자동으로 연결테이블 생성을 맡기지 않고 연결테이블을 개발자가 엔티티로 구현한다. 그리고 @ManyToOne, @OneToMany으로 연결 엔티티와 학생 엔티티, 강의 엔티티를 연결하여 다대다 관계를 구현한다. 연결 엔티티는 여러 필드를 가질 수 있으므로 여러 컬럼을 가진 연결테이블을 만들 수 있다.
참고자료