일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 동적sql
- 연결리스트
- dfs
- 스토어드 프로시저
- 지연로딩
- PS
- 다대일
- querydsl
- CHECK OPTION
- JPQL
- 즉시로딩
- 이진탐색
- eager
- 비관적락
- 유니크제약조건
- 데코레이터
- BOJ
- 스프링 폼
- SQL프로그래밍
- 백트래킹
- 힙
- 다대다
- 낙관적락
- 연관관계
- fetch
- exclusive lock
- 일대다
- FetchType
- shared lock
- execute
- Today
- Total
흰 스타렉스에서 내가 내리지
[JPA] 복합 키 : 비식별 관계 매핑, @IdClass, @EmbeddedId 본문
@Entity
public class Hello{
@Id
private String id1;
@Id
private String id2;
}
둘 이상의 컬럼으로 구성된 복합 기본 키는 위의 코드처럼 하면 매핑 오류가 발생한다.
JPA에서 식별자를 둘 이상 사용하려면 별도의 식별자 클래스를 만들어야 한다.
JPA는 영속성 컨텍스트에 엔티티를 보관할 때 엔티티의 식별자를 키로 사용한다.
그리고 식별자를 구분하기 위해 equals와 hashCode를 사용해서 동등성 비교를 한다.
그런데 식별자 필드가 하나일 때는 보통 자바의 기본 타입을 사용하므로 문제가 없지만, 식별자 필드가 2개 이상이면 별도의 식별자 클래스를 만들고 그곳에 equals와 hashCode를 구현해야 한다.
JPA는 복합 키를 지원하기 위해 @IdClass와 @EmbeddedId 2가지 방법을 제공하는데,
@IdClass는 관계형 데이터베이스에 가까운 방법이고 @EmbeddedId는 좀 더 객체지향에 가까운 방법이다.
@IdClass
PARENT 테이블은 기본 키를 PARENT_ID1, PARENT_ID2로 묶은 복합 키로 구성했다.
따라서 복합 키를 매핑하기 위해 식별자 클래스를 별도로 만들어야 한다.
- 식별자 클래스의 속성명과 엔티티에서 사용하는 식별자의 속성명이 같아야 한다.
- Serializable 인터페이스를 구현해야 한다.
- equals, hashCode 를 구현해야 한다.
- 기본 생성자가 있어야 한다.
복합키를 사용하는 엔티티 저장
Parent parent = new Parent();
parent.setId1("id1");
parent.setId2("id2");
em.persist(parent);
위 코드를 보면 식별자 클래스인 ParentId가 보이지 않는데, em.persist()를 호출하면 영속성 컨텍스트에 엔티티를 등록하기 직전에 내부에서 Parent.id1, Parent.id2 값을 사용해서 식별자 클래스인 ParentId를 생성하고 영속성 컨텍스트의 키로 사용한다.
복합 키로 조회
ParentId parentId = new ParentId("id1", "id2");
Parent parent = em.find(Parent.class, parentId);
위 코드를 보면 식별자 클래스인 ParentId를 사용해서 엔티티를 조회한다.
자식 클래스 생성
@Entity
public class Child {
@Id @Column(name = "CHILD_ID")
private Long 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
@Entity
public class Parent {
@EmbeddedId
private ParentId id;
private String name;
}
Parent 엔티티에서 식별자 클래스를 직접 사용하고 @EmbeddedId 어노테이션을 적어주면 된다.
@Embeddable
public class ParentId implements Serializable {
@Column(name = "PARENT_ID1")
private String id1;
@Column(name = 'PARENT_ID2')
private String id2;
//equals and hashCode 구현
}
@IdClass와는 다르게 @EmbeddedId를 적용한 식별자 클래스는 식별자 클래스에 기본 키를 직접 매핑한다.
@EmbeddedId를 적용한 식별자 클래스는 다음 조건을 만족해야 한다.
- @EmbeddedId 어노테이션을 붙여주어야 한다.
- Serializable 인터페이스를 구현해야 한다.
- equals, hashCode 를 구현해야 한다.
- 기본 생성자가 있어야 한다.
- 식별자 클래스는 public 이어야 한다.
@EmbeddedId를 사용하였을 때 엔티티 저장
Parent parent = new Parent();
ParentId parentId = new ParentId("id1", "id2");
parent.setId(parentId);
em.persist(parent);
@EmbeddedId를 사용하였을 때 엔티티 조회
ParentId parentId = new ParentId("id1", "id2");
Parent parent = em.find(Parent.class, parentId);
복합 키는 equals()와 hashCode()를 필수로 구현해야 한다.
영속성 컨텍스트는 엔티티의 식별자를 키로 사용해서 엔티티를 관리한다.
그리고 식별자를 비교할 때 equals()와 hashCode()를 사용한다.
따라서 식별자 객체의 동등성(equals 비교)이 지켜지지 않으면 예상과 다른 엔티티가 조회되거나 엔티티를 찾을 수 없는 등 영속성 컨텍스트가 엔티티를 관리하는 데 심각한 문제가 발생한다.
@IdClass vs @EmbeddedId
@EmbbededId가 @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
복합 키에는 @GeneratedValue를 사용할 수 없다. 복합 키를 구성하는 여러 컬럼 중 하나에도 사용할 수 없다.
'Spring' 카테고리의 다른 글
[JPA]일대일 식별 관계 (0) | 2023.02.15 |
---|---|
[JPA] 복합 키 : 식별 관계 매핑 (0) | 2023.02.11 |
[JPA] 식별 관계와 비식별 관계 (0) | 2023.02.11 |
[JPA] 상속 관계 매핑 (0) | 2023.02.11 |
[JPA] 연관관계 - 다대다 @ManyToMany, 복합 키, 식별자 클래스, @IdClass (0) | 2023.02.10 |