EmbeddedID로 복수의 컬럼을 PK로 하기

6/12/2024


EmbeddedId

@EmbeddedID는 엔티티의 PK를 기본형 타입이 아닌 다른 클래스로 지정할 때 사용하는 것이다. 이렇게 함으로써 얻을 수 있는 장점은 여러가지가 있는데, PK를 위한 기능을 엔티티가 아니라 PK 클래스 자체에 구현할 수 있다는 점, 그리고 복수의 컬럼을 PK로 지정할 수 있다는 점 등이 있다.
그 중에서 복수의 컬럼을 PK로 하는 케이스에 대해서 서술해본다.
필자는 프로젝트 중에서 다음과 같은 엔티티 케이스가 있었다.

ItemCategoryItemCategoryOrder
iditem_category_id
nameuser_id
parentIdorder_num
familyId
isRoot

위 표에서 ItemCategoryOrder 엔티티는 ItemCategory의 id와 User의 id를 복합키로 하고 있다.
ItemCategory는 family의 모두가 공유하는 데이터가 되고, family에 소속된 user들은 각자 개별적인 ItemCategoryOrder를 갖는다.
(이러한 관계로 ItemCategory와 ItemCategoryOrder는 서로 다른 라이프사이클을 갖는다고 판단했고 ItemCategoryOrder는 ItemCategory를 정의할 때 관여하지 않기에 서로 다른 엔티티로 구성해보았다.)
그러면 ItemCategoryOrder를 살펴보자.

복합키를 갖는 엔티티

ItemCategoryOrder는 아래와 같이 작성되어 있다.

@Entity(name = "itemCategoryOrder")
@Table(name = "ITEM_CATEGORY_ORDER")
@NoArgsConstructor
@ToString
@Getter
@DynamicUpdate
public class ItemCategoryOrder {
    @EmbeddedId
    private ItemCategoryOrderID id;

    private int orderNum;

    @Builder()
    public ItemCategoryOrder(long userId, long categoryId, int orderNum){
        this.id = new ItemCategoryOrderID(userId, categoryId);
        this.orderNum = orderNum;
    }
}

위 코드에서 ItemCategoryOrder의 id를 ItemCategoryOrderID 클래스로 정의하였고, @EmbeddedId 어노테이션을 붙였다.

@EmbeddedId
private ItemCategoryOrderID id;

ItemCategoryOrderID 클래스를 살펴보면 아래와 같다.

@Embeddable
@AllArgsConstructor
@NoArgsConstructor
@ToString
@EqualsAndHashCode
public class ItemCategoryOrderID implements Serializable {
    @Column(name="userId")
    private long userId;
    @Column(name="itemCategoryId")
    private long itemCategoryId;
}

ItemCategoryOrderID클래스에는 @Embeddable 이라는 어노테이션을 붙였고, 복합키로 지정될 컬럼 userId와 itemCategoryId가 이곳에 위치한 것을 볼 수 있다.
이 외에 @EmbeddedId를 적용한 클래스는 아래의 조건을 만족해야 한다.

  • Serializable 인터페이스를 구현해야 한다.
  • 기본 생성자가 있어야 한다.
  • public 이어야 한다.

hibernate의 create ddl이 다음과 같이 실행 된다.

create table item_category_order (
    order_num integer not null,
    item_category_id bigint not null,
    user_id bigint not null,
    primary key (item_category_id, user_id)
)

리포지터리에서

복합키를 id로 하여 조회할 땐 ItemCategoryOrderID 클래스 인스턴스를 만들어서 전달하면 된다.

ItemCategoryOrderID orderId = new ItemCategoryOrderID(1,1)
entityManager.find(ItemCategoryOrder.class, orderId);

JpaRepository를 만들 때도 id의 타입 부분을 EmbeddedId 클래스로 써주면 된다.

public interface ItemCategoryOrderRepository extends JpaRepository<ItemCategoryOrder, ItemCategoryOrderID> {}

조인 할 때

ItemCategoryOrder는 요구사항에 따르면 ItemCategory 목록을 조회할 때, 그 순서에 사용된다. 따라서 select문에서는 ItemCategoryOrder를 조인하여 해당 ItemCategory의 order를 찾아야 하는데, 조인할 ItemCategoryOrder의 컬럼이 복합키 중에 하나인 itemCategoryId이다. Embedded된 컬럼을 JPQL 등에서 사용할 때는 해당 변수명.컬럼명으로 접근할 수 있다.
예를들어 ItemCategoryOrder의 itemCategoryId는 itemCategoryOrder.id.itemCategoryId 로 접근할 수 있는 것이다. 따라서 조인하는 select문의 예시는 다음과 같다

---JPQL
select new com.seaweed.hm.domain.item.dto.ItemCategoryDTO(
   a.id, a.name, a.familyId, a.parentId, a.isRoot
)
from itemCategory as a
  left outer join itemCategoryOrder as b on a.id = b.id.itemCategoryId and b.id.userId = :userId
where a.familyId = :familyId
order by b.orderNum

develop

Inhyeok Kim

Email : inhyeok.kim@icloud.com

GithubPortfolio