2025-04-08T19:56:24

This commit is contained in:
2025-04-08 19:56:24 +09:00
parent a75a1dbd0f
commit eef061c1c9
100 changed files with 18639 additions and 0 deletions

View 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 문제를 다뤄보겠습니다.
---
책의 흐름에 맞는지, 추가 예시나 설명이 필요하면 말씀해 주세요!