트랜잭션(Transaction) 안에서 엔티티(Entity)의 변경이 일어났을 때
변경한 내용을 자동으로 DB에 반영하는 것Dirty : 상태의 변화가 생김
Checking : 검사
주문 취소 기능
@Transactional
public void cancelOrder(Long orderId) {
//주문 엔티티 조회
Order order = orderRepository.findOne(orderId);
//주문 취소
order.cancel();
}
위 코드는 orderId를 통해 주문을 취소하는 메소드이다.
update와 같은 쿼리가 없음에도 실제로 메소드를 실행하면 데이터베이스에 update가 잘 이루어진다.
트랜잭션 시작
➔ orderId로 주문 Entity 조회
➔ 해당 Entity 주문 취소 상태로 Update
➔ 트랜잭션 커밋
👉 이를 가능하게 하는 것이 바로 '더티 체킹(Dirty Checking)'이다.
더티 체킹에서 "변화가 있다"의 기준은 최초 조회 상태이다.
JPA에서는 트랜잭션이 끝나는 시점에 변화가 있던 모든 엔티티의 객체를 데이터베이스로 알아서 반영시켜준다.
JPA에서 Entity를 조회
➔ 조회된 상태의 Entity에 대한 스냅샷 생성
➔ 트랜잭션 커밋 후 해당 스냅샷과 현재 Entity 상태의 다른 점을 체크
다른 점들을 update 쿼리로 데이터베이스에 전달
더티 체킹을 검사하는 대상은 영속성 컨텍스트가 관리하는 Entity에만 적용된다.
준영속, 비영속 Entity는 값을 변경할 지라도 데이터베이스에 반영시키지 않는다.
* 영속성 컨텍스트(엔티티를 영구 저장하는 환경)
* 엔티티의 생명주기
- 비영속(new/transient): 영속성 컨텍스트와 전혀 관계가 없는 상태
- 영속(managed): 영속성 컨텍스트에 저장된 상태
- 준영속(detached): 영속성 컨텍스트에 저장되었다가 분리된 상태
- 삭제(removed): 삭제된 상태
@Slf4j
@RequiredArgsConstructor
@Service
public class PayService {
private final PayRepository payRepository;
@Transactional
public void update(Long id, String tradeNo) {
Pay pay = payRepository.getOne(id);
pay.changeTradeNo(tradeNo);
}
테스트 코드를 작성해서 실행해보면
@Test
public void SpringDataJpa로_확인() {
//given
Pay pay = payRepository.save(new Pay("test1", 100));
//when
String updateTradeNo = "test2";
payService.update(pay.getId(), updateTradeNo);
//then
Pay saved = payRepository.findAll().get(0);
assertThat(saved.getTradeNo()).isEqualTo(updateTradeNo);
}
아래와 같이 정상적으로 update 쿼리가 수행됨을 확인할 수 있다.

JPA에서는 전체 필드를 업데이트하는 방식을 기본값으로 사용하기 때문에
기본적으로 더티 체킹을 실행하면, SQL에서는 변경된 엔티티의 모든 내용을 update 쿼리로 만들어 전달한다.
이때 필드가 많아지면(20~30개 이상) 전체 필드를 update하는게 비효율적일 수도 있다.
👉 이때는 @DynamicUpdate를 해당 Entity에 선언하여 변경 필드만 반영되도록 할 수 있다.
@Getter
@NoArgsConstructor
@Entity
@DynamicUpdate
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String product;