Post

JPA 알뜰신잡 1

JPA의 동작에 관하여

JPA 알뜰신잡 1

JPA의 1차 캐시에 대해서

Saga-Orchestrator를 개발하면서 결제가 완료되면 재고 예약을 각 주문 상품별로 진행하게 된다. 이 과정에서 모든 재고 예약이 완료되면 Saga의 상태를 업데이트하게 되는데 이 과정에서 JPA의 동작에 대해서 새로 알게된 점을 간단하게 정리하려고 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
SagaStepEntity sagaStep = sagaStepJpaRepository.findById(event.stepId())
            .orElseThrow(() -> new IllegalStateException("Saga step not found"));
        sagaStep.setStatus(SagaStepEntity.StepStatus.DONE);

        List<SagaStepEntity> inventoryReserveStep = sagaStepJpaRepository
            .findBySagaInstanceIdAndStepName(sagaId, "inventoryReserve");

        boolean allSuccess = inventoryReserveStep.stream()
            .allMatch(step -> step.getStatus() == SagaStepEntity.StepStatus.DONE);

        if(allSuccess){
            ...
        }
  }

위 코드를 보면 하나의 트랜잭션에서 각 step의 상태를 업데이트하지만 ‘마지막에 업데이트 되는 step이 업데이트 되더라도 findBySagaInstanceIdAndStepName에서 조회 할 때는 아직 transaction이 커밋되지 않았으니 allSuccess는 true가 될 수 없지 않을까?’에 대한 의문을 가지고 있었다.

결과적으로는 ‘아니다’

JPQL 실행 시 흐름

  1. JPQL → SQL 변환 → DB에 쿼리 전송
  2. DB에서 ResultSet을 반환
  3. JPA가 결과를 엔티티로 매핑하려고 할 때
  4. 각 row의 PK를 기준으로 1차 캐시에 있는지 먼저 확인
    1. 있으면: 기존 인스턴스 사용 (== 같음)
    2. 없으면: 새 객체를 생성하고 1차 캐시에 등록

즉, DB 쿼리는 날렸지만, 결과를 엔티티로 변환하는 시점에 1차 캐시가 먼저 참조.

핵심 포인트

✅ 조회 “완료 후”가 아니라 “조회 결과를 엔티티로 매핑할 때” 1차 캐시가 작동합니다.

이 때문에: 같은 트랜잭션 안에서 조건이 달라도 같은 PK를 가진 엔티티는 결국 1차 캐시에 있던 객체를 재사용.

This post is licensed under CC BY 4.0 by the author.