1. ObjectOptimisticLockingFailureException은 트랜잭션 커밋 시점에 충돌여부를 확인하여 예외를 발생시킨다
2. 그럼 지금 위 코드에서 catch는 정말로 예외를 catch할 수 있을까?
-> @Transactional을 통해 명시적으로 트랜잭션을 시작한다. 동시에 영속성 컨텍스트를 연다
-> 따라서 커밋 시점은 트랜잭션이 종료되는, 즉 해당 매서드가 종료되는 시점에 커밋이 발생한다
-> 즉 매서드 종료 후 커밋이 되기때문에 catch에 절대 걸리지않는다.
해결 방안 :
우리의 프로젝트에서 facade는 도메인 상태정보를 갖지 않는 협업의 공간으로 활용하기로 약속했다.
productService는 상품의 도메인 서비스다
즉 facade는 협업의 공간이다. 이를 활용한다
해결 방법 :
1. 현재 문제점은 매서드 종료시점에 발생하는 예외를 매서드 내부에서 잡으려고하는 바보짓
-> 자바의 예외는 외부 내부 매서드가 존재한다고 했을 때 내부의 매서드가 throw하면 외부의 매서드에서 이를 catch할 수 있다.
2. 따라서 외부의 매서드로 한 겹 감싼다.
-> 아래 코드에서 editProductStock()매서드가 트랜잭션을 시작하면서 영속성 컨텍스트를 열고 매서드 종료시점에 변경감지를 통해 update
-> 이 때 version 충돌이 일어나면 예외를 던진다.
-> 그럼 외부 facade의 consumeOrderCompletedEvent()매서드는 2번에서 발생한 예외를 catch할 수 있고 이 방법으로 해결
---- facade
@KafkaListener(topics = "orderCompletedEvent", groupId = "product", containerFactory = "orderCompletedEventListener")
public void consumeOrderCompletedEvent(Events.OrderCompletedEvent event) {
try {
productService.editProductStock(event);
} catch (ObjectOptimisticLockingFailureException e) {
log.error("동시 접근으로인한 재고 수정 실패");
}
}
----- productService
@Transactional
public void editProductStock(Events.OrderCompletedEvent event) throws ObjectOptimisticLockingFailureException{
Product product = productService.findById(event.getProductId());
product.orderProduct(event.getOrderedProductCount());
}
아쉬운 점 :
사실 매서드를 분리하고 트랜잭션 전파 레벨 new를 통해 항상 새로운 트랜잭션을 시작하도록하면 해결할 수 있을줄알았는데 x
-> 아래 코드에서 editProductStock()가 시작할때 트랜잭션을 시작하고 종료할 때 커밋하면서 예외를 던질 줄 알았는데 x
@KafkaListener(topics = "orderCompletedEvent", groupId = "product", containerFactory = "orderCompletedEventListener")
public void consumeOrderCompletedEvent(Events.OrderCompletedEvent event) {
try {
editProductStock(event);
} catch (ObjectOptimisticLockingFailureException e) {
log.error("동시 접근으로인한 재고 수정 실패");
}
}
@Transactional(propagetion = Propagation.REQUIRES_NEW)
protected void editProductStock(event) throw ObjectOptimisticLockingFailureException{
Product product = productService.findById(event.getProductId());
product.orderProduct(event.getOrderedProductCount());
}
기존 코드 :
문제 :
해결 방안 :
아쉬운 점 :