변경 감지는 영속성 컨텍스트가 관리하는 엔티티의 값이 변경되었는지 확인하고, 수정 SQL을 자동으로 생성하는 기능입니다. 따라서 영속 상태의 엔티티는 값을 변경한 뒤 별도의 update()나 save()를 호출하지 않아도 트랜잭션 커밋 시 데이터베이스에 반영됩니다.
엔티티가 영속성 컨텍스트에 들어오면 JPA 구현체는 일반적으로 최초 상태를 스냅샷으로 보관합니다. 플러시가 발생하면 현재 엔티티의 상태와 스냅샷을 비교하고, 변경된 엔티티가 있으면 UPDATE SQL을 쓰기 지연 저장소에 등록한 뒤 데이터베이스에 실행합니다. 플러시는 보통 트랜잭션 커밋 직전이나 JPQL 실행 전에 발생합니다.
변경 감지는 영속 상태의 엔티티에만 적용됩니다. 준영속 또는 비영속 엔티티의 값을 바꿔도 자동으로 반영되지 않으며, JPQL 벌크 연산은 영속성 컨텍스트를 거치지 않고 데이터베이스를 직접 수정하므로 변경 감지와 별개로 동작합니다.
@Transactional
public void changeName(Long memberId, String name) {
Member member = entityManager.find(Member.class, memberId);
member.changeName(name);
// 커밋 시 변경 감지로 UPDATE SQL 실행
}플러시와 커밋의 차이는 무엇인가요?
플러시는 영속성 컨텍스트의 변경 내용을 SQL로 데이터베이스에 동기화하는 과정이고, 커밋은 데이터베이스 트랜잭션을 최종 확정하는 과정입니다. 플러시 후에도 트랜잭션이 롤백되면 변경 내용은 확정되지 않습니다.
준영속 엔티티를 수정하면 변경 감지가 동작하나요?
동작하지 않습니다. 변경 감지는 현재 영속성 컨텍스트가 관리하는 엔티티만 대상으로 합니다. 일반적으로 엔티티를 다시 조회하여 필요한 값만 변경하고, 꼭 필요한 경우
merge()가 반환한 영속 엔티티를 사용합니다.
JPQL 벌크 수정 후 영속성 컨텍스트를 비워야 하는 이유는 무엇인가요?
벌크 연산은 영속성 컨텍스트를 무시하고 데이터베이스를 직접 변경하기 때문에 1차 캐시의 엔티티가 이전 값을 유지할 수 있습니다. 벌크 연산을 먼저 실행하거나, 실행 후
clear()또는@Modifying(clearAutomatically = true)로 불일치를 방지해야 합니다.
@DynamicUpdate