[Hoops PJ] 트러블 슈팅 & TIL (2)
식별관계 비식별관계
나는 이걸 여태까지 잘 안다고 생각했지만 아니였다. 그래서 다시 정리!
특히 스프링에서 어떻게 되는지 다시 정리하기!
- 데이터를 유일하게 구분해낼 수 있는 컬럼을 primary key -> PK 두 개 이상으로 구성한 게 복합키
- pk 컬럼은 다른 테이블에 빌려줄 수 있으며 이렇게 빌려온 key 컬럼을 forign key 라고 한다.
테이블 관계 : 집합 관점
- 1:1
- 1:n or n:1
- n:n
테이블 관계 : 식별 관점
-
식별관계 : forign key가 복합키로 참여
-
비식별관계 : forign key가 키에 참여하지 않음
비식별 관계를 사용하는 이유
-
데이터의 무결성 측면에서는 식별관계로 만드는 것이 좋지만 개발 측면에서 다양한 요구사항을 수용하기 위해 엔티티간의 관계를 느슨하게 유지하는 것이 좋다. 장단점이 존재하지만 실제 프로젝트에서는 느슨한 관계를 유지하기 위해 비식별관계를 많이 사용한다.
-
예제에서 식별관계의 시혐결과 테이블에 학생아이디가 없다면 데이터를 추가하지 못할 것이다. 하지만 학생이 존재하지 않는 시혐 결과도 입혁하게 해달라는 요구사항이 있다면 비식별관계로 만들어야한다. -> 이런 요구사항이 비일비재하기 때문에 유연한 관계로 유지하는 것이 좋다.
-
다만 이런 비식별관계로 전환하는 경우 정규화에 대한 이해는 필수다.
그럼 스프링에서는 어떻게 되는걸까?
나는 여태까지 @ManyToOne
혹은 @OneToMany
를 사용했을 때 난 당연히 식별관계로 되는 줄 알았다.
결론은 케바케지만 어떻게 하면 스프링에서 식별, 비식별관계가 되는지 정리했다.
비식별 관계
@Entity
public class UserEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(nullable = false)
private Long userId;
@OneToMany(mappedBy = "userEntity")
private List<GameEntity> games = new ArrayList<>();
// 나머지 필드 및 메서드
}
@Entity
public class GameEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(nullable = false)
private Long gameId;
@ManyToOne
private UserEntity userEntity;
// 나머지 필드 및 메서드
}
- 위와 같은 상태는 비식별관계인데 그 이유는 UserEntity , GameEntity 가 각각 따로 PK값이 따로따로 생성되기 때문이다.
- 종속관계가 아님
- 참고로
@ManyToOne
@OneToMany
두개의 엔티티 중에 하나만 있어도 상관 없다.
식별관계는?
@Entity
public class UserEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(nullable = false)
private Long userId;
@OneToMany(mappedBy = "userEntityId", cascade = CascadeType.ALL, orphanRemoval = true)
private List<GameEntity> games = new ArrayList<>();
// 나머지 필드 및 메서드
}
@Entity
public class GameEntity {
@EmbeddedId
private GameEntityId id;
@ManyToOne
@MapsId("userEntityId")
private UserEntity userEntity;
// 나머지 필드 및 메서드
}
@Embeddable
public class GameEntityId implements Serializable {
@Column(name = "user_id")
private Long userEntityId;
@Column(name = "game_id")
private Long gameId;
// 생성자, getter, setter 등
}
- 식별 관계는 위와 같이 PK 값을 직접 할당해 줘야 한다.
- 내가 잘못 알고 있었던 부분은
@JoinColumn
이 종속관계로 이어지는줄 알았는데 그게 아니라 그냥 키 값의 이름을 다시 지정하는 역할을 한다.- 다시말하면 단순히 왜래 키 컬럼을 지정하고 매핑하는 역할을 하지 종속시키거나 식별관계로 만드는 역할이 아님.
@JoinColumn
@Entity
public class Order {
@Id
private Long id;
@ManyToOne
@JoinColumn(name = "customer_id") // 외래 키 컬럼 이름 지정
private Customer customer;
// ...
}
@JoinColumn
애노테이션은 JPA에서 엔티티 간의 연관 관계를 매핑할 때 사용되는 애노테이션이다.
- 외래 키 매핑
@JoinColumn
은 연관된 엔티티의 기본 키를 현재 엔티티의 외래 키 필드에 매핑하는 역할을 한다. 예를 들어,@ManyToOne
관계에서@JoinColumn
은 다(Many) 엔티티의 외래 키 필드를 일(One) 엔티티의 기본 키에 매핑한다. - 외래 키 컬럼 세부 정보 지정
@JoinColumn
의 속성을 통해 외래 키 컬럼의 이름, 제약조건 등을 지정할 수 있다.name
속성으로 외래 키 컬럼 이름을 명시할 수 있고,unique
,nullable
,updatable
등의 속성으로 제약조건을 설정할 수 있다. - 복합 키 매핑 복합 키(Composite Key)를 사용하는 경우,
@JoinColumns
애노테이션을 통해 여러 개의@JoinColumn
을 설정할 수 있다.
위 코드에서 @JoinColumn(name = "customer_id")
는 Order
엔티티의 외래 키 컬럼을 customer_id
로 지정하고, 이를 Customer
엔티티의 기본 키에 매핑한다.
즉, @JoinColumn
은 엔티티 간의 연관 관계에서 외래 키 매핑 정보를 명시하고, 외래 키 컬럼의 세부 사항을 설정할 수 있도록 해주는 중요한 역할을 한다.
하지만, @JoinColumn
애노테이션 자체는 식별 관계(Identifying Relationship)나 비식별 관계(Non-Identifying Relationship)를 결정하지 않는다.
@JoinColumn
은 단순히 외래 키 컬럼을 지정하고 매핑하는 역할을 한다. 엔티티 간의 관계가 식별 관계인지 비식별 관계인지는 기본 키(Primary Key)의 구성에 따라 결정된다.
식별 관계는 자식 엔티티의 기본 키에 부모 엔티티의 기본 키가 포함되는 경우를 말한다. 이때 부모 엔티티의 기본 키 값이 자식 엔티티의 기본 키를 구성하는 데 사용된다.
반면에 비식별 관계는 자식 엔티티의 기본 키가 부모 엔티티의 기본 키와 독립적으로 생성되는 경우를 말한다. 이때 자식 엔티티는 단순히 부모 엔티티를 외래 키로 참조한다.
예를 들어, @JoinColumn
을 사용하더라도 자식 엔티티의 기본 키가 별도의 @Id
및 @GeneratedValue
애노테이션으로 생성된다면, 이는 비식별 관계다.
반대로 자식 엔티티의 기본 키가 @EmbeddedId
나 @IdClass
를 사용하여 부모 엔티티의 기본 키를 포함하는 복합 키로 구성된다면, 이는 식별 관계가 된다.
따라서 @JoinColumn
은 외래 키 매핑을 위한 애노테이션일 뿐, 식별 관계나 비식별 관계를 결정하는 역할은 아니다. 관계의 식별 여부는 기본 키의 구성 방식에 따라 결정된다.
댓글남기기