Post

[JPA] Dirty Checking

[JPA] Dirty Checking

📌 Dirty Checking이란?

Dirty Checking(변경 감지)란 JPA가 영속 상태인 엔티티의 값이 바뀌었는지를 자동으로 감지해, 트랜잭션을 flush하는 순간 필요한 UPDATE 쿼리를 동적으로 생성하여 실행하는 기능이다. 따로 save 또는 saveAndFlush 메서드를 호출하지 않아도 트랜잭션이 끝날 때 자동으로 엔티티 변경 내용이 DB에 반영된다.

준영속 또는 비영속 엔티티는 Dirty Checking 대상이 아니다. 즉, 이들은 트랜잭션이 끝나도 변경 내용이 DB에 자동으로 반영되지 않는다.

📌 동작 과정

persist , find 메서드 등으로 엔티티가 영속 상태가 되면, 실제 엔티티 객체는 1차 캐시에 저장되고, 엔티티의 스냅샷 또한 함께 보관된다.

트랜잭션 내에서 엔티티의 필드를 수정해도 즉시 쿼리문이 DB에 날라가는 것이 아니다. 변경 내용은 영속성 컨텍스트에만 반영된다.

트랜잭션이 커밋되면 flush 가 호출되게 되는데, JPA는 flush 시점에서 1차 캐시의 스냅샷과 엔티티를 필드별로 비교하여 달라진 점이 있는지 확인한다. 만약 있다면, 쓰기 지연 SQL 저장소에 UPDATE 쿼리를 생성하여 넣는다.

flush 마지막 단계에서 쓰기 지연 SQL 저장소의 쿼리를 DB에 전송하고, 실제 트랜잭션 커밋이 일어나면서 변경 사항에 DB에 반영되게 된다.

📌 특징

생성되는 UPDATE 쿼리는 변경된 필드가 아니라 모든 필드가 포함된 쿼리이다. 변경된 필드가 맞춰 쿼리를 파싱하는 비용보다 모든 필드를 전송하도록 하는 방법의 비용이 더 적을 수 있으며, 항상 같은 쿼리를 사용하므로 쿼리를 캐시하여 사용할 수 있다.

다만 엔티티의 스냅샷을 메모리에 보관하기 때문에, 엔티티의 수가 많거나 필드가 많다면 메모리 사용량이 증가하게 된다. 또한 flush 시점에서 영속 엔티티의 필드 값 비교 시 비용도 증가한다. 김영한 님의 ‘자바 ORM 표준 JPA 프로그래밍’에 의하면, 필드가 30개가 넘어가면 전체 필드 UPDATE 쿼리문의 장점이 퇴색된다고 한다.

이 경우, @DynamicUpdate 를 사용하며 수정된 필드에 대해서만 UPDATE 쿼리를 생성하도록 할 수 있다. 다만, 필드가 30개가 넘어가는 상황이라면 테이블 설계를 다시 살펴 볼 필요가 있다.

또는 특정 필드만 업데이트하는 JPQL 쿼리를 작성하는 방법도 있다. 다만 이 방법은 DB와 영속성 컨텍스트 간 불일치하는 상황이 발생할 수 있다. JPQL 쿼리는 영속성 컨텍스트를 거치지 않고 바로 DB에 쿼리를 전송하기 때문이다.

만약 읽기 전용 트랜잭션이라면, @Transaction(readOnly = true) 를 통해 Dirty Checking을 생략할 수 있다.

📌 참고

https://jojoldu.tistory.com/415

This post is licensed under CC BY 4.0 by the author.