이 장은 복합 키를 매핑하는 방법과 식별 관계, 비식별 관계를 매핑하는 방법에 대해 알아본다.
식별관계 : 부모 테이블의 기본 키를 내려받아서 자식 테이블의 기본 키 + 외래 키로 사용하는 관계
비식별 관계 : 부모 테이블의 기본 키를 받아서 자식 테이블의 외래 키로만 사용하는 관계
필수적 비식별 관계(Mandatory) : 외래 키에 NULL을 허용하지 않는다. 연관관계를 필수적으로 맺어야 한다.
선택적 비식별 관계(Optional) : 외래 키에 NULL을 허용한다. 연관관계를 맺을지 말지 선택할 수 있다.
JPA에서 식별자를 둘 이상 사용하려면 별도의 식별자 클래스를 만들어야 함
-> 식별자 필드가 2개 이상이면 별도의 식별자 클래스를 만들고 그곳에 식별자를 구분하기 위해 equals와 hashCode을 구현해 동등성 비교를 해야함.
@IdClass
관계형 데이터베이스에 가까운 방법
- PARENT 테이블은 기본 키를 PARENT_ID1, PARENT_ID2로 묶은 복합 키로 구성
- 따라서, 복합 키를 매핑하기 위한 식별자 클래스
- @IdClass를 사용해 식별자 클래스인 ParentId 클래스를 식별자 클래스로 지정
[식별자 클래스의 조건] in @IdClass
- 식별자 클래스의 속성명 = 엔티티에서 사용하는 식별자의 속성명
- Serializable 인터페이스 구현
- equals, hashCode 구현
- 기본 생성자
- 식별자 클래스는 public 이어야함
Parent parent = new Parent();
parent.setId1("myId1"); // 식별자
parent.setId2("myId2"); // 식별자
parent.setName("parentName");
em.persist(parent);
em.persist() 호출
-> 영속성 컨텍스트에 엔티티를 등록하기 직전 내부에서
Parent.id1, Parent.id2 값 사용해 식별자 클래스인 ParentId 생성
-> 영속성 컨텍스트의 키로 사용
ParentId parentId = new ParentId("myId1", "myId2");
Parent parent = em.find(Parent.class, parentId);
- 식별자 클래스인 ParentId를 사용해 엔티티 조회
- 부모 테이블의 기본 키 컬럼은 복합 키 -> 자식 테이블의 외래 키도 복합 키
- @JoinColumns : 외래 키 매핑 시 부모 테이블의 여러 컬럼을 매핑하기 위함
- @JoinColumns : 각각의 외래 키 컬럼 매핑
참고) @JoinColumn의 name 속성 = referencedColumnName 속성의 값
-> referencedColumnName 생략 가능
객체지향적인 방법
- 식별자 클래스 직접 사용
- @EmbeddedId 어노테이션
- @IdClass와는 다르게 식별자 클래스에 기본키 직접 매핑
[식별자 클래스의 조건] in @EmbeddedId
- @Embeddable 어노테이션
- Serializable 인터페이스 구현
- equals, hashCode 구현
- 기본 생성자
- 식별자 클래스는 public 이어야함
Parent parent = new Parent();
ParentId parentId = new ParentId("myId1", "myId2");
parent.setId1(parentId);
parent.setName("parentName");
em.persist(parent);
식별자 클래스 parentId를 직접 생성해서 사용
ParentId parentId = new ParentId("myId1", "myId2");
Parent parent = em.find(Parent.class, parentId);
조회코드는 @IdClass와 마찬가지로 식별자 클래스 parentId 직접 사용
복합 키는 equals(), hashCode() 필수로 구현
- id1과 id2 인스턴스 둘 다 myId1, myId2라는 같은 값을 가지고 있는데
인스턴스가 다름
- 마지막 줄의 id1.equals(id2)는 참? 거짓?
: equals()를 적절히 오버라이딩하지 않으면 결과는 거짓
-> 자바의 모든 클래스는 기본으로 Object 클래스를 상속받는데
이 클래스가 제공하는 기본 equals()는 인스턴스 참조값 비교인
==비교(동일성 비교)를 함
- 식별자 클래스는 보통 equals()와 hashCode()를 구현할 때 모든 필드 사용
- @EmbeddedId 특정 상황에 JPQL이 더 길어질 수 있음
참고) 복합 키에는 @GenerateValue 사용 X
- 식별 관계는 기본 키 & 외래 키 같이 매핑
-> 식별자 매핑 : @Id, 연관관계 매핑 : @ManyToOne 사용
- Child 엔티티의 parent 필드는 @Id로 기본 키를 매핑하여 @ManyToOne과 @JoinColumn으로 외래 키 같이 매핑
- 식별 관계로 사용할 연관관계의 속성에 @MapsId를 사용함.
- @MapsId : 외래키와 매핑한 연관관계를 시본 키에 매핑하겠다.
- @MapsId 속성 값 : @EmbeddedId를 사용한 식별자 클래스의 기본 키 필드 지정
위에서 들었던 식별 관계 테이블을 비식별 관계로 변경해보자!
- 매핑 쉽고 코드 단순
- 복합 키 없어서 복합 키 클래스를 만들지 않아도 됨
- 식별자가 복합 키가 아닌 컬럼 하나면 @MapsId 사용하고 속성 값은 비움
- @MapsId는 @Id를 사용해서 식별자로 지정한 BoardDetail.boardId로 자동 매핑
ex) 부모 테이블 기본 키 컬럼 : 1개
-> 자식 테이블 기본 키 컬럼 : 2개
-> 손자 테이블 기본 키 컬럼 : 3개
따라서, 조인할 때 SQL 복잡해지고 기본 키 인덱스가 불필요하게 커짐
ORM 신규 프로젝트 진행시 추천하는 방법
비식별 관계를 사용하고, 기본 키는 Long 타입의 대리 키를 사용
+) 필수적 비식별 관계
얼마 전에 동아리에서 프로젝트 진행하면서 ERD를 설계하는데 식별/비식별 관계를 선택하는 부분이 있었다. 근데 이 책에서 그 내용이 나와 이 부분을 공부하면서 내가 설계한 테이블 조금 고쳐야될 것 같다고 생각했다..ㅎ
출처 : 자바 ORM 표준 JPA 프로그래밍 책