💡 본 게시글은 김영한님의 인프런(Inflearn) 강의 자바 ORM 표준 JPA 프로그래밍 - 기본편에 대해 공부하고, 정리한 내용입니다.
1. 연관관계 매핑 시 고려사항
JPA를 사용하여 객체와 테이블 간의 연관관계를 매핑할 때는 몇 가지 중요한 사항을 고려해야 합니다. 이러한 고려사항은 올바른 설계와 최적의 성능을 보장합니다.
1) 연관관계 매핑 시 고려해야 할 3가지
- 다중성: 연관관계의 수(다대일, 일대다, 일대일, 다대다)를 의미합니다.
- 단방향, 양방향: 객체 관계의 방향성을 정의합니다.
- 연관관계의 주인: 외래 키(Foreign Key)를 관리하는 주체를 결정합니다.
(1) 다중성
다중성은 연관된 엔티티 간의 관계가 몇 대 몇인지를 정의합니다. JPA에서 지원하는 다중성 매핑 어노테이션은 다음과 같습니다:
- 다대일(N:1):
@ManyToOne - 일대다(1:N):
@OneToMany - 일대일(1:1):
@OneToOne - 다대다(N:M):
@ManyToMany - Note: 대칭적 관점에서 바라보면 이해가 쉬워집니다. 예를 들어, 일대일(1:1)의 반대는 일대일(1:1), 다대다(N:M)의 반대도 다대다(N:M)입니다. 다대일(N:1)의 반대는 일대다(1:N), 일대다(1:N)의 반대는 다대일(N:1)입니다.
(2) 단방향, 양방향
테이블 연관관계
- 테이블: 외래 키 하나로 양쪽 조인이 가능합니다. 즉, 방향이라는 개념이 없습니다.
객체 연관관계
- 객체: 참조용 필드가 있는 쪽에서만 참조가 가능합니다.
- 한쪽만 참조하면 단방향.
- 양쪽이 서로 참조하면 양방향입니다. (사실상 단방향이 2개 있는 것과 같음)
(3) 연관관계의 주인
- 테이블: 외래 키 하나로 두 테이블 간의 연관관계를 설정할 수 있습니다.
- 객체: 양방향 관계에서는 A -> B, B -> A 처럼 서로 참조하는 두 가지 관계가 존재합니다. 이 중 한 곳에서 외래 키를 관리해야 합니다.
- 연관관계의 주인: 외래 키를 관리하는 주체입니다. 주인의 반대편은 외래 키에 영향을 주지 않고, 단순 조회만 가능합니다.
2) 다중성에 따른 매핑 전략
(1) 다대일 (N:1)
가장 일반적으로 사용하는 연관관계입니다.
다대일 단방향 매핑
- 설계:
Member는Team을 참조하고,Team은Member를 알지 못하는 구조입니다.
Member 엔티티 코드:
@Entity
public class Member {
@Id
@GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
@Column(name = "USERNAME")
private String username;
@ManyToOne // 다대일 관계 설정
@JoinColumn(name = "TEAM_ID") // 외래 키 매핑
private Team team;
}- 다대일 단방향 매핑 정리:
- 가장 많이 사용하는 연관관계입니다.
- 다대일의 반대는 일대다입니다.
다대일 양방향 매핑
- 외래 키가 있는 쪽이 연관관계의 주인입니다. 양쪽이 서로 참조하도록 개발합니다.
Team 엔티티 코드:
@Entity
public class Team {
@Id
@GeneratedValue
@Column(name = "TEAM_ID")
private Long id;
private String name;
@OneToMany(mappedBy = "team") // 역방향 매핑
private List<Member> members = new ArrayList<>();
}(2) 일대다 (1:N)
JPA 표준 스펙에서 지원은 하지만 실무에서는 권장되지 않는 모델입니다.
일대다 단방향 매핑
- 설계:
Team은Member들을 알고 있지만,Member는Team을 모르는 구조입니다.
Team 엔티티 코드:
@Entity
public class Team {
@Id
@GeneratedValue
@Column(name = "TEAM_ID")
private Long id;
private String name;
@OneToMany
@JoinColumn(name = "TEAM_ID") // FK 매핑
private List<Member> members = new ArrayList<>();
}Member 엔티티 코드:
@Entity
public class Member {
@Id
@GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
@Column(name = "USERNAME")
private String username;
}- 문제점:
Team의List<Member>값을 바꾸면Member테이블에 추가적인UPDATE쿼리가 발생합니다.
일대다 양방향 매핑
Member에서Team으로 조회하고자 하면 약간의 변칙적인 방법을 사용합니다.
Member 엔티티 코드:
@Entity
public class Member {
@Id
@GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
@Column(name = "USERNAME")
private String username;
@ManyToOne
@JoinColumn(insertable = false, updatable = false) // 읽기 전용
private Team team;
}(3) 일대일 (1:1)
일대일: 주 테이블에 외래 키 (단방향)
- 설계:
Member엔티티가 외래 키를 가지는 구조입니다.
Member 엔티티 코드:
@Entity
public class Member {
@Id
@GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
@Column(name = "USERNAME")
private String username;
@OneToOne
@JoinColumn(name = "LOCKER_ID") // 외래 키 매핑
private Locker locker;
}일대일: 대상 테이블에 외래 키 (단방향)
- 반대쪽 사이드에 외래 키를 두는 경우를 말합니다. 단방향 관계는 JPA에서 지원하지 않습니다.
일대일 정리
- 주 테이블에 외래 키: 객체 지향 개발자 선호, 조회 시 장점
- 대상 테이블에 외래 키: 전통적인 데이터베이스 개발자 선호, 테이블 구조 변경 시 유리
(4) 다대다 (N:M)
다대다는 실무에서 사용하지 않는 것이 좋습니다.
- 제한 사항: 관계형 데이터베이스는 정규화된 테이블 2개로 다대다 관계를 표현할 수 없습니다.
- 해결책: 연결(중간) 테이블을 추가하여 일대다, 다대일 관계로 풀어내야 합니다.
Member 엔티티 코드:
@Entity
public class Member {
@Id
@GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
@ManyToMany
@JoinTable(name = "MEMBER_PRODUCT") // 연결 테이블 매핑
private List<Product> products = new ArrayList<>();
}Product 엔티티 코드:
@Entity
public class Product {
@Id
@GeneratedValue
private Long id;
private String name;
}- 다대다 매핑의 한계:
- 매핑 정보 외에 추가적인 데이터를 넣을 수 없습니다.
- 중간 테이블이 숨겨져 있어 예측하기 어려운 쿼리가 발생할 수 있습니다.
- 해결책: 연결 테이블용 엔티티를 추가하여
@ManyToMany를@OneToMany,@ManyToOne으로 변환합니다.
MemberProduct 엔티티 코드:
@Entity
public class MemberProduct {
@Id
@GeneratedValue
private Long id;
@ManyToOne
@JoinColumn(name = "MEMBER_ID")
private Member member;
@ManyToOne
@JoinColumn(name = "PRODUCT_ID")
private Product product;
private int count;
private int price;
private LocalDateTime orderDatetime;
}3) **연관관계의 주인을 정하는 기준**
- 비즈니스 로직을 기준으로 연관관계의 주인을 선택하면 안 됩니다.
- 연관관계의 주인은 실제 테이블의 외래 키 위치를 기준으로 결정해야 합니다.
4) 총정리
- 연관관계 매핑의 핵심은 다중성, 단방향/양방향, 연관관계의 주인을 이해하고 설계하는 것입니다.
- 단방향 매핑으로 시작하고, 필요할 때 양방향 매핑을 추가합니다.
- 연관관계의 주인은 외래 키가 있는 곳으로 설정하며, 비즈니스 로직과 상관없이 테이블 구조를 기준으로 결정합니다.
- 다대다 매핑은 실무에서 사용하지 않으며, 연결 테이블을 엔티티로 승격하여 풀어내야 합니다.
- 무한 루프와 같은 문제를 방지하기 위해 엔티티를 직접 반환하지 말고, DTO로 변환하여 반환합니다.
이러한 원칙들을 적용하면 JPA를 통한 안정적이고 확장 가능한 애플리케이션을 개발할 수 있습니다.
'Spring > 자바 ORM 표준 JPA 프로그래밍' 카테고리의 다른 글
| 05. 연관관계 매핑 기초 (0) | 2024.09.08 |
|---|---|
| 04. 엔티티 매핑 (1) | 2024.09.08 |
| 03. 영속성 관리 - 내부 동작 방식 (0) | 2024.08.11 |
| 02. JPA 시작하기 (0) | 2024.08.11 |
| 01. JPA 소개 (0) | 2024.07.24 |