[JPA] PK를 equals와 hashcode의 기준으로 사용할 경우, Set 사용을 주의하자

유알·2023년 8월 17일
1

[DB/JPA]

목록 보기
6/7

테스트 코드를 작성하다가 이상한 점을 발견했다.

@Entity @Table(name = "token")
//...
@EqualsAndHashCode(of = "id")
//...
public abstract class CommonTokenEntity {
	
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "token_id")
    private Long id;
    
    //...

이러한 엔티티가 정의되어 있다고 하자. pk 값을 기준으로 equals와 hashcode를 구현하도록 하였다.

@Entity @Table(name = "oauth2_authorization")
@Getter
@Setter @EqualsAndHashCode(of = "id")
@NoArgsConstructor
public class OAuth2AuthorizationEntity {

	//...

    @OneToMany(fetch = FetchType.EAGER, mappedBy = "authorization",cascade = CascadeType.ALL, orphanRemoval = true)
    private Set<CommonTokenEntity> tokens = new HashSet<>();
    
    //...
    
    public void addToken(CommonTokenEntity token) {
        this.tokens.add(token);
        token.setAuthorization(this);
    }
}

보면, cascade의 persist가 존재하기 때문에, 토큰을 등록하기만 하면, persist를 대신 호출해주는 구조이다.

하지만 여기서 문제가 발생한다.
1. 일단 토큰의 id는 generatedValue 이기 때문에, persist를 호출하기 전까지는 null로 존재한다.
2. 그런데 persist는 일단 Set에 삽입이 된 다음 호출된다.
3. Set은 equals 를 통해 같은 객체는 삽입하지 않는다.
4. 여러개의 신규 토큰을 삽입하는 경우, Set에 의해 한개를 제외하고는 삽입이 되지 않는다. (모두 id 가 null 이기 때문)

열심히 디버깅을 하며 찾아냈다.

이는 상당히 위험한 상황으로 보인다. 다음과 같은 상황에서 발생하는데,
1. Set을통해 OneToMany를 받고
2. cascade.persist를 통해 persist가 호출된다고 가정한 상태이고
3. equals를 pk를 통해 체크하는 경우

이렇게 코드를 짜면, 예상치 못한 곳에서 db에 저장이 안되는 당황스러운 상황이 발생한다.

내가 생각한 해결책은 다음과 같다.
1. equals의 기준을 pk 뿐만이 아니라, 비즈니스 키를 포함해서 정의한다. 예를 들어 토큰의 경우, 토큰 타입과 토큰 밸류 정도?
2. Set을 사용하지 않는다.(그게 좋아 보이긴한다.)

profile
더 좋은 구조를 고민하는 개발자 입니다

0개의 댓글