[Spring] JPA Entity 복합키(2개 이상의 컬럼으로 이루어진 PK) 사용 방법

무심코·2023년 3월 9일
0

PHP로 이루어진 사내 서버를 Spring으로 이전하는 작업중 상당수의 테이블이 복합키로 이루어진 것을 확인하고 JPA에서 복합키를 Entity로 풀어낼 수 있는 방법을 찾아보았습니다.

정확하지 않고 오류가 있는 부분이 있을 수 있습니다. 구현해보시면서 오류나는 부분 있으시면 같이 공유해서 알아가면 좋을 것 같습니다!

JPA에서 복합키를 표현할 수 있는 방법

JPA에서 복합키는 두가지 방법을 표현할 수 있다고 합니다.
첫번째 방법이 @EmbededId 어노테이션을 사용하는 방법이고 다른 방법은 @IdClass를 사용하는 방법입니다.
@EmbededId@IdClass에 비해 복합키 표현을 더 객체지향적으로 해주지만 복합키로 구성된 계층(복합키 안 FK가 존재할 때)이 복잡해질수록 표현이 어려워집니다.

예를 들어 복합키 안에 복합키 또 복합키... 같이 구성된 연관관계가 존재하면 아래와 같이 복잡한 그래프 탐색을 수행하게되어 구조가 복잡해집니다.

payShop.getId().getPayDetailId().getPayId().getPayNumber();

사내 서버의 경우

사내 서버는 복합키 자체는 많은데 두 뎁스 이상의 연결이 없어 충분히 @EmbededId 방식을 사용할 수 있을 것 같았습니다.
그래서 @EmbededId 방식으로 사내 테이블중 하나인 MapTables를 구현해보았습니다.

@EmbededId

@EmbededId는 복합키를 하나의 객체로 묶은 후 해당 객체를 Entity에서 사용하는 방식으로 구현됩니다.

@Getter
@Embeddable
@NoArgsConstructor
public class MapTablesPK implements Serializable {

    @Column(name = "map_id")
    private Integer mapId;

    @Column(name = "table_id")
    private Integer tableId;


    public MapTablesPK(Integer mapId, Integer tableId) {
        this.mapId = mapId;
        this.tableId = tableId;
    }
}

다음과 같이 MapTablesPK 라는 클래스 안에 MapTables에서 사용되는 PK를 필드로 넣어줍니다.
해당 클래스는 Serializable 을 implements 하고 @Embeddable 어노테이션을 사용합니다.

이후 MapTables Entity에서는 다음과 같이 MapTablesPK 객체를 사용합니다.

@Getter
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class MapTables {

    @Id
    @EmbeddedId
    private MapTablesPK mapTablesPK;

    @MapsId(value = "mapId")
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "map_id" , referencedColumnName = "map_id")
    private Maps maps;

MapTablesPK 클래스에 @EmbededId@Id 어노테이션을 사용하여 복합키로 구성된 PK임을 명시해줍니다.

이때 MapTables의 경우 복합키에서 사용되는 컬럼(map_id, table_id) 중 map_id는 Map과 연결된 FK입니다.

이를 구현하기 위해 해당 필드(mapId)를 연관관계 맵핑(N:1) 해줘야하는데 이때 MapTablesPK에 들어가있는 mapId와 연관관계를 맵핑하지 않고 MapTables Entity안에 Maps 객체를 새로 생성하여 연관관계를 맵핑해줍니다.

그래서 @ManyToOne, @JoinColumn 어노테이션을 MapTablesPK 안 mapId 가 아닌 MapTables에 새로운 Maps를 생성 후 어노테이션을 명시해줬습니다.

여기서 기존 연관관계 맵핑과의 차이점은 @MapsId(value = "mapId") 부분입니다. 해당 어노테이션을 통해 Maps 객체의 'mapId' 라는 필드를 기준으로 FK를 연결해줄 수 있습니다.

@EmbededId 사용시 필수 조건

  • @Embeddable 어노테이션을 붙여주어야 함
  • Serializable 인터페이스 구현해야 함
  • equals, hashCode 구현해야함
  • 기본 생성자 필요
  • 식별자 클래스는 public 이어야 함

참고자료

https://techblog.woowahan.com/2595/
https://docs.jboss.org/hibernate/jpa/2.1/api/javax/persistence/MapsId.html

profile
지나치지 않기 위하여

0개의 댓글