JPA 연관관계 매핑 - 복합키 매핑
By on October 28, 2019
복합키 매핑
데이터베이스의 테이블을 설계 할때에는 식별관계와 비식별관계 두가지 방법으로 설계를 할 수 있습니다. 식별 관계와 비식별 관계에 대해서 정리 해보도록 하겠습니다.
식별관계와 비식별관계
-
식별관계
식별관계 매핑은 부모테이블의 키를 자식테이블이 내려 받아서 기본키 + 외래키 형태로 사용 하는 매핑 방식 입니다.
부모의 키가 자식 테이블로 상속 되면서 테이블의 키가 많아지는 단점이 있고 유연하지 못하기 때문에 꼭 필요 한 곳에서만 사용 되고 있습니다. -
비식별관계
비식별관계 매핑은 부모 테이블의 기본키를 자식 테이블의 외래키로만 사용 하는 매핑 방식 입니다.
비식별관계는 NULL을 허용하는 선택적 비식별관계 과 NULL을 허용 하지 않는 필수적 비식별관계로 나누어 집니다.
복합키 매핑 하기
-
JPA 는 복합키를 매핑 하기위해서 아래 두가지 어노테이션을 제공 합니다.
어떤 방식을 사용 하던 상관은 없으나 각각 장단점이 있기 때문에 예제를 보고 확인 해보도록 하겠습니다.어노테이션 설명 @IdClass 관계형 데이터 베이스에 가까운 방법 @EmbeddedId 객체 지향에 가까운 방법 -
복합키 매핑을 사용 하기 위한 조건
- @IdClass, @EmbeddedId 어노테이션 추가
- Serializable 인터페이스 구현(equals(), hashCode() 구현)
- 식별자 클래스는 public 이여야함.
식별 관계 매핑 하기
-
@IdClass 사용
- @IdClass 를 사용 하는 경우에는 식별자 클래스를 지정 해야 합니다.
//부모 @Entity public class Shop { @Id @Column(name = "SHOP_SEQ") private int seq; private String name; //...getter, setter } //자식1 @Entity @IdClass(PaymentID.class) public class Shop_Payments { @Id @Column(name="PAY_SEQ") private int paySeq; private String name; @Id @ManyToOne @JoinColumn(name = "SHOP_SEQ") private Shop shop; //...getter, setter } //식별자 클래스 public class PaymentID implements Serializable{ private Shop shop; //Shop_Payments.shop 매핑 private int paySeq; //Shop_Payments.paySeq 매핑 //...hashCode(), equals } //자식2 @Entity @IdClass(OrderID.class) public class Shop_Order { @Id private int orderSeq; private String name; @Id @ManyToOne @JoinColumns({ @JoinColumn(name="SHOP_SEQ"), @JoinColumn(name="PAY_SEQ") }) private Shop_Payments payments; //...getter, setter } //식별자 클래스 public class OrderID implements Serializable{ private Shop_Payments payments; //Shop_Order.payments 매핑 private int orderSeq; //Shop_Order.orderSeq 매핑 //...hashCode(), equals }
- 테스트 코드
Shop shop = new Shop(); shop.setSeq(1); shop.setName("JPA마켓"); em.persist(shop); Shop_Payments payment = new Shop_Payments(); payment.setPaySeq(1); payment.setName("JPA간편페이"); payment.setShop(shop); em.persist(payment); Shop_Order order = new Shop_Order(); order.setOrderSeq(1); order.setName("봉골레"); order.setPayments(payment); em.persist(order);
-
@EmbeddedId 사용
- @EmbeddedId 를 적용한 식별자 클래스를 사용하는 경우에는 식별자 클래스에 기본 키를 직접 매핑 해야 합니다.
@Entity public class Shop { @Id @GeneratedValue @Column(name = "SHOP_SEQ") private int seq; private String name; //...getter, setter } //자식1 @Entity public class Shop_Payments { @EmbeddedId private PaymentID paySeq; private String name; @MapsId("shopSeq") //PaymentID.shopSeq 매핑 @ManyToOne @JoinColumn(name = "SHOP_SEQ") private Shop shop; //...getter, setter } //Serializable 인터페이스 구현 @Embeddable public class PaymentID implements Serializable{ @Column(name="PAY_SEQ") private int paySeq; private int shopSeq; //@MapsId("shopSeq") 매핑 public PaymentID() {} public PaymentID(int shopSeq, int paySeq) { this.shopSeq = shopSeq; this.paySeq = paySeq; } //...hashCode(), equals } //자식1 @Entity public class Shop_Order { @EmbeddedId private OrderID orderSeq; private String name; @MapsId("paySeq") //OrderID.paySeq 매핑 @ManyToOne @JoinColumns({ @JoinColumn(name="SHOP_SEQ"), @JoinColumn(name="PAY_SEQ") }) private Shop_Payments payment; //...getter, setter } @Embeddable public class OrderID implements Serializable { @Column(name = "ORDER_SEQ") private int orderSeq; private PaymentID paySeq; // @MapsId("paySeq") 매핑 public OrderID() {} public OrderID(int orderSeq, PaymentID paySeq) { super(); this.orderSeq = orderSeq; this.paySeq = paySeq; } //...hashCode(), equals }
- 테스트 코드
Shop shop = new Shop(); shop.setName("JPA마켓"); em.persist(shop); PaymentID id = new PaymentID(1,1); Shop_Payments payment = new Shop_Payments(); payment.setPaySeq(id); payment.setName("JPA간편페이"); payment.setShop(shop); em.persist(payment); OrderID orderId = new OrderID(1, id); Shop_Order order = new Shop_Order(); order.setOrderSeq(orderId); order.setName("봉골레"); order.setPayment(payment); em.persist(order);
- 테스트코드를 수행한 결과는 (@IdClass, @EmbeddedId) 동일합니다.
비식별 관계 매핑 하기
-
위 예제를 바탕으로 비식별 관계로 변경 해보겠습니다.
@Entity public class Shop { @Id @GeneratedValue @Column(name = "SHOP_SEQ") private int seq; private String name; //...hashCode(), equals } @Entity public class Shop_Payments { @Id @GeneratedValue @Column(name="PAY_SEQ") private int paySeq; private String name; @ManyToOne @JoinColumn(name = "SHOP_SEQ") private Shop shop; //...hashCode(), equals } //자식2 @Entity public class Shop_Order { @Id @GeneratedValue @Column(name="ORDER_SEQ") private int orderSeq; private String name; @ManyToOne @JoinColumn(name="PAY_SEQ") private Shop_Payments payments; //...hashCode(), equals }
- 테스트 코드
Shop shop = new Shop(); shop.setName("JPA마켓"); em.persist(shop); Shop_Payments payment = new Shop_Payments(); payment.setName("JPA간편페이"); payment.setShop(shop); em.persist(payment); Shop_Order order = new Shop_Order(); order.setName("봉골레"); order.setPayments(payment); em.persist(order);
- 테스트 결과
- 비식별 관계 매핑은 식별관계 매핑의 코드와 비교 하면 매우 쉽고 단순합니다. 식별자 클래스를 생성할 필요도 없고 복합 키 클래스를 만들 필요도 없습니다.
마무리
오늘은 식별관계 vs 비식별관계 매핑에 대해서 정리 해보았습니다.
식별관계 매핑은 자식이 늘어나면서 키 컬럼이 계속 늘어나고 변경이 쉽지 않으며 키의 자동생성 (@GeneratedValue)을 활용 하지 못하는 단점이 있기 때문에
데이터베이스 설계 관점에서는 비식별관계 매핑을 더 선호합니다.
자바 ORM 표준 JPA프로그래밍(저자:김영한) 도서를 학습하면서 정리 한 내용 입니다.