You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
기본 정책과 무관하게 특정 예외를 강제로 롤백하고 싶으면 rollbackFor 를 사용하면 된다. (해당 예외의 자식 도 포함된다.)
rollbackFor = MyException.class 을 지정했기 때문에 MyException 이 발생하면 체크 예외이지만 트랜잭션이 롤백된다.
예외와 트랜잭션 커밋, 롤백 - 활용
스프링은 왜 체크예외는 커밋하고, 언체크 예외는 롤백할까?
스프링은 기본적으로 체크예외는 비즈니스 의미가 있을때 사용하고, 언체크 예외는 복구가 불가능한 예외로 가정한다.
체크 예외 : 비즈니스 의미가 있을때 사용
언체크 예외: 복구 불가능한 예외 -> 시스템에서 발생한 에러(데이터베이스 접근 안됨, sql쿼리문 오류)
꼭 이걸 따를필욘 없고, rollbackfor 옵션으로 롤백하면 된다.
그런데 비즈니스 의미가 있는 예외라는것이 무슨뜻일까?
비즈니스 요구사항
주문을 하는데 상황에 따라 다음과 같이 조치한다.
정상: 주문시 결제를 성공하면 주문 데이터를 저장하고 결제 상태를 완료 로 처리한다.
시스템 예외: 주문시 내부에 복구 불가능한 예외가 발생하면 전체 데이터를 롤백한다.
비즈니스 예외: 주문시 결제 잔고가 부족하면 주문 데이터를 저장하고, 결제 상태를 대기 로 처리한다.
이 경우 고객에게 잔고 부족을 알리고 별도의 계좌로 입금하도록 안내한다.
이때 결제 잔고 부족하면 NotEnoughMoneyException 이라는 체크 예외가 발생한다고 가정하겠다.
이 예외는 시스템 문제가 있어서 발생한게 아니라, 비즈니스 상황에서 문제가 되기 때문에 발생한 예외.
고객의 잔고가 부족한것은 시스템 문제가 아니라 시스템은 문제없이 동작하고 비즈니스 상황이 예외인 것이다.
이런 예외를 비즈니스 예외라고 한다.
비즈니스 예외는 매우 중요하고, 반드시 처리해야 하는 경우가 많으므로 체크 예외를 고려할 수 있다.
코드로 이 상황을 만들어보자.
결제 잔고가 부족하면 발생하는 비즈니스 예외이다. Exception 을 상속 받아서 체크 예외가 된다.
이런 상황에서 익셉션을 던지는게 아니라, 트리거 역할을 추가해서 분기를 태우는게 어떨까?
@Entity@Table(name = "orders") // 맵핑 디비에선 order 라는 테이블 사용 불가.@Getter@SetterpublicclassOrder {
@Id@GeneratedValueprivateLongid;
privateStringusername; // 정상, 예외, 잔고부족privateStringpayStatus; // 대기, 완료
}
JPA를 사용하는 Order 엔티티이다.
예제를 단순하게 하기 위해 @Getter , @Setter 를 사용했다. 참고로 실무에서 엔티티에 @Setter 를 남발해 서 불필요한 변경 포인트를 노출하는 것은 좋지 않다.
주의!@Table(name = "orders") 라고 했는데, 테이블 이름을 지정하지 않으면 테이블 이름이 클래스 이름 인 order 가 된다. order 는 데이터베이스 예약어( order by )여서 사용할 수 없다. 그래서 orders 라는 테 이블 이름을 따로 지정해주었다.
참고로 테이블 자동 생성은 application.properties 에 spring.jpa.hibernate.ddl-auto 옵션을 조정할 수 있다.
none : 테이블을 생성하지 않는다.
create : 애플리케이션 시작 시점에 테이블을 생성한다. (기본값)
complete()
사용자 이름을 정상 으로 설정했다. 모든 프로세스가 정상 수행된다.
다음을 통해서 데이터가 완료 상태로 저장 되었는지 검증한다. assertThat(findOrder.getPayStatus()).isEqualTo("완료");
runtimeException()
사용자 이름을 예외 로 설정했다. RuntimeException("시스템 예외") 이발생한다.
런타임 예외로 롤백이 수행되었기 때문에 Order 데이터가 비어 있는 것을 확인할 수 있다.
bizException()
사용자 이름을 잔고부족 으로 설정했다. NotEnoughMoneyException("잔고가 부족합니다") 이발생한다.
체크 예외로 커밋이 수행되었기 때문에 Order 데이터가 저장된다.
다음을 통해서 데이터가 대기 상태로 잘 저장 되었는지 검증한다. assertThat(findOrder.getPayStatus()).isEqualTo("대기");
정리
NotEnoughMoneyException 은 시스템에 문제가 발생한 것이 아니라, 비즈니스 문제 상황을 예외를 통해 알려준다.
마치 예외가 리턴 값 처럼 사용된다. 물론 예외를 사용하지 않고 리턴 값으로 사용해도 된다.
따라서 이 경우에는 트랜잭션을 커밋하는 것이 맞다. 이 경우 롤백하면 생성한 Order 자체가 사라진다. 그러면 고객에게 잔고 부족을 알리고 별도의 계좌로 입금하도록 안내해도 주문( Order ) 자체가 사라지기 때문에 문제가 된다.
그런데 비즈니스 상황에 따라 체크 예외의 경우에도 트랜잭션을 커밋하지 않고, 롤백하고 싶을 수 있다. 이때는 rollbackFor 옵션을 사용하면 된다.
런타임 예외는 항상 롤백된다. 체크 예외의 경우 rollbackFor 옵션을 사용해서 비즈니스 상황에 따라서 커밋 과 롤백을 선택하면 된다.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
예외와 트랙잭션 커밋, 롤백 - 기본
예외가 발생했는데, 처리를 못하고 트랜잭션 범위 (AOP) 밖으로 예외를 던지면 어떻게될까?
예외 발생시 스프링 트랜잭션 AOP는 예외의 종류에 따라 트랜잭션을 커밋하거나 롤백한다.
RuntimeException,Error와 그 하위 예외가 발생하면 트랜잭션을 롤백한다.Exception과 그 하위 예외가 발생하면 트랜잭션을 커밋한다.체크예외에선 왜 커밋하지? 에 대해서도 뒤에 설명할 예정
로그 추가 확인을 위한 application.properties
runtimeException() 실행 - 런타임 예외
RuntimeException이 발생하므로 트랜잭션이 롤백된다.checkedException() 실행 - 체크 예외
MyException은Exception을 상속받은 체크 예외이다. 따라서 예외가 발생해도 트랜잭션이 커밋된다.rollbackFor
이 옵션을 사용하면 기본 정책에 추가로 어떤 예외가 발생할 때 롤백할 지 지정할 수 있다.
예를 들어서 이렇게 지정하면 체크 예외인
Exception이 발생해도 커밋 대신 롤백된다. (자식 타입도 롤백된다.)rollbackFor() 실행 - 체크 예외를 강제로 롤백
rollbackFor를 사용하면 된다. (해당 예외의 자식 도 포함된다.)rollbackFor = MyException.class을 지정했기 때문에MyException이 발생하면 체크 예외이지만 트랜잭션이 롤백된다.예외와 트랜잭션 커밋, 롤백 - 활용
스프링은 왜 체크예외는 커밋하고, 언체크 예외는 롤백할까?
스프링은 기본적으로 체크예외는 비즈니스 의미가 있을때 사용하고, 언체크 예외는 복구가 불가능한 예외로 가정한다.
꼭 이걸 따를필욘 없고,
rollbackfor옵션으로 롤백하면 된다.그런데 비즈니스 의미가 있는 예외라는것이 무슨뜻일까?
비즈니스 요구사항
주문을 하는데 상황에 따라 다음과 같이 조치한다.
완료로 처리한다.대기로 처리한다.이때 결제 잔고 부족하면
NotEnoughMoneyException이라는 체크 예외가 발생한다고 가정하겠다.이 예외는 시스템 문제가 있어서 발생한게 아니라, 비즈니스 상황에서 문제가 되기 때문에 발생한 예외.
고객의 잔고가 부족한것은 시스템 문제가 아니라 시스템은 문제없이 동작하고 비즈니스 상황이 예외인 것이다.
이런 예외를 비즈니스 예외라고 한다.
코드로 이 상황을 만들어보자.
NotEnoughMoneyException
결제 잔고가 부족하면 발생하는 비즈니스 예외이다.
Exception을 상속 받아서 체크 예외가 된다.이런 상황에서 익셉션을 던지는게 아니라, 트리거 역할을 추가해서 분기를 태우는게 어떨까?
Order엔티티이다.@Getter,@Setter를 사용했다. 참고로 실무에서 엔티티에@Setter를 남발해 서 불필요한 변경 포인트를 노출하는 것은 좋지 않다.@Table(name = "orders")라고 했는데, 테이블 이름을 지정하지 않으면 테이블 이름이 클래스 이름 인order가 된다.order는 데이터베이스 예약어(order by)여서 사용할 수 없다. 그래서orders라는 테 이블 이름을 따로 지정해주었다.등록, 수정, 삭제 조회를 가장 간편하게하는법 : Spring Data JPA
서비스
username)에 따라서 처리 프로세스를 다르게 했다.기본:payStatus를완료상태로 처리하고 정상 처리된다.예외:RuntimeException("시스템 예외")런타임 예외가 발생한다.잔고부족:payStatus를대기상태로 처리한다.NotEnoughMoneyException("잔고가 부족합니다")체크예외가발생한다.payStatus를대기상태로 두고, 체크 예외가 발생하지만,order데이터는 커밋 기대�테스트 코드
테이블 자동 생성 로그를 보기위해선
참고로 테이블 자동 생성은
application.properties에spring.jpa.hibernate.ddl-auto옵션을 조정할 수 있다.none: 테이블을 생성하지 않는다.create: 애플리케이션 시작 시점에 테이블을 생성한다. (기본값)complete()
사용자 이름을
정상으로 설정했다. 모든 프로세스가 정상 수행된다.다음을 통해서 데이터가
완료상태로 저장 되었는지 검증한다.assertThat(findOrder.getPayStatus()).isEqualTo("완료");runtimeException()
사용자 이름을
예외로 설정했다.RuntimeException("시스템 예외")이발생한다.런타임 예외로 롤백이 수행되었기 때문에
Order데이터가 비어 있는 것을 확인할 수 있다.bizException()
사용자 이름을
잔고부족으로 설정했다.NotEnoughMoneyException("잔고가 부족합니다")이발생한다.체크 예외로 커밋이 수행되었기 때문에
Order데이터가 저장된다.다음을 통해서 데이터가
대기상태로 잘 저장 되었는지 검증한다.assertThat(findOrder.getPayStatus()).isEqualTo("대기");정리
NotEnoughMoneyException은 시스템에 문제가 발생한 것이 아니라, 비즈니스 문제 상황을 예외를 통해 알려준다.Order자체가 사라진다. 그러면 고객에게 잔고 부족을 알리고 별도의 계좌로 입금하도록 안내해도 주문(Order) 자체가 사라지기 때문에 문제가 된다.rollbackFor옵션을 사용하면 된다.rollbackFor옵션을 사용해서 비즈니스 상황에 따라서 커밋 과 롤백을 선택하면 된다.All reactions