TIL

7_3.롤백 전략과 커밋 관리

꿀승 2025. 2. 7. 10:50
728x90
반응형
SMALL

학습내용

  1. 롤백과 커밋
  2. 트랜잭션 롤백 전략
  3. 부분커밋 (entityManager)

학습정리

1.롤백과 커밋

  • 커밋

    • 트랜잭션 작업이 성공적으로 완료 된 경우 변경 사항을 영구적으로 저장

    • 원리 예제코드

        @Transactional
        public void sellProduct(Long productId, int quantity) {
          // 상품 조회: 존재하지 않으면 예외 발생
          Product product = productRepository.findById(productId)
              .orElseThrow(() -> new ServiceException(ServiceExceptionCode.NOT_FOUND_PRODUCT));
      
          // 재고 차감: 엔티티 내 reduceStock 메서드를 활용
          product.reduceStock(quantity);
      
          // 변경 사항 저장: 트랜잭션 커밋 시점에 DB에 반영됨
          productRepository.save(product);
        }
  • 롤백

    • 트랜잭션 작업이 실패시 모든 작업을 취소하고 작업 전 상태로 되돌림

    • 원리 예제코드

        @Transactional
        public void sellProductWithError(Long productId, int quantity) {
          // 상품 조회
          Product product = productRepository.findById(productId)
              .orElseThrow(() -> new ServiceException(ServiceExceptionCode.NOT_FOUND_PRODUCT));
      
          // 재고 부족 체크: 재고가 부족하면 예외 발생하여 트랜잭션 롤백
          if (product.getStock() < quantity) {
            throw new ServiceException(ServiceExceptionCode.OUT_OF_STOCK_PRODUCT);
          }
      
          // 재고 차감
          product.reduceStock(quantity);
      
          // 변경 사항 저장: 예외가 없으면 커밋되어 DB에 반영됨
          productRepository.save(product);
        }

2. 트랜잭션 롤백 전략

  • 체크 예외(Checked Exception)

    • 컴파일시에 반드시 예외 처리가 요구 됨

    • Spring에서는 체크예외 발생시에도 트랜잭션 롤백하지 않음

    • 예제코드

      //checked예외는 발생하면 롤백이 안됨
      //rollbackFor을 사용하면 해당 예외가 발생하면 롤백처리 됨
      //메서드에서 throws가 필수면 보통 체크드 예외
      @Transactional(rollbackFor = CustomCheckedException.class)
      public void updateProductPrice(Long productId, BigDecimal newPrice)
          throws CustomCheckedException {
        Product product = productRepository.findById(productId)
            .orElseThrow(() -> new ServiceException(ServiceExceptionCode.NOT_FOUND_PRODUCT));
      
        product.setPrice(newPrice);
      
        if (newPrice.compareTo(BigDecimal.ZERO) < 0) {
          throw new CustomCheckedException("가격은 0보다 커야합니다.");
        }
      
        productRepository.save(product);
      }
  • 언체크 예외(UnChecked Exception)

    • 런타임 시 발생하며, 강제 처리가 요구되지 않음

    • Spring에서는 언체크예외 발생시 트랜잭션을 롤백함.

      //unchecked예외는 발생하면 무조건 롤백됨
      //noRollbackFor을 사용해서 롤백되지 않도록 처리
      @Transactional(noRollbackFor = IllegalArgumentException.class)
      public void reduceProductStockNoRollback(Long productId, Integer quantity) {
        Product product = productRepository.findById(productId)
            .orElseThrow(() -> new ServiceException(ServiceExceptionCode.NOT_FOUND_PRODUCT));
      
        product.reduceStock(quantity);
        productRepository.save(product);
      
        if (product.getStock() < quantity) {
          throw new IllegalArgumentException("재고가 부족합니다.");
        }
      
      }
      

3. 부분 커밋

  • 예시코드

    @Service
    public class ProductBatchService {
    
        @PersistenceContext
        private EntityManager entityManager;
    
        /**
         * 대량의 제품 목록에 대해 재고를 감소시키는 작업을 배치 단위로 처리합니다.
         * 각 배치가 끝날 때마다 flush()와 clear()를 호출하여 메모리 사용량을 최적화합니다.
         */
      @Transactional
      public void batchUpdateProductStock(List<Product> productList, int stockDecrease) {
        int batchSize = 10;
    
        for (int i = 0; i < productList.size(); i++) {
          Product product = productList.get(i);
          // 판매 후 재고 감소 처리
          product.reduceStock(stockDecrease);
          entityManager.merge(product);
    
          // 배치 단위마다 커밋 및 영속성 컨텍스트 초기화
          if ((i + 1) % batchSize == 0) {
            flushAndClear();
          }
        }
    
        // 잔여 데이터 처리
        flushAndClear();
      }
    
      private void flushAndClear() {
        entityManager.flush(); // 변경 사항을 데이터베이스에 반영
        entityManager.clear(); // 영속성 컨텍스트 초기화 (캐시된 엔티티 제거)
      }
    }
    

ps. 금일에는 트랜잭션의 롤백 전략을 배웠는데 이번 주에 트랜잭션에 대해서 깊이 배우고
어떤식으로 사용해야하는지 자세히 배운 것 같아서 좋았다.

728x90
반응형
LIST