엔티티의 필드 값을 수정하고 반영하는데 변경 감지 혹은 벌크 수정 쿼리를 사용할 수 있다.
예시로 Waiting 엔티티의 필드를 수정해 보며 둘의 차이점을 알아보자.

1. 영속성 컨텍스트와 DB 간 불일치 가능성
변경 감지에서는 영속성 컨텍스트와 DB가 항상 일치한다.
반면 벌크 수정 쿼리는 상황에 따라 영속성 컨텍스트와 DB가 불일치 할 수 있다.
변경 감지
엔티티를 repository에 저장하면 해당 엔티티가 영속화되어 영속성 컨텍스트에서 관리된다.
따라서 setStatus()로 status 필드를 PROGRESS에서 CANCELED로 수정했을 때
영속성 컨텍스트는 변경 감지를 하여 엔티티의 수정 사항을 반영한다.

참고로 update 쿼리와 h2 필드를 확인하기 위해 @Rollback(false)를 추가하였다.
로그를 확인해보면 엔티티 조회 시 수정 사항이 잘 반영되었다.
또한, 트랜잭션이 끝나면 commit()되어서 DB에 업데이트 쿼리가 나간다.

1차 캐시에서 조회했을 때와 DB에서 조회했을 때 Status 필드가 동일하다.

벌크 수정연산
아래 코드는 현재 웨이팅 상태를 newStatus로 변경하는 쿼리이다.

해당 함수를 활용하여 아까와 동일하게 상태 필드를 PROGRESS에서 CANCELED로 변경했다.
벌크 연산은 영속성 컨텍스트를 거치지 않고, 바로 DB로 업데이트하기 때문에
영속성 컨텍스트는 엔티티의 변경사항을 알 수 없다.

실제로 1차 캐시에서 엔티티를 조회 시 변경 사항이 반영되지 않았음을 확인할 수 있다.

반면 DB에는 업데이트 쿼리가 날아가 status가 변경되었다.


한 트랜잭션 내에서 엔티티를 저장한 후 수정된 필드를 조회할 때 영속성 컨텍스트와 DB가 불일치하는 것이다.
벌크 연산 수행 시 이러한 불일치성을 해결하기 위해서는 아래의 작업들이 필요하다.
1. 벌크 연산 수행 전, 쓰기 지연 SQL 저장소 내 쿼리를 flush한다.
2. 벌크 연산 수행 후, 영속성 컨텍스트를 초기화한다.
@Modifying 옵션 flushAutomatically와 clearAutomatically를 true로 설정하면 1,2번을 각각 수행한다.

사실 엔티티 매니저의 FlushModeType=AUTO가 기본 설정이므로, JPQL 쿼리 실행 전 자동으로 플러시가 호출된다.
따라서 위 예제에서 flushAutomatically 옵션은 없어도 무방하다.
위와 달리, 영속성 컨텍스트와 DB의 불일치를 걱정하지 않아도 되는 경우가 있다.
바로 벌크 연산 진행 후 다른 트랜잭션에서 bulk update된 엔티티를 조회할 때이다.
트랜잭션이 종료되면 영속성 컨텍스트와 그 안에 1차 캐시는 지워지기 때문이다.

bulk 연산으로 status를 canceled로 변경한 후, 다른 테스트 메서드에서 조회하면
벌크 연산이 DB에 나갔고, 영속성 컨텍스트가 비워진 후이므로 아래의 테스트가 성공한다.


2. 준영속 상태일 때 업데이트 반영 여부
변경 감지는 영속성 컨텍스트에서 관리해주므로 준영속 상태일 때 변경 사항이 업데이트가 되지 않는다.
반면 벌크 업데이트는 직접 DB에 쿼리를 날리므로 준영속 상태일 때도 변경 사항이 업데이트 된다.
변경 감지
변경 감지가 발생하려면 대상 엔티티가 영속 상태여야 한다.
JPA는 1차 캐시에 저장된 엔티티의 스냅샷과 현재 상태를 비교하고,
변경이 감지되면 이에 맞는 업데이트 쿼리를 생성해 쓰기 지연 SQL 저장소로 보내기 때문이다.

영속 상태이므로 변경 감지에 성공하여 업데이트 쿼리가 잘 나간다.

그렇다면 em.detach()로 엔티티를 준영속화 하면 어떻게 될까?

영속성 컨텍스트에서 더 이상 관리되지 않으므로, 변경 감지가 일어나지 않아 업데이트 쿼리가 생성되지 않는다.

벌크 수정 쿼리
벌크 수정 연산은 영속성 컨텍스트를 거치지 않고 바로 쿼리를 날린다.
따라서 변경하려는 엔티티들이 더이상 영속성 컨텍스트에서 관리되지 않더라도, DB에 수정 쿼리가 잘 날아간다.


3. 쿼리
변경 감지
로그를 확인해보면, 변경 감지 시 엔티티 모든 필드에 대한 업데이트 쿼리가 나간다.

이는 모든 필드를 업데이트 하면 수정 쿼리가 항상 같기 때문이다. (바인딩되는 데이터는 다르다.)
따라서 애플리케이션 로딩 시점에 쿼리를 미리 생성하고 재사용할 수 있다.
또한, 트랜잭션 내에서 변경 사항이 여러 번 발생하더라도 트랜잭션 커밋 직전 최종 변경 사항만 DB에 반영한다.
덕분에 애플리케이션과 데이터베이스 간에 전송되는 데이터가 적어질 수 있다.
벌크 연산
변경 감지에서는 각 엔티티에 대한 개별 쿼리가 나간다면,
벌크 연산은 모든 엔티티에 대해 단일 업데이트 쿼리가 나간다.

수정한 필드에 대한 업데이트 쿼리만 나간다.
따라서 대규모 데이터를 빠르게 업데이트하거나 삭제해야 할 때 유용하다.
정리
둘의 차이점에 기반하여 변경 감지와 벌크 수정 연산의 장단점을 아래와 같이 정리할 수 있다.
변경 감지
장점
- JPA가 변경 사항을 감지하므로, 개발자가 직접 업데이트 쿼리를 작성하지 않아도 된다.
단점
- 각 엔티티에 대해 개별적인 업데이트 쿼리가 나가므로, 대량의 데이터를 다룰 때 성능이 저하될 수 있다.
- 엔티티가 영속 상태가 아니면 변경 감지가 발생하지 않는다.
벌크 수정 쿼리
장점
- 단일 쿼리로 특정 필드만 업데이트할 수 있으므로, 대량의 데이터를 빠르게 업데이트할 수 있다.
단점
- 영속성 컨텍스트를 무시하고 데이터베이스에 직접 적용된다. 상황에 따라 영속성 컨텍스트와 DB 간 동기화가 안될 수 있다.
Reference
책 자바 ORM 표준 JPA 프로그래밍