spring / / 2023. 10. 16. 20:02

spring data jpa의 @DynamicUpdate

1. 개요

Hibernate로 Spring Data JPA를 사용할 때, @DynamicUpdate와 같은 추가적인 특징을 사용할 수 있다.

@DynamicUpdate는 JPA 엔터티에 적용될 수 있는 클래스 수준의 어노테이션이다. 이는 변경된 컬럼에 대해서만 업데이트할 수 있는 구문을 만들어낸다.

2. JPA @Entity

애플리케이션이 시작될 때 Hibernate는 CRUD에 대한 SQL 구문을 만들어낸다. 이 SQL 구문은 한번 생성되어 성능 향상을 위해 메모리에 캐싱된다.

생성된 SQL 업데이트 구문은 엔터티의 모든 컬럼을 포함한다. 엔터티를 업데이트 하는 경우에, 수정된 컬럼의 값은 SQL 업데이트 구문에 전달된다. 업데이트 되지 않는 컬럼의 경우에는 Hibernate에서 업데이트를 위해 기존 값을 사용한다.

아래의 예제를 한번 보자.

UserJpo 이름을 가진 JPA entity
@Entity
@NoArgsConstructor
public class UserJpo {

    @Id
    private String userId;

    private String name;

    private String address;

    private long createdAt;
}
JPA repository
@Repository
public interface UserRepository extends JpaRepository<UserJpo, String> {
}

여기서 name만 수정을 하면

@Test
public void modify() {
    User user = userService.find("1");
    user.setName("홍길동2");
    userService.modify(user);
}

모든 컬럼의 값을 변경하는 SQL이 실행이 된다.

update user_jpo set address=?, created_at=?, name=? where user_id=?

이름만 변경을 하는데 테이블의 모든 컬럼이 update가 된다.

3. JPA @Entity with @DynamicUpdate

name 필드만 수정했음에도 Hibernate는 모든 컬럼을 포함시켰다.

이제, @DynamicUpdate 어노테이션을 추가해보자.

@Entity
@NoArgsConstructor
@Getter
@DynamicUpdate // 추가
public class UserJpo {
}

이렇게 실행하면

update user_jpo set name=? where user_id=?

변경되는 컬럼에 대한 SQL만 포함한다.

엔터티에 @DynamicUpdate를 사용할 때 어떤 일이 벌어지는 것일까?

실제로, 엔터티에 @DynamicUpdate를 사용할 때, Hibernate는 업데이트를 위해서 캐싱된 SQL 구문을 사용하지 않는다. 대신 엔터티 업데이트할 때마다 SQL 구문을 만든다. 생성된 SQL은 변경된 컬럼만을 포함한다.

변경된 컬럼을 찾기 위해서 Hibernate는 현재 엔터티 상태를 추적할 필요가 있다. 그래서 엔터티 필드를 변경할 때 현재와 변경된 상태를 비교한다.

이 말은 @DynamicUpdate는 성능 오버헤드를 가지고 있다는 것을 의미한다. 그러므로 실제로 필요할 때만 사용을 해야 한다.

이 어노테이션을 사용하는 몇 가지 시나리오가 있다. 예를 들어 엔터티가 많은 수의 컬럼이 있고 오직 몇 개의 컬럼만 자주 변경이 되는 경우이다. 또한 version이 없는 optimistic locking을 사용할 때 @DynamicUpdate를 사용할 필요가 있다.

언제 @DynamicUpdate를 사용해야 할까?

case 1: 엔터티가 많은 컬럼을 가지고 있을 때

애플리케이션이 수십 개의 필드를 가지고 있을 수 있다. 필드 크기가 몇 바이트부터 수 천 바이트일 수 있다. 그런 경우 @DynamicUpdate가 적용될 수 있다. 그렇지 않으면 네트워크, CPU, 다른 리소스(직렬화, 전송 등)를 낭비하게 된다.

case 2: 데이터베이스가 컬럼 수준의 locking을 지원

PostgreSQL, MySQL, Oracle은 로우 레벨 locking을 지원한다.

하지만, 컬럼 레벨의 locking을 지원하는 YugabyteDB에서 유용하게 사용될 수 있다.

case 3: 데이터베이스가 컬럼 수준의 버저닝을 지원

case 4: 다양한 컬럼에 대해 동시성 이슈가 발생할 때

여러 필드를 여러 쓰레드에서 동시에 update를 할 때 @DynamicUpdate를 사용하면 필요한 컬럼만 변경하므로 문제가 발생할 가능성이 줄어든다. 하지만 동시성 이슈의 모든 문제를 해결해주지 못한다. 이런 경우 locking 매커니즘이나 다른 분산 락 등을 사용해서 해결해야 한다.

참고

https://www.baeldung.com/spring-data-jpa-dynamicupdate

https://dzone.com/articles/when-to-use-the-dynamicupdate-with-spring-data-jpa

https://multifrontgarden.tistory.com/299

반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유