개발을 하다 보면 PK, FK, 영속성 전이, ManyToOne, OneToMany 등등 이런 단어와 어노테이션들을 자주 사용하게 되는데, 정확한 개념을 모르는 경우가 있다.
앞으로 많이 사용하게 될테니 알아두자!
먼저 연관관계 매핑에 대해 알아보도록 하자.
연관관계 매핑이란?
연관관계 매핑이란, 데이터베이스에서 테이블 간의 관계를 정의하는 것이다.
즉, 객체(Entity) 간의 관계를 설정해 데이터를 효율적으로 저장하고 조회할 수 있도록 하는 과정이다.
연관관계 매핑을 설명하기 전 기본 개념인 PK와 FK에 대해 설명하도록 하겠다.
PK (Primary Key, 기본 키)
PK(기본 키)란 테이블에서 각 행(Row)을 유일하게 식별할 수 있는 값이다.
즉, 각 행을 구별하는 "주인공"이나 "대표주자" 역할을 한다고 생각하면 된다.
import jakarta.persistence.*;
@Entity
public class User {
@Id // PK 설정
@GeneratedValue(strategy = GenerationType.IDENTITY) // 자동 증가 (Auto Increment)
private Long id;
private String name;
private String email;
}
FK (Foreign Key, 외래 키)
FK(외래 키)란 다른 테이블의 PK를 참조하는 키이다.
즉, 두 테이블을 연결해주는 "다리" 역할을 한다고 보면 된다.
import jakarta.persistence.*;
@Entity
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne // 다대일 관계 설정
@JoinColumn(name = "user_id") // FK 컬럼명 지정
private User user;
private String product;
}
@ManyToOne이라는 어노테이션이 바로 연관관계 매핑 중 하나이다.
연관관계 매핑의 종류
1:1 (일대일)
- 하나의 엔티티가 다른 엔티티와 1:1로 연결하는 것이다
- 예) 사용자(User) - 프로필(Profile)
1:N (일대다)
- 하나의 엔티티가 여러 엔티티와 연결되는 것이다.
- 예) 사용자(User) - 주문(Order)
N:M (다대다)
- 여러 엔티티가 여러 엔티티와 연결되는 것이다.
- 예) 주문(Order) - 장바구니(Cart)
@Entity
public class User {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToOne // 일대일 관계 설정
@JoinColumn(name = "profile_id") // FK 설정
private Profile profile;
}
@Entity
public class Profile {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String bio;
}
@Entity
public class User {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy = "user") // 일대다 관계 설정
private List<Order> orders = new ArrayList<>();
}
@Entity
public class Order {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String product;
@ManyToOne
@JoinColumn(name = "user_id") // FK 설정
private User user;
}
여기서 FK는 @ManyToOne인 객체가 갖는 것이다.
mappedBy는 Order 테이블의 user 필드와 매핑한다는 뜻이다.
다대다 관계에서는 중간 테이블을 생성하여 관리한다.
Student_Course라는 중간테이블에 id (PK), student_id(FK), course_id(FK) 이 세 멤버가 추가된다.
@Entity
public class Student {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
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 @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
@ManyToMany(mappedBy = "courses")
private List<Student> students = new ArrayList<>();
}
그러나 실무에서는 ManyToMany를 많이 사용하지 않는다.
추가적인 데이터 저장 불가능, 성능 문제, 데이터 변경 어려움 등의 이유라는데 일단 알아두기!
Cascade (영속성 전이)
부모 엔티티의 변경이 자식 엔티티에도 영향을 미치도록 하는 설정
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL) private List<Order> orders = new ArrayList<>();
FetchType (지연 로딩 vs 즉시 로딩)
연관된 엔티티를 조회할 때, 데이터를 즉시 가져올지(LAZY) 또는 필요할 때 가져올지(EAGER) 설정
@ManyToOne(fetch = FetchType.LAZY) private User user;
@OneToOne, @ManyToOne → 기본값 EAGER
@OneToMany, @ManyToMany → 기본값 LAZY