ERD를 짜다가 보면 Identifying Relationship(식별관계)와 Non-Identifying Relationship(비식별관계)을 봤을것이다.
오늘은 이 둘이 무엇이고, 무슨 차이가 있는지 알아보자.
▶ Identifying Relationship(식별관계)
Identifying Relationship은 부모테이블의 PK를 자식테이블의 PK로 사용하는것이다.
간단하게 예시로 보자면
부모테이블(학생정보) 없이 자식 테이블(성적 정보)이 존재할 수 없는 관계이다.
이렇게 두개의 PK가 존재해도 된다.
subject_id는 자식 테이블의 PK이고
student_id는 부모 테이블의 PK이면서 자식테이블의 PK이자 FK이다.
▶ Non-Identifying Relationship(비식별관계)
Non-Identifying Relationship은 부모테이블의 PK를 자식테이블의 FK로 사용하는 것이다.
간단하게 예시로 보자면
비식별 관계는 자식테이블의 PK인 subject_id가 따로 있어서,
부모테이블(학생정보)없이 자식테이블(성적정보)이 존재할 수 있다.
추가로 무조건 부모테이블의 PK가 자식테이블의 FK가 되어야하는건 아니다.
위 사진처럼 부모테이블의 unique한 필드(student_email)라면 자식테이블의 FK가 될 수 있다.
비식별 관계는 FK의 NULL 허용 여부에 따라서 필수적, 선택적 비식별 관계로 나뉜다.
- 필수적 비식별 관계(Mandatory) : FK에 NULL을 허용하지 않음. 연관관계 필수적
- 선택적 비식별 관계(Optional) : FK에 NULL을 허용. 연관관계가 필수적이지 않음.
▷ 필수적 비식별 관계
자식 테이블의 레코드가 존재하기 위해서 반드시 부모 테이블의 관련 레코드가 존재해야한다.
예를 들어보자면 학생의 성적정보가 존재하기 위해서는 반드시 학생의 id가 있어야한다.
즉, student_id(FK)가 NULL이면 안된다.
▷ 선택적 비식별 관계
자식 테이블이 레코드가 존재할 때 부모테이블의 관련 레코드가 반드시 존재할 필요는 없다.
예를 들어 직원 테이블과 프로젝트 테이블이 있다고 해보자.
이때 프로젝트에 아직 할당된 직원이 없으면 프로젝트 테이블의 직원 칸에는 NULL이 될 수 있다.
즉 FK가 NULL일 수도 있다.
▶ 정리
정리를 해보자면
식별관계
- 식별관계에서는 부모 테이블의 PK를 자식 테이블의 PK로 사용해야한다.
- 부모테이블의 PK가 아닌것을 자식테이블의 PK로 사용하면 식별관계가 아니다.
- 식별관계에서 부모테이블의 PK를 자식테이블의 PK로 사용하면서 자식테이블의 PK를 추가하는것은 가능하다.
비식별관계
- 비식별관계에서는 부모테이블의 PK를 자식테이블의 FK로 사용해야한다.
- 부모테이블의 PK가 아닌 일반키를 자식테이블의 FK로 사용해도 되지만 일반키에 유니크 인덱스가 설정되어야한다.
- 비식별 관계는 필수적, 선택적으로 나뉜다.
이렇게 나뉠 수 있다.
▶ Entity 코드
각 상황에 맞는 Entity 코드를 보자
- 식별 관계에서 부모 테이블의 PK를 자식 테이블의 PK로 사용
- 식별 관계에서 부모 테이블의 PK를 자식 테이블의 PK로 사용하면서 자식테이블의 PK를 추가
- 비식별 관계에서 부모 테이블의 PK를 자식테이블의 FK로 사용
- 비식별 관계에서 부모 테이블의 PK가 아닌 일반키를 자식 테이블의 FK로 사용
- 필수적 비식별 관계의 예시
- 선택적 비식별 관계의 예시
▷ 식별 관계 : 부모 테이블의 PK를 자식 테이블의 PK로 사용
부모 테이블의 PK가 자식 테이블의 PK로 사용됨
@Entity
public class Student { //부모 테이블
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long studentId; //부모 테이블의 PK
@Column(nullable = false)
private String studentName;
@Column(nullable = false)
private int studentGrade;
@OneToMany(mappedBy = "student")
private Set<Grade> grades;
}
----------------------------------------------------------------------------------
@Entity
public class Grade { //자식 테이블
@Id
private Long studentId; //부모 테이블의 PK를 자식 테이블의 PK로 사용
@MapsId //Grade 엔티티의 PK가 Student 엔티티와 공유된다는 것을 JPA에게 알려줌
@ManyToOne //Grade 엔티티의 student 필드가 Student 엔티티와 N:1로 연결되어 있음을 나타냄
@JoinColumn(name = "student_id") //Grade 엔티티의 studnet 필드는 student_id 컬럼을 통해 Student 엔티티와 연결됨
private Student student; // student_id컬럼은 Grade 테이블에서 Student 테이블의 기본키를 참조
@Column(nullable = false)
private String subjectName;
@Column(nullable = false)
private int subjectScore;
}
▷ 식별 관계 : 부모 테이블의 PK를 자식 테이블의 PK로 사용하면서 자식테이블의 PK를 추가
부모 테이블의 PK와 자식 테이블의 추가 PK가 복합키를 형성
@Entity
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long studentId;
@Column(nullable = false)
private String studentName;
@Column(nullable = false)
private int studentGrade;
@OneToMany(mappedBy = "student", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Grade> grades;
}
----------------------------------------------------------------------------------
@Entity
public class Grade implements Serializable {
@EmbeddedId
private GradeId id;
@MapsId("studentId")
@ManyToOne
@JoinColumn(name = "student_id", nullable = false)
private Student student;
@Column(nullable = false)
private int subjectScore;
}
----------------------------------------------------------------------------------
@Embeddable
@NoArgsConstructor
@AllArgsConstructor
public class GradeId implements Serializable {
private Long studentId;
private String subjectName;
// getters and setters
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
GradeId gradeId = (GradeId) o;
return Objects.equals(studentId, gradeId.studentId) &&
Objects.equals(subjectName, gradeId.subjectName);
}
@Override
public int hashCode() {
return Objects.hash(studentId, subjectName);
}
}
DB 예시
▷ 비식별 관계 : 부모 테이블의 PK를 자식 테이블의 FK로 사용
부모 테이블의 PK가 자식 테이블의 FK로 사용됨
@Entity
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long studentId;
@Column(nullable = false)
private String studentName;
@Column(nullable = false)
private int studentGrade;
@OneToMany(mappedBy = "student")
private Set<Grade> grades;
}
----------------------------------------------------------------------------------
@Entity
public class Grade {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long subjectId;
@ManyToOne
@JoinColumn(name = "student_id", nullable = false)
private Student student;
@Column(nullable = false)
private String subjectName;
@Column(nullable = false)
private int subjectScore;
}
▷ 비식별 관계 : 부모 테이블의 PK가 아닌 일반키를 자식 테이블의 FK로 사용
부모 테이블의 일반키를 자식 테이블의 FK로 사용하며, 이 일반키는 유니크 인덱스로 설정됨
@Entity
@Table(uniqueConstraints = {
@UniqueConstraint(columnNames = "studentName")
})
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long studentId;
@Column(nullable = false)
private String studentName;
@Column(nullable = false)
private int studentGrade;
@OneToMany(mappedBy = "student")
private Set<Grade> grades;
}
----------------------------------------------------------------------------------
@Entity
public class Grade {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long subjectId;
@ManyToOne
@JoinColumn(name = "student_name", referencedColumnName = "studentName", nullable = false)
private Student student;
@Column(nullable = false)
private String subjectName;
@Column(nullable = false)
private int subjectScore;
}
▷ 필수적 비식별 관계의 예시
자식 테이블의 FK가 반드시 부모 테이블의 PK를 참조해야 하며, NULL을 가질 수 없다.
@Entity
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long studentId;
@Column(nullable = false)
private String studentName;
@Column(nullable = false)
private int studentGrade;
@OneToMany(mappedBy = "student")
private Set<Grade> grades;
}
----------------------------------------------------------------------------------
@Entity
public class Grade {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long subjectId;
@ManyToOne(optional = false) //optional 추가
@JoinColumn(name = "student_id", nullable = false)
private Student student;
@Column(nullable = false)
private String subjectName;
@Column(nullable = false)
private int subjectScore;
}
▷ 선택적 비식별 관계의 예시
자식 테이블의 FK가 부모 테이블의 PK를 참조할 수 있지만, NULL을 가질 수 있다.
@Entity
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long studentId;
@Column(nullable = false)
private String studentName;
@Column(nullable = false)
private int studentGrade;
@OneToMany(mappedBy = "student")
private Set<Grade> grades;
}
----------------------------------------------------------------------------------
@Entity
public class Grade {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long subjectId;
@ManyToOne
@JoinColumn(name = "student_id")
private Student student;
@Column(nullable = false)
private String subjectName;
@Column(nullable = false)
private int subjectScore;
}
▶ 내 의견
내 의견은 굳이 식별관계를 사용하지 않아도 될것같다.
식별관계를 써야한다면 식별 관계보다는 비식별 관계에서 필수적 비식별 관계를 사용할 것 같다.
왜냐하면
- 구조가 단순하며, 외래키와 기본키가 분리되어 있어서 이해하기 쉽고 관리하기도 용이함.
- 기본키를 변경하지 않고도 외래키를 업데이트 할 수 있어서 유지보수 하기 쉬움.
- 복합키를 사용하지 않기 때문에 인덱스 관리를 더 쉽게 할 수 있음
- 위 이유 때문에 비식별 관계가 더 많이 사용되는 경향이 있음.
이러한 이유때문이다! 한번 정확히 알고 넘어가면 좋을 것 같다! 알찬 시간이었다!
혼자서 공부한 내용입니다!
틀린 부분이 있으면 댓글로 알려주시면 감사하겠습니다!