객체는 Collection을 사용하여 N:M 설정이 가능하지만 관계형 DB는 N:M 연관관계를 구현할 수 없다.
따라서 중간 테이블을 추가해서 1:n, n:1 관계로 설정하면 된다.
JPA를 사용하면 @ManyToMany로 위 과정을 쉽게 해결가능한데 문제가 있으니 주의하여야한다.
단방향 예시
@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;
@ManyToOne
@JoinColumn(insertable = false, updatable = false)
private Company company;
@OneToOne
@JoinColumn(name = "address_id", unique = true)
private Address address;
@ManyToMany
@JoinTable(
name = "tutor_language",
joinColumns = @JoinColumn(name = "tutor_id"),
inverseJoinColumns = @JoinColumn(name = "language_id")
)
private List<Language> languages = new ArrayList<>();
public Tutor() {
}
public Tutor(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
}

양방향 예시
@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 매핑의 문제점
- 중간테이블에 id값만 존재하는게 아니라 추가적인 데이터가 필요할 경우가 많다.
- @ManyToMany 에서는 사용할 수 없다.
- 중간 테이블이 숨겨져 있어서 생각하지 못한 SQL Query가 실행된다.
- tutor_id, language_id 를 묶어서 PK로 설정된다.
- PK가 종속적이면 사이드 이펙트가 생긴다.
- PK 값은 비지니스적으로 의미없는 Long 값으로 설정하는 것이 유연성에 좋다.
위와 같은 문제를 해결하기 위해 중간테이블을 실제 Entity로 만들어서 관리하면 편리하다.
코드예시
@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;
@ManyToOne
@JoinColumn(insertable = false, updatable = false)
private Company company;
@OneToOne
@JoinColumn(name = "address_id", unique = true)
private Address address;
@OneToMany(mappedBy = "tutor")
private List<TutorLanguage> tutorLanguages = new ArrayList<>();
public Tutor() {
}
public Tutor(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
}
데이터베이스 설계 관점에서는 @ManyToMany 방식이 맞지만 실제 코드 구현에는 중간 테이블을 만드는 방법이 편리하다
'spring' 카테고리의 다른 글
| 페이징처리할때 수동으로 처리하면서 공부한 내용 (0) | 2025.03.05 |
|---|---|
| JPA 사용시 변경사항이 있을때 save() 가 아닌 @Transactional 사용해도 저장됨 (0) | 2025.02.26 |
| @Mock (0) | 2025.02.24 |
| 뉴스피드 트러블슈팅 (0) | 2025.02.16 |
| HttpServletResponse 메서드 (0) | 2025.02.12 |