본문 바로가기
Everyday Study

2024.09.12(목) { 복합키 클래스( @IdClass, @EmbeddedId ) }

by xogns93 2024. 9. 12.

복합키 클래스( @IdClass )

@IdClass를 사용한다는 것은 엔티티에 복합키(두 개 이상의 필드로 이루어진 기본 키)를 사용하겠다는 뜻입니다. 이를 위해서는 몇 가지 규칙을 따라야 합니다.

  1. Serializable 인터페이스 구현:
    복합키 클래스를 JPA에서 식별자로 사용하려면, 이 클래스가 직렬화될 수 있어야 합니다. 그래서 복합키 클래스를 Serializable 인터페이스로 구현해야 합니다. 이렇게 하면 JPA가 이 복합키를 데이터베이스에 저장하고 복원하는 과정에서 직렬화/역직렬화를 할 수 있게 됩니다.
  2. hashCode()equals() 메서드 구현:
    복합키 클래스에서는 hashCode()equals() 메서드를 반드시 구현해야 합니다. JPA는 엔티티를 관리할 때 엔티티의 식별자를 비교하고, 그 과정에서 복합키의 두 개 이상의 필드를 비교할 수 있어야 하므로 이 메서드들을 구현해야 합니다.
    • hashCode(): 객체를 해시 기반 컬렉션에 저장하거나 검색할 때 사용되는 메서드로, 두 개의 복합키 객체가 같은 값을 가질 때 동일한 해시 코드를 반환해야 합니다.
    • equals(): 두 객체가 같은지 비교하는 메서드로, 복합키의 각 필드가 같으면 두 객체는 동일하다고 판단되어야 합니다.

예시 코드

  1. 복합키 클래스 (Serializable 구현, hashCode()equals() 메서드 포함):
import java.io.Serializable;
import java.util.Objects;

public class OrderId implements Serializable {

    private Long orderId;
    private Long productId;

    // 기본 생성자 필요
    public OrderId() {}

    public OrderId(Long orderId, Long productId) {
        this.orderId = orderId;
        this.productId = productId;
    }

    // hashCode() 메서드 구현
    @Override
    public int hashCode() {
        return Objects.hash(orderId, productId);
    }

    // equals() 메서드 구현
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        OrderId that = (OrderId) obj;
        return Objects.equals(orderId, that.orderId) &&
               Objects.equals(productId, that.productId);
    }
}
  1. 엔티티 클래스 (복합키 사용):
import javax.persistence.*;

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

    @Id
    private Long orderId;

    @Id
    private Long productId;

    private int quantity;

    // 기본 생성자, 게터 및 세터
}

요약

  • @IdClass를 사용하면 복합키를 만들 수 있습니다.
  • 복합키 클래스를 Serializable로 구현해야 하고, hashCode()equals()를 적절히 구현해야 JPA가 엔티티를 올바르게 관리할 수 있습니다.

@EmbeddedId@IdClass와 마찬가지로 복합키(Composite Key)를 처리하는 방식 중 하나입니다. 그러나 @EmbeddedId는 약간 다른 접근 방식을 사용하여 복합키를 관리합니다. @EmbeddedId는 복합키를 별도의 클래스로 만들어 그것을 엔티티 안에 내장(Embed)하는 방식입니다.

@EmbeddedId 방식

@EmbeddedId를 사용하면 복합키 클래스 자체를 엔티티의 필드로 포함시켜, 이 필드가 엔티티의 복합키로 사용됩니다. 이를 통해 엔티티와 키 클래스 간의 명확한 매핑을 설정할 수 있습니다.

@EmbeddedId의 규칙

  1. 복합키 클래스 정의:
    @IdClass처럼 복합키 클래스는 Serializable 인터페이스를 구현해야 합니다. 그리고 hashCode()equals() 메서드를 구현해야 JPA가 이 클래스를 기본 키로 사용할 수 있습니다.
  2. @EmbeddedId로 엔티티에 포함:
    복합키 클래스를 엔티티의 필드로 선언하고, 해당 필드에 @EmbeddedId를 붙입니다. 이 필드가 복합키 역할을 하게 됩니다.
  3. 엔티티 필드에서 복합키 접근:
    복합키 클래스의 필드들은 엔티티 필드처럼 접근할 수 있으며, JPA가 이를 자동으로 매핑합니다.

예시 코드

  1. 복합키 클래스 (Serializable 구현, hashCode()와 equals() 포함)
import java.io.Serializable;
import java.util.Objects;

// 복합키 클래스를 Serializable로 구현
public class OrderId implements Serializable {

    private Long orderId;
    private Long productId;

    // 기본 생성자 필요
    public OrderId() {}

    public OrderId(Long orderId, Long productId) {
        this.orderId = orderId;
        this.productId = productId;
    }

    // hashCode() 메서드 구현
    @Override
    public int hashCode() {
        return Objects.hash(orderId, productId);
    }

    // equals() 메서드 구현
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        OrderId that = (OrderId) obj;
        return Objects.equals(orderId, that.orderId) &&
               Objects.equals(productId, that.productId);
    }
}
  1. 엔티티 클래스 (복합키 사용)
import javax.persistence.*;

@Entity
public class Order {

    @EmbeddedId  // 복합키 필드를 내장
    private OrderId id;  // 복합키 필드

    private int quantity;

    // 기본 생성자, 게터 및 세터
    public Order() {}

    public Order(OrderId id, int quantity) {
        this.id = id;
        this.quantity = quantity;
    }

    public OrderId getId() {
        return id;
    }

    public void setId(OrderId id) {
        this.id = id;
    }

    public int getQuantity() {
        return quantity;
    }

    public void setQuantity(int quantity) {
        this.quantity = quantity;
    }
}

@IdClass와 @EmbeddedId의 차이점

  1. 구조적인 차이:
    • @IdClass는 복합키 클래스가 엔티티와 별도로 존재하며, 엔티티 클래스에 각각의 필드가 @Id로 선언됩니다.
    • @EmbeddedId는 복합키 클래스가 엔티티의 필드로 내장되어 있고, 이 필드에 @EmbeddedId가 붙습니다.
  2. 사용 방식:
    • @IdClass는 각 필드에 대해 직접 @Id를 붙여서 사용합니다.
    • @EmbeddedId는 복합키 클래스를 하나의 필드로 사용하여 관리합니다.
  3. 코드의 간결성:
    • @EmbeddedId는 코드가 더 간결할 수 있으며, 복합키의 필드를 하나의 객체로 묶어서 처리할 수 있습니다.

요약

  • @EmbeddedId는 복합키 클래스를 엔티티 내에 포함시켜, 이 클래스를 엔티티의 식별자로 사용하는 방식입니다.
  • 복합키 클래스는 Serializable 인터페이스를 구현해야 하고, hashCode()equals() 메서드를 구현해야 합니다.
  • @IdClass와 달리, 복합키 클래스를 필드로 포함하여 내장하는 방식이므로, 코드가 더 직관적일 수 있습니다.

 

@IdClass@EmbeddedId는 둘 다 JPA에서 복합키(Composite Key)를 처리하기 위한 방식이지만, 구조와 사용 방식에서 몇 가지 차이점이 있습니다. 각 방식의 차이점과 장단점을 정리해보면 다음과 같습니다.

1. 구조적인 차이

  • @IdClass:
    • 엔티티 클래스에서 복합키를 구성하는 각 필드를 별도로 @Id로 선언합니다.
    • 복합키 클래스를 따로 만들어서 엔티티 클래스에 포함시키지 않고, 엔티티 클래스에서 직접 복합키의 각 필드를 관리합니다.
    • 엔티티에 있는 필드들이 복합키 클래스로 연결됩니다.
  • @EmbeddedId:
    • 복합키를 하나의 객체로 엔티티 클래스 안에 내장합니다.
    • 복합키 자체를 필드로 선언하고, 이 필드에 @EmbeddedId를 붙입니다.
    • 복합키를 구성하는 필드들이 복합키 클래스 안에 모여있으며, 엔티티 클래스에서는 이 복합키 객체만 관리합니다.

2. 사용 방식

  • @IdClass:
    • 복합키를 사용하는 필드들이 엔티티 클래스에 그대로 정의됩니다.
    • @IdClass로 지정된 클래스를 사용하여 필드들을 묶어 복합키로 만듭니다.
    • 엔티티 내에서 각 필드를 별도로 관리하고, 복합키 클래스를 엔티티 외부에서 사용합니다.
  • @EmbeddedId:
    • 복합키 클래스는 엔티티 클래스에 직접 포함되어 있으며, 하나의 필드로 취급됩니다.
    • 복합키 클래스의 필드를 엔티티 내에서 직접 사용할 필요 없이, 복합키 필드를 통해 접근합니다.
    • 엔티티는 복합키 객체를 관리하는 방식으로 구성됩니다.

3. 필드 접근

  • @IdClass:
    • 엔티티 클래스에서 각 필드를 직접 선언하므로 필드에 직접 접근할 수 있습니다.
    • 예를 들어 orderIdproductId 같은 필드를 엔티티 안에서 직접 다룹니다.
  • @EmbeddedId:
    • 복합키 필드를 통해 복합키 내부의 필드에 접근합니다.
    • 예를 들어 order.getId().getOrderId() 식으로 복합키 객체를 통해 필드에 접근합니다.

4. 코드의 간결성

  • @IdClass:
    • 엔티티에서 복합키의 각 필드를 명시적으로 정의해야 하므로, 필드 수가 많으면 코드가 복잡해질 수 있습니다.
  • @EmbeddedId:
    • 복합키를 하나의 객체로 묶어서 관리하기 때문에 코드가 좀 더 간결하고, 복합키 필드를 객체로 다루기 때문에 엔티티의 필드 선언이 적어집니다.

5. 복합키 클래스와의 연관성

  • @IdClass:
    • 엔티티 클래스는 복합키 클래스와 필드 이름이나 타입이 일치해야 합니다.
    • 즉, 엔티티의 필드와 복합키 클래스의 필드가 별도로 선언되지만 이름과 타입이 같아야 합니다.
  • @EmbeddedId:
    • 복합키 클래스가 엔티티의 필드로 사용되므로 엔티티와 복합키 클래스의 필드가 중복되지 않습니다.

6. 장단점

  • @IdClass
    • 장점:
      • 엔티티 클래스에서 복합키의 각 필드를 직접 관리할 수 있어 더 명시적으로 보일 수 있습니다.
    • 단점:
      • 필드가 많아지면 엔티티의 코드가 복잡해지고, 필드 간의 중복이 발생할 수 있습니다.
  • @EmbeddedId
    • 장점:
      • 코드가 간결해지고 복합키 관련 로직을 하나의 객체로 모아서 관리할 수 있습니다.
      • 엔티티 클래스에서 중복 없이 복합키를 사용할 수 있습니다.
    • 단점:
      • 복합키 클래스 안에 모든 필드를 넣어 관리해야 하므로, 복합키 필드를 통해 값을 접근하는 과정이 생길 수 있습니다.

예시 비교

@IdClass 예시

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

    @Id
    private Long orderId;

    @Id
    private Long productId;

    private int quantity;

    // 엔티티에서 직접 orderId와 productId를 관리합니다.
}

@EmbeddedId 예시

@Entity
public class Order {

    @EmbeddedId
    private OrderId id;

    private int quantity;

    // id 객체를 통해 복합키를 관리합니다.
}

요약

  • @IdClass는 엔티티 클래스 내에서 각각의 필드를 직접 @Id로 관리하고, 복합키 클래스를 외부에서 연결하는 방식입니다.
  • @EmbeddedId는 복합키 클래스를 엔티티 내에 포함시켜 하나의 필드로 관리하는 방식입니다.
  • @EmbeddedId는 코드가 더 간결하고 직관적일 수 있으며, @IdClass는 더 명시적이고 각 필드에 대한 제어가 가능합니다.

둘 중 어떤 것을 사용할지는 프로젝트 요구 사항과 코드의 가독성 및 유지보수성을 고려하여 선택하면 됩니다.