@Transactional
public void orderProduct(Long memberId) {
Member member = memberRepository.findById(memberId).orElseThrow(
() -> new CustomException(BAD_REQUEST));
List<Basket> baskets = basketQueryRepository.findBasketByMemberId(memberId);
String orderNum = UUID.randomUUID().toString();
LocalDateTime orderTime = LocalDateTime.now();
for (Basket basket : baskets) {
Orders orders = Orders.builder()
.orderNum(orderNum)
.orderTime(orderTime)
.productNum(basket.getProductQuantity())
.productPrice(basket.getProducts().getPrice())
.orderStatus(OrderStatus.배송준비)
.member(member)
.products(basket.getProducts()).build();
validateStock(basket.getProducts(), basket);
decreaseProductStock(basket.getProducts(), basket.getProductQuantity());
updateProductStatus(basket.getProducts());
orderRepository.save(orders);
}
}
의도한 대로 로직이 흘러가기 위해선 Basket의 데이터를 가져올 때 먼저 실행된 스레드가 Update 쿼리를 보낸 후 실행되게 하면 된다.
즉, 동기화를 해주면 해결되는 문제다.
synchronized
키워드를 통한 동기화 public synchronized void orderProduct(Long memberId) {
Member member = memberRepository.findById(memberId).orElseThrow(
() -> new CustomException(NOT_FOUND_MEMBER));
List<Basket> baskets = basketQueryRepository.findBasketByMemberIdNoneLock(memberId);
String orderNum = makeOrderNumber();
LocalDateTime orderTime = makeOrderDataTime();
for (Basket basket : baskets) {
Products products = basket.getProducts();
Orders orders = makeOrderByBuilder(member, orderNum, orderTime, basket, products);
validateStock(products, basket);
decreaseProductStock(products, basket.getProductQuantity());
updateProductStatus(basket.getProducts());
orderRepository.save(orders);
productRepository.save(products);
}
}
synchronized
키워드를 추가해주면 한번에 하나의 스레드가 접근할 수 있도록 동기화를 해준다.synchronized
키워드는 같은 프로세스에서만 동기화를 보장해주기 때문에 서버가 2개 이상일 경우 동시성 문제를 보장해주지 않는다.