JPA

JPA에서 @Embedded로 복합키 매핑하기

요약

  1. @Embeddable를 붙인 복합키 클래스를 만든다
  2. 엔티티 클래스에 @EmbeddedId를 통해 복합키 필드를 만든다.
  3. 이때 엔티티 클래스의 모든 매개변수를 받는 생성자를 만든다.
    이때 복합키 필드는 new로 객체를 초기화해준다.

문제 상황

챌린지 테이블과 태그 테이블이 다대다 관계인 상황이다.
이때 태그 테이블은 이름이 기본키이고, 챌린지 테이블은 챌린지 ID가 기본키로 가진다.

우리는 챌린지 테이블과 태그 테이블 사이에 매핑 테이블을 도입해서 다대일 관계 두 개로 분리해서 사용하기로 했다.
챌린지 -(일대다)- 매핑 테이블 -(다대일)- 태그

이때 매핑 테이블은 기본키로 태그의 이름과 챌린지의 챌린지아이디를 가진다.(즉 복합키이다.)
이를 어떻게 JPA로 표현할까?

관련 코드

해결방법

@EmbeddedId를 도입해서 복합키를 모은 클래스를 따로 만들어서 해결한다.

ChallengeTag(매핑 테이블)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Builder
@NoArgsConstructor
@Setter
@Getter
@Entity
public class ChallengeTag {
@EmbeddedId
private ChallengeTagPK challengeTagPK;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "challenge_id", nullable = false, insertable = false, foreignKey = @ForeignKey(ConstraintMode.CONSTRAINT))
@MapsId("challengeId")
private Challenge challenge;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "name", nullable = false, insertable = false, foreignKey = @ForeignKey(ConstraintMode.CONSTRAINT))
@MapsId("name")
private Tag tag;

public ChallengeTag(ChallengeTagPK challengeTagPK, Challenge challenge, Tag tag) {
this.challengeTagPK = new ChallengeTagPK();
this.challenge = challenge;
this.tag = tag;
}
}

ChallengeTagPK라는 필드를 만들어서 @EmbeddedId를 붙여줬다. 이러면 기본키를 복합키를 필드로 가지는 객체로 설정 할 수 있다.

이때 주의할 점 몇가지 알아보자.

1. 복합키에 따른 객체를 선언해줘야 한다.

  • challengeId와 name을 복합키로 가지므로, Challenge 객체와 Tag 객체를 멤버로 가져야 한다.
  • 각 객체는 @MapsId(“필드명”)으로 연결해줘야 한다. (괄호 안 이름은 ChallengeTagPK의 필드명이다.)

2. 모든 필드를 매개변수로 받는 생성자를 선언해야 한다.

  • 이때 기본키를 담당하는 객체는 new로 초기화 한다!
  • @AllArgsConstructor로 하지 않는다.

ChallengeTagPK(기본키 클래스)

1
2
3
4
5
6
7
8
9
10
11
12
13
@EqualsAndHashCode
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Embeddable
public class ChallengeTagPK implements Serializable {

private Long challengeId;

private String name;

}

@Embeddable을 붙이면 복합키를 표현하는 클래스이다.

몇 가지 중요한 점을 살펴보자.

1. Setter 메서드를 선언해줘야 한다.

  • 나중에 이 클래스의 필드를 challenge와 tag의 기본키를 가져와 초기화한다.

2. Serializable을 구현해야 한다.

  • Serializable은 직렬화가 가능하다는 의미이다.
    • 자바 객체를 외부 자바 시스템에서도 사용할 수 있도록 byte 형태로 데이터를 변환하는게 직렬화이다.
      (자바 객체를 바이트로 만들면 다른 자바 시스템에서 Reader로 읽어들일 수 있다.)

3. equals와 hashcode를 오버라이딩 해줘야 한다.

  • 기본키는 식별할 수 있도록 해주는 역할이다.
  • @Embeddable이 붙은 객체는 각 객체들과 구분되는 기준이 명확해야 한다.
    이 기준을 equals와 hashcode를 오버라이딩해서 해결한다.
  • 롬복으로 @EqualsAndHashCode 를 붙여주면 쉽게 해결 가능하다.
Share