twocowsong

다 대 다 : 매핑의 한계와 극복, 연결 엔티티 사용 본문

IT/JPA

다 대 다 : 매핑의 한계와 극복, 연결 엔티티 사용

WsCode 2022. 6. 26. 15:27

@ManyToMany를 사용하면 연결 테이블을 자동으로 처리해주므로 도메인 모델이 단순해지고 편리해집니다.

하지만 이 매핑에는 한계가 있습니다.

예를들어 회원이 상품을 주문하면 연결 테이블에 단순히 주문한 회원 아이디와 상품 아이디만 담고 끝나지 않습니다.

 

예를들어 회원이 상품을 주문하면 연결 테이블에 단순히 주문한 회원아이디와 상품 아이디만 담고 끝나지 않습니다.

보통은 연결 테이블에 주문 수량 컬럼이나 주문한 날짜 같은 컬럼이 더 필요합니다.

 

연결 테이블에 주문 수량(ORDERAMOUNT)과 주문 날짜(ORDERDATE)컬럼을 추가했습니다.

이렇게 컬럼을 추가하면 더는 @ManyToMany를 사용 할 수 없습니다.

왜냐하면 주문 엔티티나 상품엔티티에는 추가한 컬럼들이 매핑이 될수 없기 때문입니다.

 

결국 연결 테이블을 매핑하는 연결 엔티티를 만들고 이곳에 추가한 컬럼들을 매핑해야 합니다.

그리고 엔티티 간의 관계도 테이블 관계처럼 다대다 에서 일대다, 다대일 관계로 풀어야합니다.

그래서 회원상품 엔티티를 추가하였습니다.

 

@Entity
public class Member {

   @Id
   @Column(name="MEMBER_ID")
   private String id;

   @OneToMany(mappedBy = "member")
   private List<MemberProduct> memberProducts;

   // Getter, Setter...
}

회원과 회원 상품을 양방향 관계로 만들었습니다.

회원상품 엔티티 쪽이 외래키를 가지고 있으므로 연관관계의 주인입니다.

연관관계의 주인이 아닌 Member.membersProducts에서 mappedBy를 사용하였습니다.

 

@Entity
public class Product {

   @Id
   @Column(name = "PRODUCT_ID")
   private String id;

   private String name;

}

상품 코드를 보면 엔티티에서 회원상품 엔티티로 객체 그래프 탐색 기능이 필요하지 않다고 판단해서 연관관계를 만들지 않았습니다. 순수 제품의 정보만을 가지고있는 Product엔티티가 되겠습니다.

 

다음으로는 가장 중요한 회원상품 엔티티와 회원상품 식별자 클래스를 보겠습니다.

@Entity
@IdClass(MemberProductId.class)
public class MemberProduct {

   @Id
   @ManyToOne
   @JoinColumn(name = "MEMBER_ID")
   private Member member;

   @Id
   @ManyToOne
   @JoinColumn(name = "PRODUCT_ID")
   private Product product; // MemberProductId.product와 연결

   private int orderAmount;

   @CreationTimestamp
   private Date orderDate;

}
public class MemberProductId implements Serializable {
   private String member;
   private String product;

   //hashCode and equals
   @Override
   public boolean equals(Object obj) {
      return super.equals(obj);
   }

   @Override
   public int hashCode() {
      return super.hashCode();
   }
}

회원상품 엔티티를 보면 기본 키를 매핑하는 @Id와 외래 키를 매핑하는 @JoinColumn을 동시에 사용해서 기본 키 + 외래 키를 한번에 매핑하였습니다. 그리고 @IdClass를 사용해서 복합 기본키를 매핑하였습니다.

 

복합 기본 키

회원상품 엔티티는 기본 키가 MEMBER_ID와 PRODUCT_ID로 이루어진 기본 키(복합키)입니다.

JPA에서 복합 키를 사용하려면 별도의 식별자 클래스를 만들어야합니다.

그리고 엔티티에 @IdClass를 사용해서 식별자 클래스를 지정하면 됩니다.

여기서는 MemberProductId 클래스를 복합 키를 위한 식별자 클래스로 사용하면 됩니다.

 

복합키를 위한 식별자 클래스는 다음과 같은 특징이 있습니다.

- 복합 키는 별도의 식별자 클래스로 만들어야합니다.

- Serializable을 구현해야합니다.

- equals와 hashcode 메소드를 구현해야 합니다.

- 기본 생성자가 있어야 합니다.

- 식별자 클래스는 public 이어야 합니다.

- @IdClass를 사용하는 방법외에 @EmbeddedId를 사용하는 방법도 있습니다.

 

종합해보면 회원상품은 회원과 상품의 기본키를 받아서 자신의 기본키로 사용함과 동시에 회원과의 관계를 위한 외래키로 사용합니다.

또한 MemberProductId 식별자 클래스로 두 기본 키를 묶어서 복합 기본키로 사용합니다.

 

public static void save(EntityManager em) {
   // 회원저장
   Member member1 = new Member();
   member1.setId("member1");
   em.persist(member1);

   // 상품저장
   Product productA = new Product();
   productA.setId("productA");
   productA.setName("상품1");
   em.persist(productA);

   // 회원 상품 저장
   MemberProduct memberProduct = new MemberProduct();
   memberProduct.setMember(member1);
   memberProduct.setProduct(productA);
   memberProduct.setOrderAmount(2);

   em.persist(memberProduct);
}

 

회원상품 엔티티를 만들면서 연관된 회원 엔티티와 상품 엔티티를 설정하였습니다.

회원상품 엔티티는 DB에 저장될때 연관된 회원의 식별자와 상품의 식별자를 가져와서 자신의 기본 키 값으로 사용했습니다.

 

public static void find(EntityManager em) {
   // 기본 키 값 생성
   MemberProductId memberProductId = new MemberProductId();
   memberProductId.setMember("member1");
   memberProductId.setProduct("productA");

   MemberProduct memberProduct = em.find(MemberProduct.class, memberProductId);

   Member member = memberProduct.getMember();
   Product product = memberProduct.getProduct();

   System.out.println("member : " + member.getId());
   System.out.println("product : " + product.getId());
   System.out.println("memberProduct Count : " + memberProduct.getOrderAmount());
}
member : member1
product : productA
memberProduct Count : 2

 

복합 키가되면 이야기가 달라지게됩니다. 복합 키는 항상 식별자 클래스를 만들어야 합니다.

em.find()를 보면 생성한 식별자 클래스로 엔티티를 조회합니다.

복합 키를 사용하는 방법은 복잡합니다. 그렇기에 복합키를 사용하지않고 간단히 다대다 관계를 구성하는방법을 알아보겠습니다.

'IT > JPA' 카테고리의 다른 글

다대다 연관관계 정리  (0) 2022.06.28
다대다 : 새로운 기본 키 사용  (0) 2022.06.27
다 대 다 : 양방향  (0) 2022.06.26
다 대 다 : 단방향  (0) 2022.06.21
다 대 다 [N : N]  (0) 2022.06.20