DB에서는 외래키(FK)를 기본키(PK)에 포함하는지 여부에 따라 식별과 비식별관계로 구분한다.
식별관계는 부모 테이블의 기본키를 내려받아서 자식 테이블의 기본키 + 외래키로 사용하는 관계이다.
비식별관계는 부모 테이블의 기본키를 받아서 자식 테이블의 외래키로만 사용하는 관계다.
비식별 관계에는 필수적 비식별 관계와 선택적 비식별 관계가 있다.
외래키에 NULL을 허용하지 않는다. 연관관계를 필수적으로 맺어야 한다.
외래키에 NULL을 허용한다. 연관관계를 맺을지 말지 선택할 수 있다.
위 그림의 Parent처럼 복합키가 있는 경우 PK가 하나인 경우와는 다르게 매핑을 해야한다.
JPA에는 복합키 지원을 위해 @IdClass와 @EmbeddedId를 지원한다.
[Parent]
@Entity
@IdClass(ParentId.class)
public class Parent{
@Id
@Column(name = "PARENT_ID1")
private String parentId1;
@Id
@Column(name = "PARENT_ID2)
private String parentId2;
private String name;
}
public class ParentId implements Serializable {
private String parentId1;
private String parentId2;
@Override
public int hashcode() {
...
}
@Override
public boolean equals(Object obj) {
...
}
}
식별자 클래스의 특징은 다음과 같다.
[Child]
@Entity
public class Child {
@Id
@Column(name = "CHILD_ID")
private String childId;
@ManyToOne
@JoinColumns({
@JoinColumn(name = "PARENT_ID1", referenceColumnName = "PARENT_ID1")
@JoinColumn(name = "PARENT_ID2", referenceColumnName = "PARENT_ID2")
})
private Parent parent;
}
조인 컬럼이 2개 이상이기 때문에 @JoinColumn
이 아닌 @JoinColumns
를 사용한다.
public static void save(EntityManager em) {
Parent parent = new Parent();
parent.setParentId1("PARENT#1");
parent.setParentId2("PARENT#2");
em.persist(parent);
}
public static void find(EntityManager em) {
ParentId parentId = new ParentId("PARENT#1", "PARENT#");
Parent parent = em.find(Parent.class, parentId);
System.out.println("parent = " + parent);
}
복합키 엔티티를 저장할 때에는 식별자 클래스를 이용하여 저장하지 않아도 된다.
영속성 컨텍스트에 등록하기 전 하이버네이트가 내부에서 ParentId를 생성하여 키로 사용한다.
반면 조회코드에서는 식별자 클래스인 ParentId를 사용하여 조회한다.
이때 복합키를 생성자인자로 받는 생성자를 별도로 구현했는데, 이 경우 기본생성자를 반드시 구현해주어야 한다.