JPA composite key mapping 방식 - @EmbeddedId,@IdClass
2021-12-27 19:26:22
복합키 매핑 방법
JPA는 복합키 매핑을 다음과 같은 2가지 annotation으로 지원해준다.
1.@IdClass 2.@Embeddable
복합키를 매핑하는 class의 전제조건
- composite primary key class 는 public 이여야만 한다.
- no-arg constructor 가 존재해야만 한다.
- equals,hashCode method를 overriding해야만 한다.
- Serilizable interface 를 구현해야 한다.
@IdClass
@IdClass 방식은 class의 필드를 entity에도 중복 보관한다.
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@Builder
@IdClass(UserId.class)
public class User {
@Id
private String userId;
@Id
private String password;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
private LocalDateTime deletedAt;
}
@AllArgsConstructor annotation이 entity에 추가된 이유는 lombok 빌더 떄문이다.(@Builder) 빌더 패턴은 모든 멤버변수를 생성자로 받을 수 있어야 한다, 자세한 내용은 이펙티브 자바 , builder pattern 내부 구현을 보면 알수 있는데 간략하게 정리하면 아래와 같이 빌더내 멤버변수를 생성하고자 하는 객체에 넘겨줄떄 생성자가 필요하기 떄문이다. 또는 빌더 자체를 넘겨주어서 빌더로부터 필드를 가져오는 방식도 존재한다.
// lombok builder 구현방식
public build(){
new User(builder.userId , builder.password ... );
}
User{
//builder 자체를 넘겨주는 방식
User(Builder builder){
this.userId = builder.getUserId;
}
public static class Builder{
private String userId;
public Builder userId(){
this.userId = userId;
return this;
}
//...
public build(){
new User(builder);
}
}
}
UserId를 보면 복합키에 사용될 필드를 보관하고 있고, serializable interface를 구현하고 equals & hascode를 overriding했다.
@EqualsAndHashCode
@AllArgsConstructor
@NoArgsConstructor
@Getter
public class UserId implements Serializable {
private String userId;
private String password;
}
repository 에서는 복합키 클래스를 Id Class로 명시해주면 된다.
@Repository
public interface UserRepository extends JpaRepository<User, UserId> {}
이를 테스트해보면 정상작동여부를 확인할 수 있다.
@Test
@DisplayName("@IdClass 정상작동여부 확인")
public void user(){
//given
UserId userId = new UserId("tester","1234");
User user = new User.UserBuilder()
.userId(userId.getUserId())
.password(userId.getPassword())
.createdAt(LocalDateTime.now())
.build();
User savedUser = userRepository.save(user);
//when
Optional<User> foundUser = userRepository.findById(userId);
//then
Assertions.assertThat(foundUser.isPresent());
Assertions.assertThat(foundUser.get() ).isEqualTo(savedUser);
}
@EmbeddedClass
@EmbeddedClass 방식은 필드를 복합키 클래스에서만 보관하고, entity에는 복합키 클래스만을 보관한다.
@EqualsAndHashCode
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Embeddable //추가
public class UserId implements Serializable {
private String userId;
private String password;
}
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@Builder
public class User {
@EmbeddedId //추가
private UserId userId;
}
이를 테스트해보면 마찬가지로 정상작동여부를 확인할수 있다.
@Test
@DisplayName("@EmbeddedId 정상작동여부 확인")
public void user(){
//given
UserId userId = new UserId("tester","1234");
User user = new User.UserBuilder()
.userId(userId)
.createdAt(LocalDateTime.now())
.build();
User savedUser = userRepository.save(user);
//when
Optional<User> foundUser = userRepository.findById(userId);
//then
Assertions.assertThat(foundUser.isPresent());
Assertions.assertThat(foundUser.get() ).isEqualTo(savedUser);
}
@IdClass와 @EmbeddedClass간의 차이점
@EmbeddedClass에서는 entity class에 복합키 클래스만 명시해주는 반면, @IdClass는 복합키 클래스의 필드를 중복으로 다시 명시해주어야 한다 jpql query가 나갈떄 차이점도 존재한다. @IdClass
SELECT user.userId,user.password from User user
이와달리 @EmbeddedClass에서는 추가적인 alias가 사용되어 jpql query가 나간다.
SELECT user.userId.id , user.userId.password from User user
댓글
이 게시글에 대한 의견을 공유해주세요!
