[JPA] Dirty Checking(변경감지) & @DynamicUpdate

손효재·2022년 10월 22일
0

JPA

목록 보기
10/11
post-thumbnail
post-custom-banner

JPA의 변경감지 (DirtyChecking)와 @DynamicUpdate에 대해 알아보자

JPA의 변경감지 (Dirty Checking)

JPA에는 수정관련된 메서드가 존재하지 않는다.
JPA를 통해 데이터를 수정하려면, Entity를 조회하여 조회된 Entity의 데이터를 변경하면 DB에 자동으로 반영되는 기능을 Dirty Checking이라고 한다.

여기서 Dirty란 상태의 변화가 생긴 정도로, Dirty Checking은 상태 변경 검사를 말한다.
이 변화가 있다의 기준은, 최초 조회 상태이다.

JPA의 데이터 변경 로직

  1. 트랜잭션 시작
  2. 영속성 Entity 조회 (없으면 조회하여 영속화)
  3. 조회한 영속성 Entity의 데이터 수정
  4. 트랜잭션 커밋
    @Transactional
    public void modify(String nickname, String email) {
        Optional<User> findUser = userRepository.findByEmail(email);
        User user = findUser.orElseThrow(UserNotFoundException::new);
        // nickname 변경
        user.updateNickname(nickname);
    }

이 과정에서 update쿼리를 요청하는 부분이 없지만,
JPA의 변경감지(Dirty Checking)으로 DB의 데이터가 변경된다.

Dirty Checking

JPA는 영속성 컨텍스트에 엔티티를 보관할때, 최초의 상태를 저장하고 있는다.
이를 스냅샷이라고 하고, 트랜잭션이 끝나는 시점에 스냅샷과 달라진 부분을 비교하여 Update 쿼리를 날리게된다.

image

[출처 : 김영한님의 자바 ORM 표준 JPA 프로그래밍]

영속성 컨텍스트에서 관리하는 객체에 정보가 변경되면, 1차 캐시에 저장된 스냅샷과 비교하고, 달라진 부분에 맞게 SQL을 쓰기지연 SQL 저장소에 저장한다. 이후 커밋되는 시점에 flush 한다.


Dirty Checking 테스트

테스트 코드를 작성해서 유저 닉네임을 수정해보자.

@Test
    public void 유저닉네임변경() {
        // given
        User savedUser = userRepository.save(userA);

        // when
        String nickname = "닉네임수정";
        savedUser.updateNickname(nickname);

        // then
        assertThat(savedUser.getNickname()).isEqualTo(nickname);
    }

save 메서드를 호출하지 않았지만, 아래와 같이 변경된 닉네임으로 테스트를 통과하며 정상적으로 updaet가 수행된다.

image

Dirty Checking 대상

이때, 이런 상태변경 검사(Dirty Checking)의 대상은 영속성 컨텍스트가 관리하는 엔티티에만 적용된다.

  • detach된 엔티티 (준영속)
  • DB에 반영되기 전 처음 생성된 엔티티 (비영속)

과 같은 준영속/비영속 상태의 엔티티는 Dirty Checking의 대상에 포함되지 않는다.


@DynamicUpdate

이때, 위에서 업데이트 시 변경한 내용은 nickname 하나인데, update 쿼리에는 User 엔티티의 모든 필드가 update되는 것을 확인할 수 있다.

기본적으로 JPA는 전체 필드를 업데이트 하는 방식으로, Dirty Checking으로 생성되는 update 쿼리는 기본적으로 모든 필드를 업데이트한다.

이렇게 JPA에서 변경된 필드만 update하지 않고,
모든 필드를 변경하는 쿼리를 생성하여 얻을 수 있는 장점으로,

  1. 수정 쿼리가 항상 동일하게 만들어지므로, 생성되는 쿼리가 같아 부트 실행시점에 미리 update 쿼리를 만들어서 재사용이 가능하다.
  2. 데이터베이스 입장에서 동일한 쿼리를 받으면, 이전에 파싱된 쿼리의 재사용이 가능하다.

이러한 이점으로 JPA는 Update시 모든 필드를 쿼리로 만들어 실행한다.

하지만, 하나의 엔티티에 필드가 많아져 update가 부담스러워지고,
변경된 필드만 upadte 쿼리로 적용하고 싶다면 @DynamicUpdate 어노테이션을 사용한다.

image

User엔티티에 @DynamicUpdate 어노테이션을 추가하고 동일한 테스트를 실행한결과, 변경된 nickname만 update쿼리로 적용되었다.

참고

post-custom-banner

0개의 댓글