N:M 연관관계
두 Entity가 @ManyToMany를 통해 서로 다수의 관계를 가진다.
객체와 데이터베이스의 N:M 연관관계
객체
객체는 Collection을 사용하여 N:M 설정이 가능하다
데이터베이스
관계형 데이터베이스는 N:M 연관관계를 구현할 수 없다.
그래서 중간 테이블을 추가해서 1:N, N:1 관계로 만들어서 N:M 관계를 구현한다.
N:M 단방향, 양방향
코드 예시(단방향)
@Entity
@Table(name = "language")
public class Language {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
}
@Entity
@Table(name = "tutor")
public class Tutor {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToMany
@JoinTable(
name = "tutor_language",
joinColumns = @JoinColumn(name = "tutor_id"),
inverseJoinColumns = @JoinColumn(name = "language_id")
)
private List<Language> languages = new ArrayList<>();
public Tutor() {
}
}
실행 결과
tutor_language 란 중간 테이블이 생성됐다.
코드 예시(양방향)
@Entity
@Table(name = "language")
public class Language {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToMany(mappedBy = "languages")
private List<Tutor> tutors = new ArrayList<>();
}
N:M 매핑의 문제점
N:M 매핑의 문제점
실제 설계에서는 level, license와 같은 추가적인 데이터가 필요하다.
- @ManyToMany에서 사용할 수 없다.
중간 테이블이 숨겨져 있어서 생각하지 못한 SQL Query가 실행된다.
tutor_id, language_id 를 묶어서 PK로 실행한다.
- PK가 종속적이면 사이드 이펙트가 생긴다.
- 테이블에 수정이 생겼을 때 또는 확장이 필요할 때 키 값 때문에 확장을 못하는 경우가 생긴다.
- PK같은 비즈니스적으로 의미없는 Long값으로 설정하는 것이 유연성에 좋다.
문제점 해결
중간 테이블을 실제 Entity로 만들어서 관리하면 된다.
@ManyToMany에서 @OneToMany와 @ManyToOne으로 분리하면 된다.
코드 예시
@Entity
@Table(name = "tutor_language")
public class TutorLanguage {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "tutor_id")
private Tutor tutor;
@ManyToOne
@JoinColumn(name = "language_id")
private Language language;
private Integer level;
private String license;
}
@Entity
@Table(name = "language")
public class Language {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy = "language")
private List<TutorLanguage> tutorLanguages = new ArrayList<>();
}
@Entity
@Table(name = "tutor")
public class Tutor {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy = "tutor")
private List<TutorLanguage> tutorLanguages = new ArrayList<>();
public Tutor() {
}
}
실행 결과
데이터베이스 설계 관점에서는 @ManyToMany 방식으로 중간 테이블에 외래키 두 개를 기본키로 사용하고 필드를 추가할 수 없게 끔하는게 맞지만 실제 코드 구현에는 중간 테이블을 만드는 방법이 편리하다.
'JPA' 카테고리의 다른 글
Proxy (0) | 2024.11.28 |
---|---|
상속 관계 매핑 (0) | 2024.11.28 |
1:1 연관관계 (1) | 2024.11.27 |
1:N 연관관계에서 1이 연관관계 주인일 때 (0) | 2024.11.27 |