2025-04-08T19:56:24
This commit is contained in:
236
docs/jpa/03_관계 매핑.md
Normal file
236
docs/jpa/03_관계 매핑.md
Normal file
@@ -0,0 +1,236 @@
|
||||
아래는 "관계 매핑"에 대해 예시와 함께 설명하는 글입니다. 롬복을 사용해 코드를 간결하게 작성했습니다.
|
||||
|
||||
---
|
||||
|
||||
### 관계 매핑
|
||||
|
||||
JPA에서 관계 매핑(Relationship Mapping)은 엔티티 간의 연관성을 정의하는 방법으로, 객체지향 프로그래밍과 관계형 데이터베이스의 다리를 잇는 핵심 기능입니다. 데이터베이스에서는 외래 키(Foreign Key)를 통해 테이블 간 관계를 표현하지만, JPA에서는 어노테이션(`@OneToMany`, `@ManyToOne` 등)을 사용해 객체 간 관계를 자연스럽게 매핑합니다. 이를 통해 개발자는 SQL 조인을 직접 작성하지 않고도 연관된 데이터를 쉽게 조회하고 관리할 수 있습니다.
|
||||
|
||||
#### 관계 매핑의 주요 유형
|
||||
JPA는 네 가지 주요 관계 매핑을 지원합니다:
|
||||
1. **1:1 (One-to-One)**: 한 엔티티가 다른 엔티티와 단일 관계를 가짐.
|
||||
2. **1:N (One-to-Many)**: 한 엔티티가 여러 엔티티와 관계를 가짐.
|
||||
3. **N:1 (Many-to-One)**: 여러 엔티티가 한 엔티티와 관계를 가짐.
|
||||
4. **N:M (Many-to-Many)**: 여러 엔티티가 서로 여러 엔티티와 관계를 가짐.
|
||||
|
||||
#### 주요 어노테이션
|
||||
- **`@OneToMany`**: 1:N 관계를 정의.
|
||||
- **`@ManyToOne`**: N:1 관계를 정의.
|
||||
- **`@OneToOne`**: 1:1 관계를 정의.
|
||||
- **`@ManyToMany`**: N:M 관계를 정의.
|
||||
- **`@JoinColumn`**: 외래 키 컬럼을 지정.
|
||||
|
||||
#### 예시 1: 1:N과 N:1 관계 (팀과 회원)
|
||||
아래는 `Team`과 `Member` 엔티티 간의 1:N 및 N:1 관계를 보여주는 예시입니다.
|
||||
|
||||
```java
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.OneToMany;
|
||||
import jakarta.persistence.Column;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class Team {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Column(name = "team_id")
|
||||
private Long id;
|
||||
|
||||
@Column(name = "team_name")
|
||||
private String name;
|
||||
|
||||
@OneToMany(mappedBy = "team") // 1:N 관계, Member의 team 필드에 의해 매핑
|
||||
private List<Member> members = new ArrayList<>();
|
||||
|
||||
public Team(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```java
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.Column;
|
||||
|
||||
@Entity
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class Member {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Column(name = "member_id")
|
||||
private Long id;
|
||||
|
||||
@Column(name = "username")
|
||||
private String name;
|
||||
|
||||
@ManyToOne // N:1 관계
|
||||
@JoinColumn(name = "team_id") // 외래 키 컬럼 지정
|
||||
private Team team;
|
||||
|
||||
public Member(String name, Team team) {
|
||||
this.name = name;
|
||||
this.team = team;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 코드 설명
|
||||
1. **`Team` (1:N)**
|
||||
- `@OneToMany(mappedBy = "team")`: `Team`은 여러 `Member`를 가질 수 있으며, 관계의 주인은 `Member`의 `team` 필드입니다. `mappedBy`는 양방향 매핑에서 사용되며, 외래 키를 직접 관리하지 않음을 나타냅니다.
|
||||
|
||||
2. **`Member` (N:1)**
|
||||
- `@ManyToOne`: `Member`는 하나의 `Team`에 속합니다.
|
||||
- `@JoinColumn(name = "team_id")`: `Member` 테이블에 `team_id`라는 외래 키 컬럼이 생성됩니다.
|
||||
|
||||
#### 매핑된 테이블 구조
|
||||
- `Team` 테이블:
|
||||
```sql
|
||||
CREATE TABLE team (
|
||||
team_id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||
team_name VARCHAR(255)
|
||||
);
|
||||
```
|
||||
- `Member` 테이블:
|
||||
```sql
|
||||
CREATE TABLE member (
|
||||
member_id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||
username VARCHAR(255),
|
||||
team_id BIGINT,
|
||||
FOREIGN KEY (team_id) REFERENCES team(team_id)
|
||||
);
|
||||
```
|
||||
|
||||
#### 동작 방식
|
||||
- `Team team = new Team("개발팀");`
|
||||
- `Member member = new Member("홍길동", team);`
|
||||
- `team.getMembers().add(member);`
|
||||
- 저장 시 `Member` 테이블에 `team_id`가 `Team`의 `team_id`를 참조하는 레코드가 삽입됩니다.
|
||||
|
||||
#### 예시 2: N:M 관계 (학생과 강의)
|
||||
N:M 관계는 중간 테이블을 통해 구현됩니다.
|
||||
|
||||
```java
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.ManyToMany;
|
||||
import jakarta.persistence.Column;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class Student {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Column(name = "student_id")
|
||||
private Long id;
|
||||
|
||||
@Column(name = "student_name")
|
||||
private String name;
|
||||
|
||||
@ManyToMany
|
||||
private List<Course> courses = new ArrayList<>();
|
||||
|
||||
public Student(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```java
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.ManyToMany;
|
||||
import jakarta.persistence.Column;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class Course {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Column(name = "course_id")
|
||||
private Long id;
|
||||
|
||||
@Column(name = "course_name")
|
||||
private String name;
|
||||
|
||||
@ManyToMany(mappedBy = "courses")
|
||||
private List<Student> students = new ArrayList<>();
|
||||
|
||||
public Course(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 코드 설명
|
||||
- `@ManyToMany`: `Student`와 `Course`는 다대다 관계입니다.
|
||||
- JPA는 자동으로 중간 테이블(예: `student_course`)을 생성해 외래 키를 관리합니다.
|
||||
|
||||
#### 매핑된 테이블 구조
|
||||
- `student_course` (중간 테이블):
|
||||
```sql
|
||||
CREATE TABLE student_course (
|
||||
student_id BIGINT,
|
||||
course_id BIGINT,
|
||||
FOREIGN KEY (student_id) REFERENCES student(student_id),
|
||||
FOREIGN KEY (course_id) REFERENCES course(course_id)
|
||||
);
|
||||
```
|
||||
|
||||
#### 주의사항
|
||||
- **양방향 매핑**: `mappedBy`를 사용해 관계의 주인을 명확히 지정해야 중복 매핑을 방지합니다.
|
||||
- **지연 로딩(Lazy Loading)**: `@OneToMany`와 `@ManyToMany`는 기본적으로 지연 로딩이 적용되며, 필요 시 `fetch = FetchType.EAGER`로 즉시 로딩을 설정할 수 있습니다.
|
||||
- **N:M의 한계**: 실무에서는 N:M 대신 중간 엔티티를 만들어 1:N, N:1로 분리하는 경우가 많습니다.
|
||||
|
||||
---
|
||||
|
||||
관계 매핑은 JPA의 강력한 기능으로, 객체 간 관계를 데이터베이스에 자연스럽게 반영합니다. 다음 장에서는 성능 최적화를 위한 페치 전략과 N+1 문제를 다뤄보겠습니다.
|
||||
|
||||
---
|
||||
|
||||
책의 흐름에 맞는지, 추가 예시나 설명이 필요하면 말씀해 주세요!
|
||||
Reference in New Issue
Block a user