엔티티의 연관관계를 매핑할 때, 다중성, 단방향/양방향, 그리고 연관관계의 주인을 고려해야 합니다.
이를 바탕으로 모든 연관관계 유형을 정리하겠습니다.
01 다대일 (Many-to-One)
1. 다대일 단방향 [N:1]
다수의 엔티티가 하나의 엔티티와 연관되는 관계입니다.
단방향으로 설정하면 하나의 엔티티에서만 참조할 수 있습니다.
여러 회원이 하나의 팀에 소속된 상황을 예로 들 수 있습니다.
@Entity
public class Member {
@ManyToOne
@JoinColumn(name = "team_id") // 외래 키 컬럼
private Team team;
}
@Entity
public class Team {
@Id
private Long id;
private String name;
}
2. 다대일 양방향 [N:1, 1:N]
다수의 엔티티가 하나의 엔티티와 연관되며, 양쪽에서 서로 참조할 수 있습니다.
위의 예시를 양방향으로 바꾸면 아래와 같습니다.
@Entity
public class Member {
@ManyToOne
@JoinColumn(name = "team_id") // 외래 키 컬럼
private Team team;
}
@Entity
public class Team {
@Id
private Long id;
private String name;
@OneToMany(mappedBy = "team") // 연관관계의 주인은 Member
private List<Member> members = new ArrayList<>();
}
02 일대다 (One-to-Many)
1. 일대다 단방향 [1:N]
한 엔티티가 다수의 엔티티와 연관되며, 단방향으로 설정됩니다.
다대일과 반대로 한 팀에 여러 회원이 소속되는 상황을 예로 들 수 있습니다.
@Entity
public class Team {
@Id
private Long id;
private String name;
@OneToMany
@JoinColumn(name = "team_id") // 외래 키를 Team 테이블이 관리
private List<Member> members = new ArrayList<>();
}
@Entity
public class Member {
@Id
private Long id;
private String name;
}
그러나 일대다 단방향은 매핑한 객체가 관리하는 외래 키가 다른 테이블에 있다는 단점이 있습니다.
이에 실무에서 잘 사용되지 않고, 다대일 양방향으로 대체하는 것이 일반적입니다.
2. 일대다 양방향 [1:N]
한 엔티티가 다수의 엔티티와 연관되며 양쪽에서 서로 참조가 가능한 관계입니다.
한 팀에 여러 회원이 있고 회원도 팀을 참조할 수 있습니다.
@Entity
public class Team {
@Id
private Long id;
private String name;
@OneToMany(mappedBy = "team") // 연관관계의 주인은 Member
@JoinColumn(name = "team_id")
private List<Member> members = new ArrayList<>();
}
@Entity
public class Member {
@Id
private Long id;
private String name;
@ManyToOne
@JoinColumn(name = "team_id", insertable = false, updatable = false) // 외래 키 컬럼
private Team team;
}
이 방법 역시 읽기 전용으로 구현은 가능하나 일대다 단방향과 같은 단점이 있어 지양합니다.
03 일대일(One-to-One)
일대일 관계는 주 테이블, 대상 테이블 둘 중 어느 곳에나 외래 키를 가질 수 있습니다.
주 테이블에 외래 키를 둘 경우에는 객체 참조와 비슷하게 사용할 수 있습니다.
대상 테이블에 외래 키를 둘 경우에는 테이블 관계를 일대일에서 일대다로 변경할 때 테이블 구조를 그대로 유지할 수 있다는 점입니다.
1. 일대일 단방향 [1:1]
두 엔티티가 서로 1:1로 매핑되며, 단방향으로 설정합니다.
예를 들어 한 사용자가 하나의 프로필을 가지는 상황이 이에 해당합니다.
단, 여기서 프로필은 사용자를 알지 못합니다.
@Entity
public class User {
@Id
private Long id;
private String name;
@OneToOne
@JoinColumn(name = "profile_id") // 외래 키 컬럼
private Profile profile;
}
@Entity
public class Profile {
@Id
private Long id;
private String bio;
}
2. 일대일 양방향 [1:1]
두 엔티티가 서로 1:1로 매핑되며 양쪽에서 서로 참조합니다.
@Entity
public class User {
@Id
private Long id;
private String name;
@OneToOne
@JoinColumn(name = "profile_id") // 외래 키 컬럼
private Profile profile;
}
@Entity
public class Profile {
@Id
private Long id;
private String bio;
@OneToOne(mappedBy = "profile") // 연관관계의 주인은 User
private User user;
}
04 다대다(Many-to-Many)
관계형 데이터베이스는 테이블 2개로 다대다 관계를 표현할 수 없어 연결테이블을 사용합니다.
그러나 객체는 테이블과 달리 객체 2개로 다대다 관계를 매핑할 수 있습니다.
1. 다대다 단방향
두 엔티티가 다대다로 매핑되며 단방향으로 설정하는 경우입니다.
여러 학생이 여러 수업을 듣는 상황을 생각할 수 있습니다.
@Entity
public class Student {
@Id
private Long id;
private String name;
@ManyToMany
@JoinTable(
name = "student_course", // 중간 테이블
joinColumns = @JoinColumn(name = "student_id"),
inverseJoinColumns = @JoinColumn(name = "course_id")
)
private List<Course> courses = new ArrayList<>();
}
@Entity
public class Course {
@Id
private Long id;
private String title;
}
2. 다대다 양방향
위의 코드에서 양쪽에서 서로 참조할 수 있게 바꾸었습니다.
@Entity
public class Student {
@Id
private Long id;
private String name;
@ManyToMany(mappedBy = "students") // 연관관계의 주인은 Course
private List<Course> courses = new ArrayList<>();
}
@Entity
public class Course {
@Id
private Long id;
private String title;
@ManyToMany
@JoinTable(
name = "student_course", // 중간 테이블
joinColumns = @JoinColumn(name = "course_id"),
inverseJoinColumns = @JoinColumn(name = "student_id")
)
private List<Student> students = new ArrayList<>();
}
위와 같이 ManyToMany를 사용하면 연결테이블을 자동으로 처리해 편리하지만,
실무에서 이렇게 사용하기에는 한계가 있습니다.
컬럼을 추가할 경우에는 더이상 매핑이 불가해지기 때문입니다.
이러한 문제를 해결하기 위해 기본 키와 외래 키를 한번에 복합 기본 키로 매핑하는 전략을 택할 수 있습니다.
하지만 이 방법은 매핑에서 처리해야 할 일들이 늘어나 새로운 기본 키를 사용하는 방법으로 대체할 수도 있습니다.
즉, 다대다 관계를 일대다 다대일 관계로 만들기 위해
연결 테이블을 만들 때 식별자를 어떻게 구성할지 정해야 합니다.
- 식별 관계 : 식별자를 기본 키 + 외래 키로 사용
- 비식별 관계 : 식별자를 외래 키로만 사용하고 새로운 식별자 추가
비식별 관계를 사용할 경우 복합 키를 위한 식별자 클래스를 만들지 않아도 되어 단순하고 편리하게 매핑할 수 있습니다.
'BackEnd > JPA' 카테고리의 다른 글
[JPA] JPA 엔티티 기본 매핑 어노테이션 완벽 정리 (1) | 2024.12.20 |
---|---|
[JPA]JPA와 영속성 컨텍스트 (1) | 2024.12.17 |
[JPA] SpringBoot + JPA 시작하기 (1) | 2024.12.13 |
[JPA] JPA를 배워야 하는 이유는? SQL Mapper vs ORM (2) | 2024.12.10 |