JPA Dirty Check

엔티티 객체의 필드 값이 트랜잭션 안에서 변경되었을 때, 명시적으로 save() 하지 않아도 JPA가 자동으로 UPDATE SQL을 수행함.

  • update 메서드를 직접 호출하지 않아도 됨. (save() 필요 없음)

  • 트랜잭션 안에서 객체 상태만 변경하면 JPA가 자동 감지하여 SQL을 생성

  • 비즈니스 로직에서 엔티티 중심 설계 가능

 public OrderResponse createOrder(OrderCreateRequest request, LocalDateTime registered) {

        List<String> productNumbers = request.getProductNumbers();

        List<Product> products = findProductsBy(productNumbers);

        // 재고 차감 체크가 필요한 상품들 filter
        List<String> stockProductNumbers = products.stream()
                .filter(product -> ProductType.containStockType(product.getType()))
                .map(Product::getProductNumber)
                .collect(Collectors.toList());
        // 재고 엔티티 조회
        List<Stock> stocks = stockRepository.findAllByProductNumberIn(stockProductNumbers);

        Map<String, Stock> stockMap = stocks.stream()
                .collect(Collectors.toMap(Stock::getProductNumber, s -> s));

        // 상품별 couting
        Map<String, Long> productCountingMap = stockProductNumbers.stream()
                .collect(Collectors.groupingBy(p -> p, Collectors.counting()));

        // 재고 차감 시도
        for(Stock stock : stockMap.values()) {

            int quantity = productCountingMap.get(stock.getProductNumber()).intValue();
            if(stock.isQuantityLessThan(quantity)){
                throw new IllegalArgumentException("재고가 부족한 상품이 있습니다.");
            }
            stock.deductQuantity(quantity);
           // stockRepository.save(stock);

        }
        Order order = Order.create(products,registered);

        Order savedOrder = orderRepository.save(order);
        // 저장이 된 후에야 Order에 id값 부여됨
        return OrderResponse.of(savedOrder);
    }

JPA에서 최초에 조회해올 때 스냅샷을 찍고, 마지막 Transaction 종료 시점에 인스턴스를 비교 후 달라진 부분이 있다면 update 쿼리 발생.

🔸 변경 감지가 동작하는 조건

조건
설명

영속 상태

EntityManager가 관리하는 객체여야 함 (find(), save() 등으로 불러온 것)

트랜잭션 범위 안

@Transactional로 트랜잭션이 시작되어 있어야 함

필드 변경

엔티티 객체의 필드 값을 setter나 메서드로 변경해야 함

flush 시점

트랜잭션 커밋 시점 또는 flush() 호출 시점에 변경 감지 실행

🔸 Dirty Checking과 merge()의 차이

항목

Dirty Checking

merge()

대상

영속 상태의 엔티티

비영속 상태의 엔티티

방식

객체 필드 변경 자동 감지

객체의 모든 값 복사 (값이 null이어도 덮어씀)

용도

트랜잭션 내 값만 변경하고 싶을 때

외부 DTO 값을 업데이트할 때

// Dirty Checking (추천 방식)
order.setStatus(OrderStatus.COMPLETED);

// merge (주의: null 필드도 덮어씀)
em.merge(orderDto.toEntity());

🔸 변경 감지가 적용되지 않는 경우

  • 트랜잭션 밖에서 변경

  • new로 만든 객체 (영속 아님)

  • 영속성 컨텍스트에서 분리된 상태 (detach, clear, close 등)

Last updated