인프런 커뮤니티 질문&답변

jayjoy05님의 프로필 이미지
jayjoy05

작성한 질문수

자바 ORM 표준 JPA 프로그래밍 - 기본편

[JPA 더티체킹] 조회한 엔티티에 대해 update할 때, 실제 update 값은 기존 엔티티 필드 값들과 모두 동일해도 update 쿼리가 실행되는 문제

작성

·

15

·

수정됨

0

학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.

[질문 템플릿]
1. 강의 내용과 관련된 질문인가요? 예
2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 예
3. 질문 잘하기 메뉴얼을 읽어보셨나요? 예

[질문 내용]

안녕하세요 영한님! 강의 잘 듣고 있습니다. JPA 더티 체킹 관련해서 질문이 있습니다.,

현재 매일 외부로부터 몇천~몇만 건의 데이터를 받아서 인터페이스 테이블에 해당 데이터의 키 값에 해당하는 데이터가 없으면 삽입, 이미 있으면, 키 값을 제외한 해당 데이터와 인터페이스 테이블에 있는 데이터의 컬럼들을 비교해서 update 치는 로직으로 구현했습니다. (해당 정보는 변경될 일이 거의 없기 때문에 매일 테이블을 delete insert 하는 방식보다는 효율적인 방식이라고 생각되어 채택하였습니다.)

 

로직은 간략하게 다음과 비슷합니다. (실제 컬럼 수는 훨씬 많습니다.)

List<UserInfoRequest> users = objectMapper.readValue(decryptedData, objectMapper.getTypeFactory().constructCollectionType(List.class,UserInfoRequest.class));

List<UserInterfaceEntity> existingUsers = UserInterfaceRepository.findAll();

List<UserInterfaceEntity> userListToAdd = new ArrayList<>();


for (UserInfoRequest request : users) {
    UserInterfaceEntity existingUser = existingUsers.stream()
            .filter(user -> user.getEmail().equals(request.getEmail()))
            .findFirst()
            .orElse(null);
       /** 기존 인터페이스 테이블에 존재하지 않는 사용자 정보이면 */
            if (existingUser == null) {
                  UserInterfaceEntity newUser = UserInterfaceEntity.builder()
                        .email(request.getEmail()) -- PK
                        .name(request.getName())
                        .address(request.getAddress))

                  
                  UserInterfaceRepository.save(newUser)

         } else {
                 existingUser.updateUserInfo(request); 

           }
}

// 엔티티 클래스
public void updateUserInfo(UserInfoRequest request){
    this.name = request.getName();
    this.address = request.getAddress(); 
   '''실제로는 컬럼수가 더 많습니다'''
}

저는 이러한 상황에서 existingUser가 존재하면 request로 들어오는 데이터가 아예 변경이 되지 않았을 수도 있고, 데이터의 어떤 컬럼 값이 변경됐을 지 모르기 때문에 일일이 if문으로 비교하는 것보다는 모든 컬럼을 update 하는 방식을 취했고, 제가 기대했던 결과는 같은 값이 들어오면 DB에 update 쿼리가 나가지 않는 것을 기대했습니다.

그런데, 실제로 existingUser가 존재하고, request의 모든 필드값과 exsitingUser 의 모든 필드 값이 다 같은 건에 대해서도 update 쿼리가 DB에 나가더라구요.

 

  1. JPA 더티체킹의 경우 최초 조회한 시점에 스냅샷을 떠두고, 커밋 시점에 변경된 엔티티에 대해서만 UPDATE 쿼리가 나가는 것으로 알고 있는데, 왜 필드 값이 실제로는 변경되지 않았음에도 UPDATE 쿼리가 나가는 것일까요?

 

  1. 이 부분에서 실제로 필드 값이 변경된 경우에만 update 쿼리를 실행하고 변경되지 않은 경우에는 update 쿼리를 막으려면 if문을 일일이 필드마다 비교해야하는 로직을 추가하는 것 말고는 다른 좋은 방법이 없을까해서 질문 남깁니다..

 

 

 

답변 2

0

김영한님의 프로필 이미지
김영한
지식공유자

안녕하세요. jayjoy05님

엔티티 필드가 단순한 String이나 기본 타입이 아닌 커스텀 타입(Embeddable, Converter, UserType 적용)이나 Date/Time, Lob(Clob, Blob) 필드를 사용할 경우, Hibernate가 해당 필드의 동등성(equality)을 판단할 때 기대와 다른 방식으로 작동할 수 있습니다.

 

추가로 이런 경우 체크 코드가 들어가더라도 변경된 데이터가 있는 경우에만 데이터의 값을 바꾸는 것이 명확하기 때문에 체크 하는 방식으로 변경하는 것을 권장합니다.

감사합니다.

0

안녕하세요, 인프런 AI 인턴입니다.

jayjoy05님께서 제기하신 문제는 JPA의 변경 감지(Dirty Checking) 메커니즘과 관련된 내용입니다. JPA는 엔티티의 상태를 추적하고, 변경된 경우에만 UPDATE 쿼리를 실행합니다. 하지만 동일한 값으로 필드를 재할당하면 변경으로 간주되어 쿼리가 실행될 수 있습니다.

해당 상황에서 성능 최적화를 위해 몇 가지 접근 방법을 고려해볼 수 있습니다:

  1. 변경된 필드만 업데이트: 필드별로 변경 여부를 체크하는 방법입니다. 이는 다소 번거롭지만, 가장 확실한 방법입니다. Java에서는 Objects.equals와 같은 메서드를 이용하여 각각의 필드가 변경되었는지 비교할 수 있습니다.

  2. JPA 툴의 특정 기능 사용: Hibernate를 사용하는 경우, 데이터베이스에서는 Update가 발생하지만, 실제로 데이터에 변동이 없으면 Update Trigger를 통해 데이터베이스 레벨에서 최적화가 가능할 수 있습니다. 이 경우 Hibernate의 특정 설정을 살펴보세요.

  3. 커스텀 업데이트 쿼리 사용: JPA의 @QueryEntityManager를 이용해 변경된 필드만 업데이트하는 JPQL 또는 네이티브 쿼리를 작성할 수 있습니다.

관련 질문에 대한 링크

아래는 관련된 주제를 다룬 인프런의 다른 질문 답변을 참고하셔서 문제 해결에 도움을 얻으실 수 있습니다.

이 외에도 혹시 더 큰 그림에서 최적화가 필요하시다면, 특정 트랜잭션에서 발생하는 데이터의 일관성이나 동시성 제어에 대한 고민도 함께 이루어져야 할 것입니다.

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.

jayjoy05님의 프로필 이미지
jayjoy05

작성한 질문수

질문하기