JPA

JPA Dirty Checking 시 update query 분석 (+ 모든 필드가 업데이트 되는 이유)

1. 알고자 하는 것

- JPA의 Dirty Checking 시 발생하는 update query를 확인한다.

- 수정된 필드만 update query를 날리는 것이 아닌, 모든 필드에 대해 update query가 나가는 이유를 분석한다.

 

2. 알게된 것

JPA의 Dirty Checking 시 발생하는 update query를 확인

- JPA의 Dirty Checking(변경 감지)은 트랜잭션 내에서 Entity의 변경사항을 DB에 자동으로 반영해주는 기능이다.

- 영속성 컨텍스트 내에서 최초 조회 시의 Entity에 대한 스냅샷을 보관하고

- 트랜잭션 커밋 시 EntityManager가 flush를 수행할 때, 스냅샷과 Entity를 비교한다.

- 변경 내용에 대해 쓰기 지연 SQL 저장소에 update query를 저장한다.

- query를 DB에 날리고 트랜잭션을 커밋한다.

- 이러한 Dirty Checking 기능을 사용할 때 발생하는 update query를 확인해본다.

 

- 사용한 Entity는 다음과 같다.

@Getter
@Setter
@NoArgsConstructor
@Entity
public class Member {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    private Integer age;

    public Member(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
}

 

- 다음과 같은 update 메서드를 통해 member의 값 중 나이(age)는 변경하지 않고, 이름(name) 필드만 변경한다.

- 이 때 Dirty Checking이 동작한다.

@Transactional
public void update(Long memberId) {
    // find member (in persistence context)
    Member member = memberRepository.findById(memberId)
            .orElseThrow(() -> new IllegalArgumentException("member not found"));

    // dirty checking
    member.setName("change");
}

 

- 다음과 같이 변경된 필드에 대해서만 update query를 날릴 것이라고 예상했다.

update member set name='change' where id=1

- 실제로 나간 update query는 다음과 같이 모든 필드에 대해 update query가 나간다.

Hibernate: 
    update
        member 
    set
        age=?,
        name=? 
    where
        id=?

 

수정된 필드만 update query를 날리는 것이 아닌, 모든 필드에 대해 update query가 나가는 이유

- 모든 필드에 대해 update query를 날리면 DB에 보내는 데이터 전송량이 증가한다.

- 그럼에도 모든 필드에 대해 update query를 날릴 때의 장점은 다음과 같다.

- 모든 필드에 대해 update query를 수행하면 변경되는 필드에 관계없이 동일한 update query를 만들어 둘 수 있다.

- 이로 인해 JPA는 애플리케이션 로딩 시점에 update query를 미리 생성해두고 재사용이 가능해 성능상 이점을 가진다.

- DB 입장에서도 동일한 쿼리에 대해 이전에 파싱된 쿼리를 재사용이 가능하다.

 

- 만약 Entity가 가지는 필드의 수가 너무 많아 데이터 전송량 자체가 성능 문제를 일으킬 수 있는 경우

- DynamicUpdate 어노테이션을 사용해 수정된 데이터만 동적으로 update query를 날릴 수 있다.

@DynamicUpdate // 수정된 필드에 대해 동적으로 update query 생성
@Entity
public class Member {

 

Hibernate: 
    update
        member 
    set
        name=? 
    where
        id=?

 

- 책에서 권장하는 DynamicUpdate 사용 시 성능향상 기준은 컬럼 30개 이상이라고 한다.

- 하지만 컬럼이 30개 이상이라면 DB 설계 상의 문제가 있을 수도 있음을 의미한다.

- DynamicUpdate를 사용해야 할 상황이라면, 테이블 설계 확인 및 책임 분리를 먼저 고려해보자.

 

3. 정리

- JPA Dirty Checking 기능은 모든 필드에 대해 update query가 나간다.

- 이는 모든 수정사항 경우에 대해 동일한 update query를 재사용 가능하게 하여 성능상 이점을 가지기 위함이다.

- 만약 동적으로 update query를 날리고 싶다면, DynamicUpdate 어노테이션을 Entity Class에 붙이자.

- 하지만 DynamicUpdate 어노테이션이 필요할만큼 필드 수가 많다면 테이블 설계 확인 및 책임 분리를 먼저 고려하자.

 


Source Code

https://github.com/HunSeongPark/jpa-playground/tree/main/src/main/java/com/example/jpa_playground/dirty_checking

 

GitHub - HunSeongPark/jpa-playground: JPA를 학습하고 분석하는 놀이터 🛝

JPA를 학습하고 분석하는 놀이터 🛝. Contribute to HunSeongPark/jpa-playground development by creating an account on GitHub.

github.com

 

Reference

자바 ORM 표준 JPA 프로그래밍