본문 바로가기

JPA/양방향연관관계

N:M(다 대 다) 매핑 2: @ManyToMany와 @JoinTable

 

2021.05.20 - [JPA/양방향연관관계] - N:M(다 대 다) 매핑 1: 매핑테이블

 

N:M(다 대 다) 매핑 1: 매핑테이블

RDB에서는 정규화된 테이블 2개로 N:M 관계를 매핑하는 것이 불가능하다. (정규화란 Table을 아름답게 만드는 과정(?)이다. 정규화에 대해 더 궁금하다면 검색을!!!) 따라서 매핑테이블을 따로 추가

code-mania.tistory.com

 

Entity 설계를 보기 전에 테이블이 어떻게 설계됐는지 여기서 먼저 확인해보자~~~

@ManyToMany와 @JoinTable

이번 글에서는 'N:M(다 대 다) 매핑 1: 매핑테이블'에서 설계했던 테이블을 Entity로 만들어볼 것이다.
@ManyToMany@JoinTable 어노테이션을 사용해서 N:M 양방향 연관관계를 매핑해보겠다.

각 Entity에서 연관관계를 매핑하는 부분만 간단하게 살펴보겠다.

 


Student Entity

package hellojpa;

import lombok.Data;

import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;

@Entity
@Data
public class Student {

    @Id @GeneratedValue
    private Long id;

    private String name;

    private int age;

    @ManyToOne
    @JoinColumn(name = "CLUB_ID", insertable = false, updatable = false)
    private Club club;

    @OneToOne
    @JoinColumn(name = "LOCKER_ID")
    Locker locker;

    @ManyToMany
    @JoinTable(name = "StudentSubject",
            joinColumns = @JoinColumn(name = "STUDENT_ID"),
            inverseJoinColumns = @JoinColumn(name = "SUBJECT_ID"))
    private List<Subject> subjects = new ArrayList<>();
}

N:M 관계를 매핑하므로, subjects의 Type을 List로 해줘야 한다.

@ManyToMany Annotation은 N:M 관계 매핑에서 사용하는 어노테이션이고,
@JoinTable은 매핑테이블을 설정하는 어노테이션이다.
(테이블명은 StudentSubject로 하였고, Student Table을 참조하는 컬럼명은 STUDENT_ID, 반대쪽의 Subject Table을 참조하는 컬럼명은 SUBEJCT_ID로 지정하였다.)

Subject Entity

package hellojpa;

import lombok.Data;

import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;

@Data
@Entity
public class Subject {
    @Id @GeneratedValue
    private Long id;

    private String name;

    @ManyToMany(mappedBy = "subjects")
    private List<Student> students = new ArrayList<>();
}

여기에도 students@ManyToMany 어노테이션이 달린 것을 볼 수 있다.

대신 Subject Entity의 students 필드는 현재 관계에서 주가 아닌 종이기때문에 mappedBy 옵션이 달려있는 것을 볼 수 있다.

 

 

아직 양방향 연관관계에서의 주와 종이 뭔지 모른다면 이 글을 참고하자!!!

 

N:1 양방향 연관관계 매핑하기

길지 않으니 이 글을 먼저 읽어보길 권장한다!!! code-mania.tistory.com/35 DB와 객체의 양방향 연관관계 차이 알아보기 이번 시간에는 양방향 연관관계에 대해 알아보겠다!!!! (좀 빡센 거 같다...) 단방

code-mania.tistory.com

JpaMain

package hellojpa;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;

public class JpaMain {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");

        EntityManager em = emf.createEntityManager();
        EntityTransaction tx = em.getTransaction();

        tx.begin();

        try {
            // 수학 과목 등록
            Subject math = new Subject();
            math.setName("수학");
            em.persist(math);

            // 한국사 과목 등록
            Subject history = new Subject();
            history.setName("한국사");
            em.persist(history);

            // code-mania 학생 등록
            Student codeMania = new Student();
            codeMania.setName("code-mania");
            codeMania.setAge(21);
            codeMania.getSubjects().add(math); // 수학 과목 수강
            codeMania.getSubjects().add(history); // 역사 과목 수강
            em.persist(codeMania);

            // code-lover 학생 등록
            Student codeLover = new Student();
            codeLover.setName("code-lover");
            codeLover.setAge(21);
            codeLover.getSubjects().add(history); // 역사 과목 수강
            em.persist(codeLover);
            tx.commit();
        } catch (Exception e) {
            tx.rollback();
            e.printStackTrace();
        } finally {
            em.close();
        }

        em.close();
        emf.close();
    }
}

 

JpaMain을 실행시키고, h2에서 결과를 확인해보면 매핑테이블인 StudentSubject Table에 데이터가 잘 들어간 것을 볼 수 있다.

 

 

Student Entity에서 Subject Entity를 List로 관리하므로 한 학생이 여러 과목을 수강할 수 있다.

다른 말로 Student:Subject=N:M 관계가 성립하게 된 것이다.

 

그런데 지금의 매핑테이블 형태는 우리가 처음에 살펴봤던 매핑테이블 형태와 다르다.

현재 매핑테이블은 @JoinTable에 의해 JPA가 자동으로 생성했고, 그 과정에서 최소한의 컬럼만이 정의됐다.

하지만 우리가 설계했던 Join Table에 PK를 위한 컬럼이 있었다.

또한 나중에 필요에 의해서 수강시작날짜 체크를 위한 컬럼을 추가해서 관리하게 될 수도 있는데,

최소한의 컬럼만을 만들어주는 @JoinTable 어노테이션으로 위와 같은 경우들을 해결하는 것은 불가능하다.

이러한 이유로 어떤 상황이 있을지 모르는 실무에서 @JoinTable은 봉인하는 게 좋다.

매핑테이블을 Entity로 따로 만들어 사용하는 것이 바람직한 방법이고, 다음 글에서는 이에 대해 알아보겠다!

 

git: git 주소(ManyToMany branch)

참고강의: 배달의 민족 개발팀장 김영한 강사님의 JPA 강의