1. 알고자 하는 것
- JPA의 Dirty Checking 시 발생하는 update query를 확인한다.
- 수정된 필드만 update query를 날리는 것이 아닌, 모든 필드에 대해 update query가 나가는 이유를 분석한다.
2. 알게된 것
JPA의 Dirty Checking 시 발생하는 update query를 확인
- JPA의 Dirty Checking(변경 감지)은 트랜잭션 내에서 Entity의 변경사항을 DB에 자동으로 반영해주는 기능이다.
- 영속성 컨텍스트 내에서 최초 조회 시의 Entity에 대한 스냅샷을 보관하고
- 트랜잭션 커밋 시 EntityManager가 flush를 수행할 때, 스냅샷과 Entity를 비교한다.
- 변경 내용에 대해 쓰기 지연 SQL 저장소에 update query를 저장한다.
- query를 DB에 날리고 트랜잭션을 커밋한다.
- 이러한 Dirty Checking 기능을 사용할 때 발생하는 update query를 확인해본다.
- 사용한 Entity는 다음과 같다.
@Getter
@Setter
@NoArgsConstructor
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private Integer age;
public Member(String name, Integer age) {
this.name = name;
this.age = age;
}
}
- 다음과 같은 update 메서드를 통해 member의 값 중 나이(age)는 변경하지 않고, 이름(name) 필드만 변경한다.
- 이 때 Dirty Checking이 동작한다.
@Transactional
public void update(Long memberId) {
// find member (in persistence context)
Member member = memberRepository.findById(memberId)
.orElseThrow(() -> new IllegalArgumentException("member not found"));
// dirty checking
member.setName("change");
}
- 다음과 같이 변경된 필드에 대해서만 update query를 날릴 것이라고 예상했다.
update member set name='change' where id=1
- 실제로 나간 update query는 다음과 같이 모든 필드에 대해 update query가 나간다.
Hibernate:
update
member
set
age=?,
name=?
where
id=?
수정된 필드만 update query를 날리는 것이 아닌, 모든 필드에 대해 update query가 나가는 이유
- 모든 필드에 대해 update query를 날리면 DB에 보내는 데이터 전송량이 증가한다.
- 그럼에도 모든 필드에 대해 update query를 날릴 때의 장점은 다음과 같다.
- 모든 필드에 대해 update query를 수행하면 변경되는 필드에 관계없이 동일한 update query를 만들어 둘 수 있다.
- 이로 인해 JPA는 애플리케이션 로딩 시점에 update query를 미리 생성해두고 재사용이 가능해 성능상 이점을 가진다.
- DB 입장에서도 동일한 쿼리에 대해 이전에 파싱된 쿼리를 재사용이 가능하다.
- 만약 Entity가 가지는 필드의 수가 너무 많아 데이터 전송량 자체가 성능 문제를 일으킬 수 있는 경우
- DynamicUpdate 어노테이션을 사용해 수정된 데이터만 동적으로 update query를 날릴 수 있다.
@DynamicUpdate // 수정된 필드에 대해 동적으로 update query 생성
@Entity
public class Member {
Hibernate:
update
member
set
name=?
where
id=?
- 책에서 권장하는 DynamicUpdate 사용 시 성능향상 기준은 컬럼 30개 이상이라고 한다.
- 하지만 컬럼이 30개 이상이라면 DB 설계 상의 문제가 있을 수도 있음을 의미한다.
- DynamicUpdate를 사용해야 할 상황이라면, 테이블 설계 확인 및 책임 분리를 먼저 고려해보자.
3. 정리
- JPA Dirty Checking 기능은 모든 필드에 대해 update query가 나간다.
- 이는 모든 수정사항 경우에 대해 동일한 update query를 재사용 가능하게 하여 성능상 이점을 가지기 위함이다.
- 만약 동적으로 update query를 날리고 싶다면, DynamicUpdate 어노테이션을 Entity Class에 붙이자.
- 하지만 DynamicUpdate 어노테이션이 필요할만큼 필드 수가 많다면 테이블 설계 확인 및 책임 분리를 먼저 고려하자.
Source Code
Reference
'JPA' 카테고리의 다른 글
JpaRepository의 save() 메서드 내부동작 - Persist vs Merge (1) | 2023.07.14 |
---|---|
@Transactional(readOnly = true)를 왜 붙여야 하나요 (7) | 2023.05.04 |
Entity Class에 Lombok @NoArgsConstructor(access=PROTECTED)를 붙이는 이유 (0) | 2022.11.23 |
fetch join + Paging (limit) 처리 시 발생하는 문제 및 해결 (0) | 2022.07.19 |
Entity 삭제 시 JdbcSQLIntegrityConstraintViolationException: Referential integrity constraint violation 오류 발생 (0) | 2022.05.04 |