JVM/JPA

복합키 매핑 @IdClass, @EmbeddedId

kyoulho 2023. 6. 16. 18:10

복합 키를 사용하기 위한 식별자 클래스를 지정하는 데 사용한다.

특징

  • Serializable을 구현해야 한다.
  • equals와 hashCode 메소드를 구현해야 한다.
  • 기본 생성자가 있어야 한다.
  • 식별자 클래스는 public이어야 한다.
  • 복합 키에는 @GenerateValue를 사용할 수 없다. 복합 키를 구성하는 여러 컬럼 중 하나에도 사용할 수 없다.

 

아래 두 예제 코드는 식별 관계를 예로 들고 있다.

매핑도 쉽고 코드도 단순하고 복합 키도 필요 없는 비식별 관계를 사용하자.

그럴듯 해 보이는 것이 항상 문제를 만든다.

 

@IdClass

@Entity
@IdClass(OrderID.class)
public class Order {

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

    @Id
    @ManyToOne
    @JoinColumn(name = "PRODUCT_ID")
    private Product product;

    private int orderAmount;
}


public class OrderId implements Serializable {
    private String member; // 속성명이 같아야 한다.
    private String product;

    @Override
    public boolean equals(Object o) {...}

    @Override
    public int hashCode() {...}
}


// 저장하는 코드
public void save(){
        Member member = new Member();
        member.setId("member");
        em.persist(member);

        Product product = new Product();
        product.setId("product");
        em.persist(product);

        Order order = new Order();
        order.setMember(member);
        order.setProduct(product);
        order.setOrderAmount(2);
        em.persist(order);
}

// 조회하는 코드
 public void find() {
        Order order = new Order();
        order.setMember("memberId");
        order.setProduct("productId");

        Order order = em.find(Order.class, order);

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

 

복합키를 사용하는 테이블과 비식별 관계인 테이블을 매핑할 경우

@Entity
public class OrderChild {
	
    @Id
    private String id;
    
    @ManyToOne
    @JoinColumns({
    	@JoinColumn (name = "MEMBER_ID",
        	referecedColumnName = "MEMBER_ID"),
        @JoinColumn (name = "PRODUCT_ID",
        	referecedColumnName = "PRODUCT_ID"),
    })
    private Order order;
}

 

@EmbeddedId

@IdClass에 비해 객체지향적이고 중복도 없지만 특정 상황에 JPQL이나 코드가 조금 더 길어질 수 있다.

@Entity
public class Parent {
    @Id @Column(name = "PARENT_ID")
    private String id;
    private String name;
}

@Entity
public class Child {

    @EmbeddedId
    private ChildId id;

    @MapsId("parentId") // ChildId.parentId 매핑
    @ManyToOne
    @JoinColumn(name = "PARENT_ID")
    public Parent parent;

    private String name;
}

@Embeddable
public class ChildId implements Serializable {

    public String parentId; // @MapsId("parentId")로 매핑

    @Column(name = "CHILD_ID")
    private String id;

    // equals, hashCode
}

@Entity
public class GrandChild {

    @EmbeddedId
    private GrandChildId id;

    @MapsId("childId") // GrandChildId.childId 매핑
    @ManyToOne
    @JoinColumns({
    	@JoinColumn(name = "PARENT_ID"),
        @JoinColumn(name = "CHILD_ID")
    })
    public Child child;

    private String name;
}

@Embeddable
public class GrandChildId implements Serializable {

    public ChildId childId; // @MapsId("childId")로 매핑

    @Column(name = "GRANDCHILD_ID")
    private String id;

    // equals, hashCode
}

 

복합 키와 equals(), hashCode()

 영속성 컨텍스트는 엔티티의 식별자를 키로 사용해서 엔티티를 관리한다. 그리고 식별자를 비교할 때 equlas()와 hashCode()를 사용한다. 따라서 식별자 객체의 동등성(equals 비교)이 지켜지지 않으면 예상과 다른 엔티티가 조회되거나 엔티티를 찾을 수 없는 등 영속성 컨텍스트가 엔티티를 관리하는 데 심각한 문제가 발생한다. 

 복합키 엔티티의 equals() 를 오버라이드 하지 않았다면 Object 클래스의 equals()를 사용해서 인스턴스를 비교하는데 Object.equals 는 기본적으로 인스턴스 참조 값만을 비교한다. 

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

@MappedSuperclass  (0) 2023.06.17
상속 관계 매핑 (슈퍼-서브 타입 모델링)  (0) 2023.06.17
@ManyToMany  (0) 2023.06.16
@OneToOne  (0) 2023.06.16
@OneToMany  (0) 2023.06.12