아래는 "관계 매핑"에 대해 예시와 함께 설명하는 글입니다. 롬복을 사용해 코드를 간결하게 작성했습니다. --- ### 관계 매핑 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 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 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 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 문제를 다뤄보겠습니다. --- 책의 흐름에 맞는지, 추가 예시나 설명이 필요하면 말씀해 주세요!