사이드 프로젝트(8) - JPA의 save() 중 select가 발생하는 이유(feat : 기본키 변경)

김정훈·2024년 7월 25일
0

우선 Match 대한 간략한 소개입니다 : https://velog.io/@kjh1232100/%EC%82%AC%EC%9D%B4%EB%93%9C-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B86-Match-%EC%A0%95%EB%B3%B4-%EA%B4%80%EB%A6%AC
SummonerInfo를 저장하기 위해 save()를 호출했는데 연관관계인 match를 select 하는 현상이 발생했습니다. 불필요한 쿼리를 해결하기 위해 원인을 파악하기로 했습니다.

JPA의 save() 메소드와 select 쿼리 발생 원인

CrudRepository의 save를 SimpleRepository에서 구현한 코드이다.

1. 엔티티의 상태 확인

JPA에서 save() 메소드는 내부적으로 EntityManager.merge()를 호출하여 엔티티를 저장한다. merge() 과정에서 영속상태였던 객체의 @Id가 변경됐다면 해당 객체의 유효성을 검사하기 위해 select 쿼리를 실행하여 DB에서 객체를 불러온다. 이는 DB에 있는 엔티티의 현재 상태와 애플리케이션에서 제공된 엔티티 상태를 비교하기 위해 필요한 절차이다.

2. 변경 감지 (Dirty Checking)

만약 영속상태에서 객체가 유효하다면 JPA는 merge()를 통해 엔티티의 변경된 부분을 탐지하고 해당 변경사항만 데이터베이스에 반영한다. 이 과정을 '변경 감지'라고 하며, 영속성 컨텍스트가 엔티티의 모든 변경을 추적하여 데이터베이스와 동기화를 맞추는 역할을 한다.

3. 엔티티 신규 여부 판단 (isNew())

EntityManager.merge() 호출 시, JPA는 엔티티가 새로운 것인지, 즉 데이터베이스에 이미 존재하는 엔티티인지를 판단한다. 이 판단은 주로 엔티티의 @Id 필드를 검사하여 결정되며, Id가 할당되지 않았거나 초기값을 가지고 있다면, 새로운 엔티티로 간주하고 persist() 작업이 수행된다. 기존의 Id를 가진 엔티티는 merge()를 통해 기존 데이터와 합병된다.

요약

JPA에서 save() 메소드는 영속성 컨텍스트를 사용하여 엔티티의 초기 상태를 스냅샷으로 저장하고, isNew()를 통해 엔티티의 @Id 값이 null이거나 초기값일 경우 true를 반환하여 persist()로 새로 저장한다. isNew()false를 반환하면, merge()가 호출되어, @Id의 변경 여부에 따라 데이터베이스에서 엔티티를 조회하거나, 필드 값 변경만 감지된 경우 '더티 체킹'을 통해 필요한 필드만 업데이트한다. 이 과정은 데이터의 일관성무결성을 유지하며 애플리케이션과 데이터베이스 간의 동기화를 보장한다.

그렇다면 내 코드에서 왜 select가 발생했을까?

@Id 변경

앞서 언급했듯이 JPA는 객체를 @Id를 통해 관리한다. 이 ID가 변경됐다면 객체에 접근할 수가 없으니 문제가 된다.

public class Match implements Persistable<String> {
    @Id
    private String matchId;
    private Long gameStartTimestamp;
    private Long kills;
    private Long deaths;
    private Long assists;
    private String kda;
    }

내 Match 엔티티의 @Id는 실제 match의 Id 정보로 update를 하면 경기 정보가 변경되기 때문에 Id도 바꿔 이런 문제가 발생한 것이다.

해결

public class Match implements Persistable<String> {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String matchId;
    private Long gameStartTimestamp;
    private Long kills;
    private Long deaths;
    }

기본키를 새롭게 만들었다. match 객체를 사용할 때 matchId가 필요하기 때문에 따로 기본키가 필요 없다고 생각했었는데 JPA를 적절하게 활용하기 위해서라도 기본키는 따로 유지하는게 맞는 것 같다.

profile
백엔드 개발자

0개의 댓글