관리 메뉴

HeBhy, since 1983.

Spring @Transaction에서 Method별 선택적 전역 롤백 방지 (Prevent the JPA global transaction rollback on the specific method) 본문

Dev/Web

Spring @Transaction에서 Method별 선택적 전역 롤백 방지 (Prevent the JPA global transaction rollback on the specific method)

HeBhy 2020. 7. 24. 23:08

Spring에서 hibernate(Jpa)사용시, Spring transaction(@org.springframework.transaction.annotation.Transactional)을 사용하면 JpaTransactionManager.setGlobalRollbackOnParticipationFailure(true)가 기본값이라서.. method 단위 호출시 하위 어느곳에서라도 에러가 뜨면 전체가 롤백되어버리는데요,

 

대안으로 @Transactional(propagation = Propagation.NESTED)를 사용하려고 하면, 전역롤백 설정을 변경하지 않고는 해당 method에서만 롤백을 방지하는건 불가능하다는 에러가 뜹니다.

 

RollbackException: Transaction marked as rollbackOnly

 

그럼 꼭 전역설정을 바꿔야 롤백을 막을수 있나? 그건 권장되지 않으니 그럴 필요는 없습니다.

 

예를 들어, 결제 처리를 하고 이메일을 보내야 하는데, 이메일 스케쥴에서 에러가 떴을 경우(이미 결제가 된 경우) 무시하고 정상 주문으로 처리하고 싶을 경우는 다음과 같이 하시면 됩니다.

즉, [2]에서 에러가떠도 [1]은 롤백하지 않습니다.

그러려면 아래 doTX() 에서 전역설정에 우선하기위해 noRollbackFor를 선언해줘야 합니다.

 

@Transactional(........)
public class OrderResetController ...... {

  @Transactional(noRollbackFor = { Exception.class })   // class 전역 transactional보다 우선
  @PostMapping("/order/doTx")
  public Object doTx(HttpServletRequest request) {

    HashMap<String, Object> entity = new HashMap<String, Object>();
    
    Order order= this.orderyService.orderTx(request);   // [1] 여기(결제)는 정상 수행되면
    this.orderService.sendOrderMail(order, request);    // [2] 여기(메일발송)은 실패해도 정상처리

    entity.put("message", "결제가 완료되었습니다.");		

  return new JSONObject(entity);
  }
}

 

그럼 이제 각각 orderTx()와 sendOrdermail()에서도(participated method) 새로운 트랜젝션을 잡도록 명시해줍니다.(둘 다 해주셔야 합니다. 트랜젝션 수가 늘어나는게 아니라, 이전 트랜젝션을 hold하고 진행하는 것입니다.)

 

(*주의: transaction이 분리되어 있으므로, 위 경우 order entity가 [2]에서는 detach되니 lazy child를 이용하기 위해선 다시 persist 해주셔야 합니다.)

public class OrderServiceImpl {

  @Transactional(propagation = Propagation.REQUIRES_NEW)
  public orderTx(....) {
    // .........
  }


  @Transactional(propagation = Propagation.REQUIRES_NEW)
  public sendOrderMail(....) {
    // .........
  }


}

 

이상입니다.

 

(참고: stackoverflow.com/questions/19349898/unexpectedrollbackexception-transaction-rolled-back-because-it-has-been-marked)

Comments