문제: 변경하지 않은 컬럼까지 update 쿼리가 발생한다.
update를 한 필드는 최대 3개인데, 실제 실행되는 쿼리를 살펴보면 entity의 전 영역을 수정하고 있다.
@Override
public Account modify(Account account, AccountModify req) {
// 1. password가 변경되었다면, 새로운 password를 암호화
if (!passwordService.isValidPassword(account.getPassword(), req.getPassword())) {
req.encryptPassword(passwordService.encryptPassword(req.getPassword()));
}
// 2. domain 객체를 업데이트
account.changeProfile(req);
// 3. update 쿼리 실행
accountRepository.findById(account.getId())
.orElseThrow(() -> new IllegalArgumentException("올바르지 않은 접근입니다."))
.update(account);
// 4. 업데이트된 도메인 객체 리턴
return account;
}
public class AccountEntity {
...
public void update(Account account) {
this.password = account.getPassword();
this.profile = account.getProfile();
this.name = account.getName();
}
}
Java
복사
2024-07-17T02:40:42.201+09:00 DEBUG 31411 --- [nio-8080-exec-3] org.hibernate.SQL :
update
account
set
campus=?,
email=?,
is_campus_certificated=?,
name=?,
password=?,
profile=?
where
id=?
Plain Text
복사
@DynamicUpdate
import org.hibernate.annotations.DynamicUpdate;
@Entity
@Getter
@NoArgsConstructor
@DynamicUpdate <-- Here!
@Table(name = "account")
public class AccountEntity extends BaseEntity
Java
복사
만약 변경된 컬럼만 update하고 싶다면, @DynamicUpdate 어노테이션을 엔티티 위에 추가하면 된다.
이는 Hibernate에서 지원하는 기능이며, 이에 따라 Hibernate는 @DynamicUpdate 어노테이션이 적용된 엔티티의 상태를 추적하게 된다. 구체적으로, 엔티티 필드를 변경할 때 현재 상태와 변경된 이후의 상태를 계속해서 비교한다.
만약 적용되지 않는다면, 이런 비교 단계 없이 바로 update 쿼리를 발생시키므로, 이미 생성되어 캐시된 쿼리를 발생시킬 수 있다. 작게나마, 혹은 수정이 잦은 엔티티의 경우 꽤 크게 성능 오버헤드가 유의미하게 작용한다.
따라서 위와 같이 6-7개정도 되는 컬럼을 업데이트하는 경우 굳이 이러한 기능을 사용할 필요가 없다.
@DynamicUpdate를 사용해야 할 때
•
많은 컬럼을 가진 엔티티일 때 사용한다. 구체적으로 20개 이상쯤 되면 사용하게 된다. 그렇지 않으면 서버 → DB로 갈 때 네트워크 리소스와 각 인스턴스의 CPU를 크게 낭비하게 된다.
•
컬럼 수준의 Lock을 지원하는 DB일 때 사용한다.
•
마찬가지로, 컬럼 수준의 버저닝을 지원할 때 발생한다.
•
동시성 이슈가 발생할 때 사용한다. 하지만 @DynamicUpdate 보다는 분산 락 등을 사용하는 것이 낫다.