솔솔

[JPA] JPA에서 @Transactional 없으면 UPDATE가 실행되지 않는 이유 본문

나의보물들/JPA

[JPA] JPA에서 @Transactional 없으면 UPDATE가 실행되지 않는 이유

솔솔하네 2025. 3. 20. 22:51
반응형

🍀 들어가기


아래와 같은 코드로 테스트를 진행을 하는데

// Test 코드
@SpringBootTest
class OrderServiceTest { 

    @Autowired
    private StockRepository stockRepository;

    @Autowired
    private OrderService orderService;
    
    @DisplayName("재고와 관련된 상품이 포함되어 있는 주문번호 리스트를 받아 주문을 생성한다")
    @Test
    void createOderWithStock() {
        ...(생략)
    	stockRepository.save(stock);
        
        orderService.createOrder(request)
        ...(생략)
        
        <내가 생각한 Stock의 quantity와 니가 수행한 Stock의 quantity가 같니??>
    }
    
}

 

테스트가 실패했다.

 

 

왜 실패했는지 찾아보니

// 서비스 코드
@RequiredArgsConstructor
@Service
public class OrderService {

    private final StockRepository stockRepository;
    
    publci OrderResponse createOrder(OrderCreateRequest request){
    	...생략
        // Stock 엔티티에서 quantity만큼 재고를 감소 시키는 메소드
        stock.deductQuantity(quantity);
        ...생략
    }
}
// Stock 엔티티 코드
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
public class Stock extends BaseEntity {
	...생략
    // Stock의 재고를 감소시키는 메소드
    public void deductQuantity(int quantity) {
        if (isQuantityLessThan(quantity)) {
            throw new IllegalArgumentException("차감할 재고 수량이 없습니다.");
        }
        this.quantity -= quantity;
    }
}

 

stock1.deductQuantity(quantity); 엔티티의 재고를 감소시키는 메서드라면

변경 감지(Dirty Checking) 작동해야 UPDATE 쿼리가 발생하고 재고가 감소하는데 

변경 감지(Dirty Checking) 작동하지 않아 UPDATE 쿼리가 발생하지 않았기 때문이다.

 

 stock.deductQuantity(quantity); 호출하면 객체의 필드 값이 변경되지만,

변경된 상태가 영속성 컨텍스트에 반영되지 않았던 것이다.

 

여기서 궁금했다.

왜 변경 감지가 일어나지 않았을까?

 

 

🍀 JPA에서 @Transactional 없으면 UPDATE가 실행되지 않는 이유


결론적으로 JPA를 사용하면서 변경 감지(Dirty Checking)를 작동시켜 UPDATE 쿼리를 발생시킬려면

Business Layer 또는 Test 코드 쪽에 @Transcational 어노테이션을 달아놓은 후

// 서비스 코드
@RequiredArgsConstructor
@Transactional
@Service
public class OrderService {
    ...생략
}


// Test 코드
@Transactional
@SpringBootTest
class OrderServiceTest { 

    ...생략
    
}

 

다시 테스트를 해보면 변경 감지(Dirty Checking)가 작동하면서 UPDATE 쿼리가 발생하고

테스트가 성공한다.

 

 

🤔 근데 여기서 왜 @Transcational이 없이 UPDATE가 실행되지 않을까?

 

Spring Data JPA에서는 엔티티 상태를 추적하여 변경된 내용만을 DB에 반영하는 Dirty Checking 기능을 제공한다.

하지만 이 기능은 트랜잭션 범위 내에서만 동작한다.

만약 @Transactional 없이 엔티티를 수정하면, 트랜잭션이 관리되지 않기 때문에 변경 사항이 DB에 반영되지 않는다.

@Transactional이 트랜잭션의 범위를 설정하고 영속성 컨텍스트의 상태를 관리한다!!

 

 

✅ JPA에서의 Dirty Checking 동작 원리

 

@Transactional이 붙은 메서드에서는 JPA의 영속성 컨텍스트(Persistence Context)가 활성화되고, 이 컨텍스트는 엔티티의 상태를 추적합니다. 엔티티가 수정되면, 영속성 컨텍스트가 그 변경을 감지하고, 트랜잭션이 커밋될 때 해당 변경을 DB에 반영하는 Dirty Checking이 이루어진다.

// 서비스 코드
@RequiredArgsConstructor
@Transactional
@Service
public class OrderService {

    private final StockRepository stockRepository;
    
    publci OrderResponse createOrder(OrderCreateRequest request){
    	...생략
        
        stock.deductQuantity(quantity); // ✅ Dirty Checking으로 변경 감지됨
        // @Transactional 덕분에 트랜잭션이 끝날 때 자동으로 UPDATE 실행됨
        
        ...생략
    }
}

 

 

 

🍀 @Transactional 없이도 UPDATE 쿼리를 실행하려면?


@Transactional 없이 엔티티를 수정하고 DB에 반영하려면

명시적으로 save() 메서드를 호출하여 엔티티 상태를 영속성 컨텍스트에 반영해야 함.

// 서비스 코드
@RequiredArgsConstructor
@Service
public class OrderService {

    private final StockRepository stockRepository;
    
    publci OrderResponse createOrder(OrderCreateRequest request){
    	...생략
        
        stock.deductQuantity(quantity); 
        stockRepository.save(stock); // 명시적으로 save() 호출하여 변경 사항을 DB에 반영
        
        ...생략
    }
}

 

JPA는 save()를 호출할 때 내부적으로 UPDATE 쿼리를 생성하고 실행하기 때문에,

@Transactional 없이도 엔티티의 변경을 DB에 반영할 수 있다.

 

그런데 말입니다..

 

StockRepository에서 상속받은 JpaRepository를 쭉쭉 타고올라가다보면

save()가 있는 CrudRepository가 있고

save()의 구현체를 살펴 보면

@Transactional이 있는 것을 확인 할 수 있다.

 

결론적으로 save()를 호출할 때 내부적으로 UPDATE 쿼리를 생성하고 실행할 수 있는건

@Transactional이 있었기 때문이다.

 

 

 

 

'나의보물들 > JPA' 카테고리의 다른 글

[JPA] @Modifying과 @Transactional의 연관성  (0) 2024.08.12
[JPA] 트러블 슈팅 : build.gradle 설정 오류  (0) 2024.08.10
[JPA] JPA란?  (0) 2024.08.01