[JPA] 더티 체킹(Dirty Checking)

나르·2022년 1월 19일
1

Spring

목록 보기
10/25
post-thumbnail

JPA를 사용해 개발을 하다보면, 특히 수정 관련된 비즈니스 로직을 만들다보면 필연적으로 더티 체킹(Dirty Checking)이라는 것을 알게 됩니다.
더티 체킹이란 무엇이고 어떻게 일어나는 것일까요?? 🤔

예를 들어 댓글을 수정하는 메소드를 보면 다음과 같습니다.

@Transactional
public void updateComment(UpdateCommentRequestDto updateCommentRequest) {
    Comment comment = checkAuthorization(updateCommentRequest.getCommentId());
    comment.setContent(updateCommentRequest.getContent());
}

// Comment Entity
...
public void setContent(String content) {
    this.content = content;
}
...

코드 어디를 봐도 save에 대한 내용은 없습니다.
어떻게 save를 하지 않아도 변경사항 업데이트가 적용되는걸까요?

여기서 사용되는 것이 더티 체킹(Dirty Checking)입니다.

Dirty란 상태의 변화입니다.
즉, Dirty Checking이란 상태 변경 검사 입니다.

더티 체킹은 Transaction 안에서 엔티티의 변경이 일어나면, 변경 내용을 자동으로 데이터베이스에 반영하는 JPA의 특징입니다.

JPA에서는 엔티티를 조회하면 해당 엔티티의 조회 상태 그대로 스냅샷을 만듭니다.
그리고 트랜잭션이 끝나는 시점에 스냅샷과 현재의 상태를 비교해 다른 점이 있다면 Update Query를 데이터베이스로 전달합니다.

  • 더티 체킹은 영속성 컨택스트(Persistence Context) 안에 있는 엔티티를 대상으로 일어납니다.
    때문에 detach 되거나(준영속), DB에 반영되기 전 처음 생성된(비영속) 상태의 엔티티는 더티 체킹이 적용되지 않습니다.

  • 또한 Transaction 안에서만 일어나기 때문에, @Transactional 어노테이션 등으로 한 트랜잭션으로 묶어주고 변경해야 적용됩니다.

예를 들어 EntityManager을 사용해 직접 Transaction을 처리한다면, 다음과 같은 과정으로 update가 이루어지는 것입니다.

EntityManager manager = entityManagerFactory.createEntityManager();
EntityTransaction transaction = manager.getTransaction();
// 1. 트랜잭션 시작
transaction.begin();
// 2. 엔티티를 조회 & 스냅샷 생성(영속성 컨텍스트에 저장)
Comment comment = manager.find(Comment.class, id);
// 3. 엔티티의 값을 변경
comment.setContent(content);
//  스냅샷과 최종 엔티티의 내용을 Dirty Checking 해서 Update Query 발생
// 4. 트랜잭션 커밋
transaction.commit();

@DynamicUpdate

Dirty Checking으로 생성되는 update 쿼리는 기본적으로 모든 필드를 업데이트합니다.

JPA에서 변경된 필드만 Update하지 않고 모든 필드를 변경하는 쿼리를 생성하는 이유는,

  • 모든 필드를 Update 쿼리로 만들면 수정 쿼리가 항상 동일하게 생성되므로 미리 쿼리를 생성하여 재사용이 가능하기 때문입니다.
  • 또한 데이터 베이스는 동일한 쿼리를 전달받으면 한번 파싱된 쿼리를 재사용할 수 있습니다.

하지만 필드가 많은 경우에는 전체 필드 Update 쿼리가 부담이 될 수 있습니다.
그래서 이런 경우엔 엔티티의 최상단에 @DynamicUpdate를 추가하여 변경 필드만 반영되도록 할 수 있습니다.

@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@DynamicUpdate
@Entity
public class Comment{

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

    @Column(nullable = false)
    private Long postId;

    @Column(nullable = false)
    private Long userId;

    @Column(nullable = false, columnDefinition = "TEXT")
    private String content;

    public void setComment(String content) {
        this.content = content;
    }

}

Ref.

더티 체킹 (Dirty Checking)이란?
JPA Dirty Checking사용시 주의점

profile
💻 + ☕ = </>

0개의 댓글