복합키 클래스( @IdClass )
@IdClass
를 사용한다는 것은 엔티티에 복합키(두 개 이상의 필드로 이루어진 기본 키)를 사용하겠다는 뜻입니다. 이를 위해서는 몇 가지 규칙을 따라야 합니다.
Serializable
인터페이스 구현:
복합키 클래스를 JPA에서 식별자로 사용하려면, 이 클래스가 직렬화될 수 있어야 합니다. 그래서 복합키 클래스를Serializable
인터페이스로 구현해야 합니다. 이렇게 하면 JPA가 이 복합키를 데이터베이스에 저장하고 복원하는 과정에서 직렬화/역직렬화를 할 수 있게 됩니다.hashCode()
와equals()
메서드 구현:
복합키 클래스에서는hashCode()
와equals()
메서드를 반드시 구현해야 합니다. JPA는 엔티티를 관리할 때 엔티티의 식별자를 비교하고, 그 과정에서 복합키의 두 개 이상의 필드를 비교할 수 있어야 하므로 이 메서드들을 구현해야 합니다.hashCode()
: 객체를 해시 기반 컬렉션에 저장하거나 검색할 때 사용되는 메서드로, 두 개의 복합키 객체가 같은 값을 가질 때 동일한 해시 코드를 반환해야 합니다.equals()
: 두 객체가 같은지 비교하는 메서드로, 복합키의 각 필드가 같으면 두 객체는 동일하다고 판단되어야 합니다.
예시 코드
- 복합키 클래스 (
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);
}
}
- 엔티티 클래스 (복합키 사용):
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의 규칙
- 복합키 클래스 정의:
@IdClass
처럼 복합키 클래스는Serializable
인터페이스를 구현해야 합니다. 그리고hashCode()
와equals()
메서드를 구현해야 JPA가 이 클래스를 기본 키로 사용할 수 있습니다. - @EmbeddedId로 엔티티에 포함:
복합키 클래스를 엔티티의 필드로 선언하고, 해당 필드에@EmbeddedId
를 붙입니다. 이 필드가 복합키 역할을 하게 됩니다. - 엔티티 필드에서 복합키 접근:
복합키 클래스의 필드들은 엔티티 필드처럼 접근할 수 있으며, JPA가 이를 자동으로 매핑합니다.
예시 코드
- 복합키 클래스 (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);
}
}
- 엔티티 클래스 (복합키 사용)
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의 차이점
- 구조적인 차이:
@IdClass
는 복합키 클래스가 엔티티와 별도로 존재하며, 엔티티 클래스에 각각의 필드가@Id
로 선언됩니다.@EmbeddedId
는 복합키 클래스가 엔티티의 필드로 내장되어 있고, 이 필드에@EmbeddedId
가 붙습니다.
- 사용 방식:
@IdClass
는 각 필드에 대해 직접@Id
를 붙여서 사용합니다.@EmbeddedId
는 복합키 클래스를 하나의 필드로 사용하여 관리합니다.
- 코드의 간결성:
@EmbeddedId
는 코드가 더 간결할 수 있으며, 복합키의 필드를 하나의 객체로 묶어서 처리할 수 있습니다.
요약
@EmbeddedId
는 복합키 클래스를 엔티티 내에 포함시켜, 이 클래스를 엔티티의 식별자로 사용하는 방식입니다.- 복합키 클래스는
Serializable
인터페이스를 구현해야 하고,hashCode()
와equals()
메서드를 구현해야 합니다. @IdClass
와 달리, 복합키 클래스를 필드로 포함하여 내장하는 방식이므로, 코드가 더 직관적일 수 있습니다.
@IdClass
와 @EmbeddedId
는 둘 다 JPA에서 복합키(Composite Key)를 처리하기 위한 방식이지만, 구조와 사용 방식에서 몇 가지 차이점이 있습니다. 각 방식의 차이점과 장단점을 정리해보면 다음과 같습니다.
1. 구조적인 차이
@IdClass
:- 엔티티 클래스에서 복합키를 구성하는 각 필드를 별도로
@Id
로 선언합니다. - 복합키 클래스를 따로 만들어서 엔티티 클래스에 포함시키지 않고, 엔티티 클래스에서 직접 복합키의 각 필드를 관리합니다.
- 엔티티에 있는 필드들이 복합키 클래스로 연결됩니다.
- 엔티티 클래스에서 복합키를 구성하는 각 필드를 별도로
@EmbeddedId
:- 복합키를 하나의 객체로 엔티티 클래스 안에 내장합니다.
- 복합키 자체를 필드로 선언하고, 이 필드에
@EmbeddedId
를 붙입니다. - 복합키를 구성하는 필드들이 복합키 클래스 안에 모여있으며, 엔티티 클래스에서는 이 복합키 객체만 관리합니다.
2. 사용 방식
@IdClass
:- 복합키를 사용하는 필드들이 엔티티 클래스에 그대로 정의됩니다.
@IdClass
로 지정된 클래스를 사용하여 필드들을 묶어 복합키로 만듭니다.- 엔티티 내에서 각 필드를 별도로 관리하고, 복합키 클래스를 엔티티 외부에서 사용합니다.
@EmbeddedId
:- 복합키 클래스는 엔티티 클래스에 직접 포함되어 있으며, 하나의 필드로 취급됩니다.
- 복합키 클래스의 필드를 엔티티 내에서 직접 사용할 필요 없이, 복합키 필드를 통해 접근합니다.
- 엔티티는 복합키 객체를 관리하는 방식으로 구성됩니다.
3. 필드 접근
@IdClass
:- 엔티티 클래스에서 각 필드를 직접 선언하므로 필드에 직접 접근할 수 있습니다.
- 예를 들어
orderId
나productId
같은 필드를 엔티티 안에서 직접 다룹니다.
@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
는 더 명시적이고 각 필드에 대한 제어가 가능합니다.
둘 중 어떤 것을 사용할지는 프로젝트 요구 사항과 코드의 가독성 및 유지보수성을 고려하여 선택하면 됩니다.
'Study Memo' 카테고리의 다른 글
2024.09.20 (금) { WHERE 절, HAVING 절, GROUP BY, ORDER BY } (0) | 2024.09.23 |
---|---|
2024.09.19(목) { 컬렉션 값 타입, CROSS JOIN } (0) | 2024.09.19 |
2024.09.11(수) { 지연 로딩&즉시 로딩, n+1 문제, 다이나믹 업데이트, 알아야할 것들 } (0) | 2024.09.11 |
2024.09.10(화) { flush(), dirty, 스냅샷, 데이터베이스 정규화 } (0) | 2024.09.10 |
2024.09.09(월) { @Transactional, RDBMS와 NoSQL, DDL, Validate } (0) | 2024.09.09 |