1️⃣ 단방향 연관관계
단방향 연관관계는 한 엔티티가 다른 엔티티를 참조하지만, 그 반대는 성립하지 않는 관계를 의미합니다.
즉, 한 쪽 방향으로만 연관관계가 설정되어 있어,
참조하는 엔티티는 다른 엔티티를 인식할 수 있지만, 참조 받는 엔티티는 이를 알 수 없습니다.
관계가 한 방향으로만 정의되므로, 구현과 관리가 상대적으로 간단합니다.
연관관계의 소유자(주로 참조하는 엔티티)만이 외래키(FK)를 관리합니다.
데이터베이스에서는 한쪽 테이블에만 외래키 컬럼이 존재하게 되어 연관관계의 단순한 처리와 관리가 가능합니다.
🔍 예시 ) 단방향 @OneToOne
Person(사람) 엔티티가 Passport(여권) 엔티티를 참조하는 관계로,
Person에서만 Passport를 알고 있으며, Passport에서는 Person에 대한 참조가 없습니다.
import javax.persistence.*;
@Entity
public class Person{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// 단방향 OneToOne 매핑 : Person이 Passport를 참조
@OneToOne
@JoinColumn(name = "passport_id")
private Passport passport;
// 기타 필드 및 getter, setter 메소드
...
}
@Entity
public class Passport{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String passportNumber;
// Customer 엔티티는 Order를 직접 참조하지 않음
// getter, setter 메서드
...
}
단방향 매핑에서는 참조하는 쪽에서만 외래 키를 관리하며,
상대방은 자신의 입장에서 해당 관계를 알지 못합니다.
이를 통해 관계 설정과 관리가 단순해지지만, 필요에 따라 양방향 매핑을 고려할 수도 있습니다.
2️⃣ 양방향 연관관계
양방향 연관관계는 두 엔티티가 서로 참조할 수 있도록 매핑하는 방식입니다.
즉, 한 엔티티는 다른 엔티티에 대한 정보를 가지고 있고,
반대쪽 엔티티 역시 다른 쪽 엔티티를 참조함으로써 양쪽 모두 관계를 인지할 수 있습니다.
양쪽에서 접근이 가능하며, 주인(Owner)과 비주인(Inverse)측이 존재한다.
양방향 연관관계에서는 한쪽을 연관관계의 주인으로 지정해야 합니다.
주인측은 실제 데이터베이스의 외래키 (FK)를 관리하며,
해당 필드에 `@JoinColumn`이 지정되어 있어, FK 매핑을 명시합니다.
반대쪽(비주인측)은 `mappedBy`속성을 사용하여 주인 측의 연관관계를 참조합니다.
두 객체 간의 관계를 양쪽에서 일관성 있게 관리해야 합니다.
양방향 연관관계에서는 자동으로 설정되지 않으므로, 양쪽 모두에 값을 할당해야 한다.
🔍 예시) 양방향 연관관계
Parent와 Child 엔티티 간의 양방향 관계를 보여주는 예시입니다.
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity
public class Parent {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// 양방향 OneToMany 관계 : Parent가 여러 Child를 소유
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Child> children = new ArrayList<>();
}
@Entity
public class Child {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// 양방향 ManyToOne 관계 : 각 Child가 하나의 Parent를 참조
@ManyToOne
@JoinColumn(name = "parent_id")
private Parent parent;
// getter, setter 메소드
}
Child 엔티티가 주인으로서, `@JoinColumn`을 이용하여 외래 키 (parent_id)를 관리합니다.
Parent 엔티티는 `mappedBy`속성을 통해 Child 엔티티의 parent 필드를 참조하여 관계를 나타냅니다.
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Child> children = new ArrayList<>();
`mappedBy = parent`
역방향 연관관계를 설정합니다.
즉, 외래키를 관리하는 쪽은 Child 엔티티의 parent 필드입니다.
Parent 엔티티는 단순히 연관관계를 참조하는 역할만 합니다.
`cascade = CascadeType.ALL`
Parent 엔티티에 수행되는 모든 영속성 작업이 자동으로 자식 엔티티(연관관계 주인)에도 적용됩니다.
`orphanRemoval = true`
Parent 와 연관관계에서 Child엔티티가 제거되면, 해당 Child가 DB에서 자동으로 삭제됩니다.
🤔 어떤 엔티티를 주인으로 둬야 하는가?
1️⃣ 외래키를 관리하는 엔티티
데이터베이스에서 외래키를 소유하고 관리하는 쪽을 주인엔티티로 둡니다.
예를 들어 @ManyToOne 관계에서 주로 '다'에 해당하는 엔티티
2️⃣ 객체 모델링 관점
도메인 모델링에서 어느 엔티티가 관계의 변경(연관 객체 추가 및 제거 등)을 주도하는지 고려할 것
3️⃣ 연관관계의 유형
🔍 1:1 (OneToOne) 관계
한 개의 엔티티가 다른 한 개의 엔티티와 1:1로 매핑되는 관계입니다.
`@OneToOne`어노테이션을 사용하며, `@JoinColumn`을 사용하여 외래키를 지정할 수 있습니다.
양방향인 경우 `mappedBy` 속성을 사용하여 연관관계의 주인을 설정합니다. (주인 엔티티 = UserInfo)
지연로딩(`FetchType.LAZY`)를 사용하여 필요할 때만 데이터를 가져올 수 있도록 최적화 할 수 있습니다.
@Entity
public class UserInfo {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long Id;
private String name;
@OneToOne(fetch = FetchType.LAZY, cascade = cascadeType.All)
@JoinColumn (name = "profile_id")
private Profile profile;
}
@Entity
public class Profile {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long Id;
private String bio;
@OneToOne(mappedBy = "profile")
private UserInfo userInfo;
}
🔍 1:N (OneToMany) & N:1 (ManyToOne) 관계
다수의 엔티티가 여러개의 엔티티와 매핑되는 관계입니다.
@Entity
public class Team {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// 하나의 팀은 여러 팀원을 가질 수 있다.
@OneToMany(mappedBy = "team", cascade = CascadeType.ALL)
private List<Member> members = new ArrayList<>();
}
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// 여러 팀원은 하나의 팀을 가질 수 있다.
// 연관관계의 주인
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "team_id") // 외래 키 설정
private Team team;
}
N:1 관계에서 외래 키를 가진 쪽이 연관관계의 주인이므로, Member 엔티티가 연관관계의 주인입니다.
🔍 N:M (ManyToMany)관계
다대다 관계는 1:N, N:1 관계로 중간 테이블을 만들어 N:M 관계를 표현해야 합니다.
@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` 를 사용하면 JPA가 자동으로 중간 테이블을 생성해 준다.
`@JoinTable` 사용하여 중간 테이블의 컬럼을 명시할 수 있다.