250x250
Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- 연관관계
- shared lock
- 일대다
- BOJ
- exclusive lock
- 힙
- querydsl
- 스프링 폼
- 스토어드 프로시저
- 지연로딩
- execute
- JPQL
- fetch
- 다대다
- SQL프로그래밍
- FetchType
- CHECK OPTION
- 동적sql
- 데코레이터
- 즉시로딩
- 연결리스트
- 유니크제약조건
- 낙관적락
- 이진탐색
- dfs
- PS
- 비관적락
- 백트래킹
- 다대일
- eager
Archives
- Today
- Total
흰 스타렉스에서 내가 내리지
복합 키 : 비식별 관계 매핑 본문
728x90
- JPA 에서 식별자를 둘 이상 사용하려면 별도의 식별자 클래스를 만들어야 한다.
- JPA 는 영속성 컨텍스트에 엔티티를 보관할 때, 엔티티의 식별자를 키로 사용한다.
- 그리고 식별자를 구분하기 위해 equals 와 hashCode 를 사용해서 동등성 비교를 한다.
- 그런데 식별자 필드가 하나일 떄는 보통 자바의 기본 타입을 사용하므로 문제가 없지만, 식별자 필드가 2개 이상이면 별도의 식별자 클래스를 만들고 그곳에 equals 와 hashCode 를 구현해야 한다.
- JPA 는 복합키를 지원하기 위해 @IdClass 와 @EmbeddedId 2가지 방법을 제공한다.
- @IdClass 는 관계형 데이터베이스에 가까운 방법이고, @EmbeddedId 는 좀 더 객체지향에 가까운 방법이다.
# @IdClass
- 복합 키를 매핑하기 위해 식별자 클래스를 별도로 만들어야 한다.
@Entity
@IdClass(ParentId.class)
public class Parent {
@Id @Column(name = "PARENT_ID1")
private String id1; // ParentId.id1 과 연결
@Id @Column(name = "PARENT_ID2")
private String id2; // ParentId.id2 와 연결
}
public class ParentId implements Serializable {
private String id1;
private String id2;
public ParentId(){}
public ParentId(String id1, String id2){
this.id1 = id1; this.id2 = id2;
}
@Override
public int hashCode() {
return Objects.hash(id1, id2);
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof ParentId))
return false;
ParentId that = (ParentId) o;
return Objects.equals(id1, that.id1) && Objects.equals(id2, that.id2);
}
}
- 식별자 클래스의 속성명과 엔티티에서 사용하는 식별자의 속성명이 같아야 한다.
- 예제의 Parent.id1 과 ParentId.id1, 그리고 Parent.id2 와 ParentId.id2 가 같다
- Serializable 인터페이스를 구현해야 한다
- equals, hashCode 를 구현해야 한다.
- 기본 생성자가 있어야 한다
- 식별자 클래스는 Public 이어야 한다.
@Entity
public class Child {
@Id
private String id;
@ManyToOne
@JoinColumns({
@JoinColumn(name = "PARENT_ID1", referencedColumnName = "PARENT_ID1"),
@JoinColumn(name = "PARENT_ID2", referencedColumnName = "PARENT_ID2")
})
private Parent parent;
}
- 부모 테이블의 키본키 컬럼이 복합 키이므로 자식 테이블의 외래 키도 복합 키이다.
- 외래키 매핑 시 여러 컬럼을 매핑해야 하므로 @JoinColumns 어노테이션을 사용하고 각각의 외래 키 컬럼을 @JoinColumn 으로 매핑한다.
- 참고로 위 예제처럼 @JoinColumn 의 name 속성과 referencedColumnName 속성의 값이 같으면 referencedColumnName 은 생략해도 된다.
# @EmbeddedId
- @IdClass 가 데이터베이스에 맞춘 방법이라면 @EmbeddedId 는 좀 더 객체지향적인 방법이다.
@Entity
public class Parent {
@EmbeddedId
private ParentId id;
}
@Embeddable
public class ParentId implements Serializable {
@Column(name = "PARENT_ID1")
private String id1;
@Column(name = "PARENT_ID2")
private String id2;
@Override
public int hashCode() {
return Objects.hash(id1, id2);
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof ParentId))
return false;
ParentId that = (ParentId) o;
return Objects.equals(id1, that.id1) && Objects.equals(id2, that.id2);
}
}
- Parent 엔티티에서 식별자 클래스를 직접 사용하고 @EmbeddedId 어노테이션을 적어주면 된다
- @IdClass 와는 다르게 @EmbeddedId 를 적용한 식별자 클래스는 식별자 클래스에 기본 키를 직접 매핑한다.
# 복합 키와 equals(), hashCode()
- 복합키는 equals() 와 hashCode 를 필수로 구현해야 한다.
- ParentId id1 = new ParentId(); ParentId id2 = new ParentId(); id1.equals(id2) → ?
- equals() 를 적절이 오버라이딩 하지 않았다면 거짓이다.
- 자바의 모든 클래스는 기본으로 Object 클래스를 상속받는데 이 클래스가 제공하는 기본 equals()는 인스턴스 참조 값 비교인 == 비교 (동일성 비교)를 하기 때문이다.
- 영속성 컨텍스트는 엔티티의 식별자를 키로 사용해서 엔티티를 관리한다.
- 그리고 식별자를 비교할 떄 equals() 와 hashCode() 를 사용한다.
- 따라서 식별자 객체의 동등성이 지켜지지 않으면 예상과 다른 엔티티가 조회되거나 엔티티를 찾을 수 없는 등 영속성 컨텍스트가 엔티티를 관리하는데 심각한 문제가 발생한다.
# 장단점
- @EmbeddedId 가 @IdClass 와 비교해서 더 객체지향적이고 중복도 없어서 좋아보리긴 하지만, 특정 상황에 JPQL 이 더 길어질 수 있다.
em.createQuery("select p.id.id1, p.id.id2 from Parent p"); // EmbeddedId
em.createQuery("select p.id1, p.id2 from Parent p"); // Idclass
복합 키에는 @GenerateValue 를 사용할 수 없다
'JPA' 카테고리의 다른 글
프록시와 즉시로딩/지연로딩 (0) | 2024.04.08 |
---|---|
일대일 식별 관계 (0) | 2024.04.04 |
식별 관계 vs 비식별 관계 (0) | 2024.04.04 |
@MappedSuperclass (1) | 2024.04.03 |
연관관계 편의 메소드 (0) | 2024.04.02 |