본문 바로가기
Spring/JPA

JPA @Query @Modifying 벌크 연산시 자동 업데이트(Auditing) 주의사항

by 흑시바 2023. 1. 15.

- 문제

@Query + @Modifying를 활용해 업데이트 벌크 연산을 진행했는데, Auditing + @LastModifiedDate가 작동하지 않는 것을 확인했다.

 

1. Auditing과 @LastModifiedDate을 적용하고 10개의 임의 데이터를 추가한다.

초기 입력된 데이터

2. @Query + @Modifying을 활용한 벌크 연산을 진행한다.

 

- ShibaRepository

@Modifying(clearAutomatically = true)
@Query("update Shiba s set s.age = :age where s in (:shibaList)")
int updateAllShibaAge(@Param("age") Integer age, @Param("shibaList") List<Shiba> shibaList);

 

- ShibaRepositoryTest

@Test
@Transactional
@Rollback(value = false)
void checkBulkUpdate() {
	List<Shiba> shibaList = shibaRepository.findAll();
	shibaRepository.updateAllShibaAge(20, shibaList);
}

 

테스트 결과, age 필드는 업데이트가 정상적으로 됐지만 날짜는 변경되지 않는 것을 확인할 수 있었다.

age는 20으로 업데이트 됐지만, 업데이트 시간은 변경되지 않았다.

3. 변경 감지를 통한 업데이트를 진행한다.

 

그렇다면, dirty checking(변경 감지)를 활용해서 업데이트를 하면 어떨까?

@Test
@Transactional
@Rollback(value = false)
void checkBulkUpdateByDirtyChecking() {
	List<Shiba> shibaList = shibaRepository.findAll();
	shibaList.forEach(i -> i.updateAge(30));
}

 

변경 감지를 통한 업데이트를 진행했을 때는 정상적으로 날짜가 업데이트 된 것을 확인할 수 있었다.

age와 업데이트 시간 모두 변경됐다.

- 원인

Auditing은 도메인을 영속성 컨텍스트에 저장하거나 조회를 수행한 후에 update를 하는 경우 자동으로 시간을 매핑하여 데이터베이스 테이블에 넣어주는 방식으로 작동한다.

 

하지만 @Query로 정의된 벌크 연산 JPQL은 기존 JPA 처럼 영속성 컨텍스트를 거쳐 쓰기지연 SQL로 동작하지 않고, 직접 DB와 쿼리하기 때문에 Auditing  기능이 적용되지 않았다.

- 해결

@Query 구문에 시간을 직접 업데이트 하는 방식으로 해결했다.

@Modifying(clearAutomatically = true)
@Query("update Shiba s set s.age = :age, s.updatedAt = :updatedAt where s in (:shibaList)")
int updateAllShibaAge(@Param("age") Integer age, @Param("updatedAt") LocalDateTime updatedAt, @Param("shibaList") List<Shiba> shibaList);

 

직접 업데이트할 때 LocalDateTime.now() 파라미터를 추가하여 시간을 저장한다.

@Test
@Transactional
@Rollback(value = false)
void checkBulkUpdate() {
    List<Shiba> shibaList = shibaRepository.findAll();
    shibaRepository.updateAllShibaAge(40, LocalDateTime.now(), shibaList);
}

 

테스트 결과, 날짜가 정상적으로 업데이트 되는 것을 확인할 수 있었다.

날짜가 모두 업데이트 되었다!

 

댓글