[Spring Data JPA] 더티 체킹 (Dirty Checking)

Juhye Pyoun·2023년 9월 19일

Spring

목록 보기
6/7

더티 체킹(Dirty Checking)이란?

트랜잭션(Transaction) 안에서 엔티티(Entity)의 변경이 일어났을 때
변경한 내용을 자동으로 DB에 반영하는 것

Dirty : 상태의 변화가 생김
Checking : 검사

예시

주문 취소 기능

@Transactional  
public void cancelOrder(Long orderId) {  
    //주문 엔티티 조회  
    Order order = orderRepository.findOne(orderId);  

    //주문 취소  
    order.cancel();  
}

위 코드는 orderId를 통해 주문을 취소하는 메소드이다.
update와 같은 쿼리가 없음에도 실제로 메소드를 실행하면 데이터베이스에 update가 잘 이루어진다.

트랜잭션 시작
orderId로 주문 Entity 조회
➔ 해당 Entity 주문 취소 상태로 Update
➔ 트랜잭션 커밋

👉 이를 가능하게 하는 것이 바로 '더티 체킹(Dirty Checking)'이다.

 

더티 체킹

  • 더티 체킹에서 "변화가 있다"의 기준은 최초 조회 상태이다.

  • JPA에서는 트랜잭션이 끝나는 시점에 변화가 있던 모든 엔티티의 객체를 데이터베이스로 알아서 반영시켜준다.

    JPA에서 Entity를 조회
    ➔ 조회된 상태의 Entity에 대한 스냅샷 생성
    ➔ 트랜잭션 커밋 후 해당 스냅샷과 현재 Entity 상태의 다른 점을 체크
    다른 점들을 update 쿼리로 데이터베이스에 전달

  • 더티 체킹을 검사하는 대상은 영속성 컨텍스트가 관리하는 Entity에만 적용된다.
    준영속, 비영속 Entity는 값을 변경할 지라도 데이터베이스에 반영시키지 않는다.
    * 영속성 컨텍스트(엔티티를 영구 저장하는 환경)
    * 엔티티의 생명주기
      - 비영속(new/transient): 영속성 컨텍스트와 전혀 관계가 없는 상태
      - 영속(managed): 영속성 컨텍스트에 저장된 상태
      - 준영속(detached): 영속성 컨텍스트에 저장되었다가 분리된 상태
      - 삭제(removed): 삭제된 상태

 

Spring Data JPA의 더티 체킹

  • Spring Data JPA에서는 @Transactional을 사용하여 더티 체킹을 수행할 수 있다.
@Slf4j
@RequiredArgsConstructor
@Service
public class PayService {

    private final PayRepository payRepository;

    @Transactional
    public void update(Long id, String tradeNo) {
        Pay pay = payRepository.getOne(id);
        pay.changeTradeNo(tradeNo);
    }

테스트 코드를 작성해서 실행해보면

@Test
public void SpringDataJpa로_확인() {
    //given
    Pay pay = payRepository.save(new Pay("test1",  100));

    //when
    String updateTradeNo = "test2";
    payService.update(pay.getId(), updateTradeNo);

    //then
    Pay saved = payRepository.findAll().get(0);
    assertThat(saved.getTradeNo()).isEqualTo(updateTradeNo);
}

아래와 같이 정상적으로 update 쿼리가 수행됨을 확인할 수 있다.

 

변경 부분만 update하고 싶을땐?

JPA에서는 전체 필드를 업데이트하는 방식을 기본값으로 사용하기 때문에
기본적으로 더티 체킹을 실행하면, SQL에서는 변경된 엔티티의 모든 내용을 update 쿼리로 만들어 전달한다.
이때 필드가 많아지면(20~30개 이상) 전체 필드를 update하는게 비효율적일 수도 있다.

👉 이때는 @DynamicUpdate를 해당 Entity에 선언하여 변경 필드만 반영되도록 할 수 있다.

@Getter
@NoArgsConstructor
@Entity
@DynamicUpdate
public class Order {

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

 

[참고자료]

링크1🔗
링크2🔗
링크3🔗

0개의 댓글