유저 닉네임 변경 API를 예시로 들어보자.
PatchMapping을 사용해서 특정 컬럼만 업데이트하는 RESTAPI라고 선언을 하고
nickname을 받아서 JPA의 더티체킹으로 업데이트를 진행한다.
우리가 원하는 건 닉네임만 업데이트가 진행되길 원한다. 그러나 결과는 그게 아니던데???
분명 업데이트 쿼리는 나가는데 왜 모든 컬럼에 대해서 set이 들어가는 거지?
결국 이는 리소스 낭비로 이어지지 않나? 라는 생각이 들어 그 이유와 해결 방법을 찾아보았다.
이유
JPA의 구현체인 하이버네이트는 결국 JDBC API를 내부적으로 사용하는데, 웹 개발을 공부하셨던 분들이라면 Statement와 PreparedStatement를 들어보셨을 것이다.
Statement를 사용하면 쿼리를 사용할 때마다 아래의 순서를 거친다.
1. 쿼리문 분석
2. 컴파일
3. 실행
단순히 Statement를 사용하면 1~3의 무한 반복이 일어나지만, PreparedStatement를 사용하게 되면 1~3의 과정을 거친 뒤 캐시에 담아서 재사용을 진행한다.
쿼리의 재사용을 위해서 하이버네이트는 주로 PreparedStatement를 사용해서 작업을 수행하게 된다.
즉 쿼리에서 ? 부분만 바꿔가면서 재사용을 진행한다.
아래는 하이버네이트의 공식 문서 내용이다.
위 코드에 나와있듯이 분명 PriceCents만 수정했는데 UPDATE 쿼리에 모든 컬럼이 SET에 들어가는 걸 볼 수 있다.
결국 하이버네이트가 알아서 효율적으로 처리하고 있었던 것이다.
특정 컬럼만 업데이트를 어떻게 날리지?
이 방법은 @DynamicUpdate 어노테이션을 Entity에 붙여줌으로써 해결이 가능하다.
@DynamicUpdate
public class User extends BaseTimeEntity {
위 쿼리처럼 내가 변경한 부분만 SET에 설정이 되는 걸 볼 수 있다.
그러면 이제 드는 생각이 이거 무조건 쓰는 게 리소스적으로 이득 아닌가??라는 생각에 모든 Entity에 다 붙이려고 했다가
굳이 쓸모 없이 붙이면 쿼리의 캐싱률이 떨어진다는 얘기를 봤다.(아무래도 전체 컬럼을 SET으로 해서 ? 만 변경해서 사용하는데, 새로운 쿼리를 날리게 돼서 캐싱을 이용 못하니깐 그런가 보다)
인덱스가 많은 곳에서 사용하자
테이블에 인덱스가 많을 경우 변경이 일어나면 재정렬을 진행하는데 리소스가 많이 소모된다.
변경되지 않는 컬럼까지 업데이트를 하면 불필요한 인덱스 작업을 발생시킬 수 있다고 하니 이런 경우에는 다이나믹업데이트가 유용하게 사용될 것 같다.
또한
결국 User 테이블에서는 다이나믹업데이트를 사용하는 것으로 결정!
'프로젝트 > 씈크럼 프로젝트' 카테고리의 다른 글
우당탕탕 동시성에 대해 파악하고 thread-safe하게 해보자 (0) | 2024.03.11 |
---|---|
for문을 stream으로 변경하자.(근데 왜 더 느리지?) (0) | 2024.02.24 |
공통으로 가지는 생성시간, 수정시간을 상속으로 이용해보자. (0) | 2024.02.19 |
QueryDsl로 페이지네이션 도입(검색 API 구현) (1) | 2024.02.17 |