본문 바로가기
학습/DB

JPA에서 Dirty Check 방지하기

코동이 2021. 10. 2.

JPA에서 업데이트 방식은 dirty check로,  모든 행에 대해서 하나씩 update 쿼리를 날려서 수정한다. 성능에 악영향을 줄 수 있다. 따라서 조회 전용이면 dirty check를 사전에 방지하는 것이 좋으며 @Transactional(readOnly = true)를 사용한다.

 

    @Transactional
    public void update() {
        List<Comment> comments = commentRepository.findAll();
        for (Comment comment : comments) {
            comment.setComment("별로에요");

            commentRepository.save(comment);
        }
    }

 

commentRepository.findAll()로 모든 comment를 조회하고 "별로에요"로 내용을 수정한다. 이를 다시 save()하여 수정하는 update() 메서드이다.

 

    @Test
    void commentTest() {
        commentService.update();
    }

 

모든 comment 객체를 for문으로 하나씩 조회해서 값을 변경하면 comment의 크기 n만큼 쿼리 갯수를 발생시킨다.

 

 

setComment로 코멘트 내용만 수정했지만, 칼럼 5개 모두 수정함을 알 수 있다.

 

Comment에 @DynamicUpdate를 추가하면, update set은 칼럼5개가 아닌 comment만 한다.

 

 

영향받는 DB가 적어지므로 안정적인 쿼리가 만들어진다. 

 

 

만약 save() 실행을 주석처리 한다면?

 

    @Transactional
    public void update() {
        List<Comment> comments = commentRepository.findAll();
        for (Comment comment : comments) {
            comment.setComment("별로에요");

            //commentRepository.save(comment);
        }
    }

 

commentRepository.save(comment)를해도 여전히 update 쿼리가 나간다. 이를 Dirty Check라고 한다.

 

 

영속성 관리 중에 일어난 변경은 별도의 save호출이 없더라도 DB에 영속화가 된다. 

 

 

application.yml에 다음 로깅 레벨을 추가하고 자세하게 확인한다

 

logging:
  level:
    root: trace

 

 

영속성 컨텍스트에 있던 id=1 인 Comment가 dirty check 대상으로 검사된 것을 알 수 있다. @Transactional가 써져있는 update 메서드는 영속성 컨텍스트에서 관리가 되는데, 여기서 변경이 되면 save()가 없어도 DB에 실제로 반영이 된다. 

 

만약에 @Transactional이 없다면, update 메서드는 영속성 컨텍스트의 관리대상이 아니기 때문에 자동으로 update를 DB에 반영하지 않는다.

 

 

*수정이 아니라 새로 생성의 경우는 dirty check가 가능할까?

 

    @Transactional
    public void insert() {
        Comment comment = new Comment();
        comment.setComment("감동이에요");
        
        //새로 생성을 할 때는 save가 필요할까?
        //commentRepository.save(comment);
    }

 

답은 불가능하다. 새롭게 생성하는 엔티티는 무조건 save()를 넣어주어야 한다. save() 대상인 comment는 영속성 컨텍스트에 없기 때문에 dirty check가 작동하지 않는다. DB로부터 영속성 컨텍스트가 관리하도록 조회해온 객체들만 dirty check를 한다.

 

dirty check는 성능 이슈가 있다. DB에서 조회하여 영속성 컨텍스트에 온 엔티티들은 jpa에서 모두 dirty check를 거친다. 수십, 수백개면 상관이 없지만, 수십만, 수백건의 배치작업에서는 문제가 생길 수 있다.

 

배치 작업에서 dirty check를 없애는 방법은 @Transcation(readOnly=true)를 추가하는 것이다.FlushModeTypeMANUAL로 설정된다. 즉, flush가 auto로 작동하지 않기 때문에 dirty check가 작동하지 않는다.

 

    @Transactional(readOnly = true)
    public void update() {
        List<Comment> comments = commentRepository.findAll();
        for (Comment comment : comments) {
            comment.setComment("별로에요");

            //commentRepository.save(comment);
        }
    }

 

readOnly이기 때문에 dirty check를 하지 않고, 따라서 update를 하지 않는다.

 

조회만 해야하는 경우에는 dirty check를 하지 않도록 readOnly를 사용하면 성능상 이점이 있다.

 

 

반응형

'학습 > DB' 카테고리의 다른 글

프로젝션(엔티티, 임베디드, 스칼라타입)  (0) 2021.10.06
경로 표현식  (0) 2021.10.05
영속성 컨텍스트로 발생하는 이슈  (0) 2021.10.01
Converter 사용하기  (0) 2021.09.30
@Embedded, @Embedabble  (0) 2021.09.30