복합키 식별 관계 매핑
부모, 자식, 손자까지 계속 기본 키를 전달하는 식별관계가 있다고 치자 식별 관계에서 자식 테이블은 부모 테이블의 기본 키를 포함해서 복합 키를 구성해야 하므로 @IdClass 나 @EmbeddedId를 사용해서 식별자를 매핑해야 한다. (일대일 관계랑은 약간 다르다.)
@IdClass
@Entity
@Data
public class Parent {
@Id
@Column(name = "PARENT_ID")
private String id;
private String name;
}
@Entity
@Data
@IdClass(ChildId.class)
public class Child {
@Id
@ManyToOne
@JoinColumn(name = "PARENT_ID")
private Parent parent;
@Id
@Column(name = "CHILD_ID")
private String childId;
private String name;
}
@Data
public class ChildId implements Serializable {
private String parent;
private String childId;
}
@Entity
@Data
@IdClass(GrandChildId.class)
public class CrandChild {
@Id
@ManyToOne
@JoinColumns({
@JoinColumn(name = "PARENT_ID"),
@JoinColumn(name = "CHILD_ID"),
})
private Child child;
@Id
@Column(name = "GRANDCHILD_ID")
private String id;
private String name;
}
@Data
public class GrandChildId implements Serializable{
private ChildId child;
private String id;
}
식별 관계는 기본 키와 외래 키를 같이 매핑 해야 한다. 따라서 식별자 매핑인 @Id와 연관관계 매핑인 @ManyToOne을 같이 사용하면 된다.
@Id
@ManyToOne
@JoinColumn(name = "PARENT_ID")
private Parent parent;
Child 엔티티의 Parent 필드를 보면 @Id로 기본 키를 매핑하면서 @ManyToOne과 @JoinColumn으로 외래 키를 같이 매핑한다.
EmbeddedId와 식별 관계
@Entity
@Data
public class Parent {
@Id
@Column(name = "PARENT_ID")
private String id;
private String name;
}
@Entity
@Data
public class Child {
@EmbeddedId
private ChildId id;
@MapsId("parentId") //ChildId.parentId 매핑
@ManyToOne
@JoinColumn(name = "PARENT_ID")
private Parent parent;
private String name;
}
@Data
@Embeddable
public class ChildId implements Serializable {
private String parentId; //@MapsId("parentId") 로 매핑
private String childId;
}
@Entity
@Data
public class CrandChild {
@EmbeddedId
private GrandChildId id;
@MapsId("childId") // GrandChildId.childId 매핑
@ManyToOne
@JoinColumns({
@JoinColumn(name = "PARENT_ID"),
@JoinColumn(name = "CHILD_ID"),
})
private Child child;
private String name;
}
@Data
@Embeddable
public class GrandChildId implements Serializable{
private ChildId childId; //@MapsId("childId") 로 매핑
@Column(name = "GRANDCHILD_ID")
private String id;
}
@EmbeddedId는 식별 관계로 사용할 연관관계의 속성에 @MapsId를 사용하면 된다. Child 엔티티 parent 필드를 보자
@MapsId("parentId") //ChildId.parentId 매핑
@ManyToOne
@JoinColumn(name = "PARENT_ID")
private Parent parent;
@IdClass와 다른 점은 @Id 대신에 @MapsId를 사용한 점이다. @MapsId는 외래 키와 매핑한 연관관계를 기본 키에도 매핑하겠다는 뜻이다. @MapsId의 속성 값은 @EmbeddedId를 사용한 식별자 클래스의 기본 키 필드를 지정하면 된다. 여기서는 ChildId의 parentId 필드를 선택했다.
비식별 관계로 구현
@Data
@Entity
public class Parent {
@Id
@GeneratedValue
@Column(name = "PARENT_ID")
private Long id;
private String name;
}
@Entity
@Data
public class Child {
@Id
@GeneratedValue
@Column(name = "CHILD_ID")
private Long id;
private String name;
@ManyToOne
@JoinColumn(name = "PARENT_ID")
private Parent parent;
}
@Data
@Entity
public class GrandChild {
@Id
@GeneratedValue
@Column(name = "GRANDCHILD_ID")
private Long id;
private String name;
@ManyToOne
@JoinColumn(name = "CHILD_ID")
private Child child;
}
식별 관계의 복합 키를 사용한 코드와 비교하면 매핑도 쉽고 코드도 단순하다. 그리고 복합 키가 없으므로 복합 키 클래스를 만들지 않아도 된다.
일대일 식별 관계
일대일 식별관계는 자식 테이블의 기본 키 값으로 부모테이블의 기본 키 값만 사용한다. 그래서 부모 테이블의 기본 키가 복합 키가 아니면 자식 테이블의 기본 키는 복합 키로 구성하지 않아도 된다.
@Entity
@Data
public class Board {
@Id
@GeneratedValue
@Column(name = "BOARD_ID")
private Long id;
private String title;
@OneToOne(mappedBy = "board")
private BoardDetail boardDetail;
}
@Entity
@Data
public class BoardDetail {
@Id
private Long boardId;
@MapsId
@OneToOne
@JoinColumn(name = "BOARD_ID")
private Board board;
private String content;
}
BoardDetail처럼 식별자가 단순히 컬럼 하나면 @MapsId를 사용하고 속성 값은 비워두면 된다. 이때 @MapsId는 @Id를 사용해서 식별자로 지정한 BoardDetail.boardId와 매핑된다.
private static void save(EntityManager entityManager){
Board board = new Board();
board.setTitle("제목");
entityManager.persist(board);
BoardDetail boardDetail = new BoardDetail();
boardDetail.setContent("내용");
boardDetail.setBoard(board);
entityManager.persist(boardDetail);
}
위의 코드는 일대일 식별 관계를 저장 하는 코드이다.
식별, 비 식별 관계의 장단점
데이터베이스 설계 관점에서 보면 다음과 같은 이유로 식별 관계보다 비식별 관계를 선호한다.
1. 식별 관계는 부모 테이블의 기본 키를 자식 테이블로 전파하면서 자식 테이블의 기본 키 컬럼이 점점 늘어난다. 그러면 조인할 때 SQL이 복잡해지고 기본 키 인덱스가 불필요하게 커질 수 있다.
2. 식별 관계는 2개 이상의 컬럼을 합해서 복합 기본 키를 만들어야 하는 경우가 많다.
3. 식별 관계를 사용할 때 기본 키로 비지니스 의미가 있는 자연 키 컬럼을 조합하는 경우가 많다. 반면에 비식별관계의 기본 키는 비지니스와 전혀 관계없는 대리 키를 주로 사용한다. 언제든지 요구사항은 변한다. 식별 관걔의 자연 키 컬럼들이 자식에 손자까지 전파되면 변경하기 힘들다.
4. 식별 관계는 부모 테이블의 기본 키를 자식 테이블의 기본 키로 사용하므로 비식별 관계보다 테이블 구조가 유연하지 못하다.
이외에도 몇가지가 더 있다. 하지만 무조건 식별관계가 단점만 있는 것은 아니다. 기본 키 인덱스를 활용하기 좋고, 상위 테이블들의 기본 키 컬럼을 자식, 손자 테이블들이 가지고 있으므로 특정 상황에 조인 없이 하위 테이블만으로 검색을 완료 할 수 있다.
이상으로 식별 비식별 관계에 대해 알아봤다. 다음은 고급매핑 마지막으로 조인 테이블에 대해서 알아보자!
출처 :
자바 ORM 표준 JPA 프로그래밍 (김영한)