-
spring transaction roll back 처리Spring/Spring 기본 지식 2021. 6. 20. 15:15반응형
회사 프로젝트를 확인하다가 try catch와 transation에 대해서 궁금한한게 생겼다.
궁금한 내용들은
각각의 예외처리가 된 메소드에서 에러가 발생이될 때 서로의 관계가 어떻게 될까? 였다. 이에 따른 현상을 이해하기 위해서
우선적으로 Excatpion과 Error의 기본적인 개념을 알면서 공부하자
아래의 링크를 타고가게 된다면 기본적인 개념에 대해서 적었지만 이번 글에서는 좀 더 나아가서 공부할 예정이다.
2020.07.19 - [Java & 배경지식/기본상식] - 자바 개발자가 알아야하는 25가지 상식!
아래에 관련된 코드는 여기 확인하면 된다.
Exception과 Error
Exception과 Error는 한 마디로 개발자가 대응을 할 수 있냐 없냐로 구분할 수 있다.
쉽게 말하면 Erorr는 OutOfMemery처럼 메모리가 부족해서 발생되는 것이기 때문에 개발자가 대응을 할 수가 없으며,
Exception은 NullPointException처럼 개발자가 대응을 할 수 있는 있는 것을 뜻한다.
Exception의 종류
Check Exception nonChecked Exception 정의 Exception의 상속받는 하위 클래스 중 Runtime Exception을 제외한 모든 Exception Runtime Exception 하위 Exception 발생이유 주로 외부의 영향으로 발생할 수 있는 것들로서, 프로그램의 사용자들의 동작에 의해서 발생하는 경우가 많다.
예를 들면, 존재하지 않는 파일을 처리하려는 경우(FileNotFoundException), 실수로 클래스의 이름을 잘못 적은 경우(ClassNotFoundException), 입력한 데이터의 형식이 잘못된 경우(DataFormatException)에 발생주로 프로그래머의 실수에 의해서 발생될 수 있는 예외들로서 자바의 프로그래밍 요소들과 관계가 깊다.
예를 들면, 배열의 범위를 벗어난 경우(IndexOutOfBoundsException), 값이 null인 참조 변수의 멤버를 호출한 경우(NullPointerException), 클래스 간의 형 변환을 잘못한 경우(ClassCastException), 정수를 0으로 나누려 하는 경우(ArithmeticException)에 발생처리여부 명시적인 예외 처리를 해주어야 한다. 명시적인 처리를 강제하지는 않는다. 혹인시점 Compile time
: 이미 컴파일 시점에 에러를 나타내기 때문에 try~catch~finally 또는 throws 구문을 통해서 처리할 수 있게 IDE에서 알려준다.Runtime
: 이미 컴파일이 끝나고, 애플리케이션이 서비스가 런타임일 때 발생하기 때문에 try~catch 또는 throws 구문을 사용해서 로직상에서 방어 코드를 만들어야 한다.예외발생시 트랜잭션 처리 non-rollback rollback Exception 처리 방법
- try catch finally - 직접 처리 방식
- throws - 간접 처리 방식
직접 처리 방식이 무엇인지 간접처리 방식이 무엇인지 글로 표현하자면 직접 내가 Exception처리를 하는가 아니면 Exception처리를 남에게 미루는가 이렇게 볼 수 있다. 이를 코드로 표현하자면 아래와 같다.
try catch finally - 직접 처리 방식
public void directException() { try { System.out.println("Exception 이 발생하지 않는다면 실행하는 공간!."); throw new Exception("직접 처리 방식"); } catch (Exception e) { System.out.println("Exception 발생하면 실행 : " + e.getMessage()); } finally { System.out.println("무조건 실행"); } }
throws - 간첩 처리 방식
public void inDirectException() throws Exception { throw new Exception("간접 처리 방식 !"); } public void callInDirectException() { try { System.out.println("여기는 call"); inDirectException(); } catch (Exception e) { System.out.println(e.getMessage()); } }
Spring Transaction
이제 중간에 에러가 발생이 되었을 때 트랜잭션이 어떻게 처리하는가에 대해서 알아 보자.
상황 1. try ~ catch문안에서 Exception이 발생된 경우
아래의 코드와 결과를 보게 된다면 Exception이 발생한 경우 Member1의 대한 정보만 저장이되고, Member2의 정보는 저장이 안된다.
/** * 직접 처리 방식내에서 오류가 발생 할 때 Transaction 처리 여부 확인 * @throws Exception */ @Transactional public void simulation1() throws Exception { Member member1 = Member.builder() .name("Member1") .nickname("닉테임1") .email("test1@test.com") .memberMakeSample(); Member member2 = Member.builder() .name("Member2") .nickname("닉테임2") .email("test2@test.com") .memberMakeSample(); memberRepository.save(member1); try { sampleRepository.failMethod(); memberRepository.save(member2); } catch (Exception e) { System.out.println("에러 발생!1!!!"); } /* Member가 저장이 되어 있다면 해당 ID들은 저장이 되어있을 것 */ System.out.println("member1 id : " + member1.getMemberId()); System.out.println("member2 id : " + member2.getMemberId()); }
상황 2. 간접 처리시 Exception이 발생된 경우
아래와 같이 모두 저장이 되지 않으며, Transaction 오류가 발생함을 알 수 있다.
/** * 간접 처리 방식내에서 오류가 발생 할 때 Transaction 처리 여부 확인 * @throws Exception */ @Transactional public void simulation2() throws Exception { Member member1 = Member.builder() .name("Member1") .nickname("닉테임1") .email("test1@test.com") .memberMakeSample(); Member member2 = Member.builder() .name("Member2") .nickname("닉테임2") .email("test2@test.com") .memberMakeSample(); memberRepository.save(member1); sampleRepository.failMethod(); memberRepository.save(member2); /* Member가 저장이 되어 있다면 해당 ID들은 저장이 되어있을 것 */ System.out.println("member1 id : " + member1.getMemberId()); System.out.println("member2 id : " + member2.getMemberId()); }
그외. try ~ catch에서 rollbackFor을 다르게 준다면
rollbackFor는 해당 Exception이 작동이 될 때 rollback을 하는 옵션이다.
1. 정상적으로 작동이 안될 때
Exception이 발생이 될때 rollback 한다 하였으나 rollack이 안되는 경우
/** * @Transactional(rollbackFor = Exception.class) 옵션 먹힘 * @throws Exception */ @Transactional(rollbackFor = Exception.class) public void simulation3() throws Exception { Member member1 = Member.builder() .name("Member1") .nickname("닉테임1") .email("test1@test.com") .memberMakeSample(); Member member2 = Member.builder() .name("Member2") .nickname("닉테임2") .email("test2@test.com") .memberMakeSample(); memberRepository.save(member1); try { sampleRepository.failMethod(); memberRepository.save(member2); } catch (Exception e) { System.out.println("에러 발생!1!!!"); } /* Member가 저장이 되어 있다면 해당 ID들은 저장이 되어있을 것 */ System.out.println("member1 id : " + member1.getMemberId()); System.out.println("member2 id : " + member2.getMemberId()); }
2. 정상적으로 작동이 될 때
try~catch문을 사용했을 때 해당 옵션에 대해 제대로 작동시키기 하기 위해서는 catch문에라도 Exception을 발동 시켜야 한다.
/** * @Transactional(rollbackFor = Exception.class) 옵션 안먹힘 * @throws Exception */ @Transactional(rollbackFor = Exception.class) public void simulation4() throws Exception { Member member1 = Member.builder() .name("Member1") .nickname("닉테임1") .email("test1@test.com") .memberMakeSample(); Member member2 = Member.builder() .name("Member2") .nickname("닉테임2") .email("test2@test.com") .memberMakeSample(); memberRepository.save(member1); try { sampleRepository.failMethod(); memberRepository.save(member2); } catch (Exception e) { throw new Exception(); } /* Member가 저장이 되어 있다면 해당 ID들은 저장이 되어있을 것 */ System.out.println("member1 id : " + member1.getMemberId()); System.out.println("member2 id : " + member2.getMemberId()); }
상황 3. Transaction이 처리된 메소드들을 호출할때 에러발생시 어디서 에러처리를 하는가?
1. Transaction이 걸려있는 메소드가 서로를 호출할 때 어디서 잡는가?
아래의 코들르 보게 된다면 step1, step2, step3 메소드가 존재하며 step3에 에러가 발생된 것이 확인이 된다. 해당코드를 디버깅한다면
에러발생지점인 step3에서 에러처리를 하는 것을 알 수 있다. 그리고 rolback을 진행한다.
/** * @throws Exception * @Transactional(rollbackFor = Exception.class) 옵션 안먹힘 */ @Transactional(rollbackFor = Exception.class) public void simulation5() throws Exception { step1Transaction(); System.out.println("simulation5"); } @Transactional(rollbackFor = Exception.class) public void step1Transaction() throws Exception { Member member = Member.builder() .name("Member1") .nickname("step1") .email("test1@test.com") .memberMakeSample(); memberRepository.save(member); step2Transaction(member); System.out.println("step1Transaction"); } @Transactional(rollbackFor = Exception.class) public void step2Transaction(Member member) throws Exception { member.setNickname("step2"); memberRepository.save(member); step3Transaction(member); System.out.println("step2Transaction"); } @Transactional(rollbackFor = Exception.class) public void step3Transaction(Member member) throws Exception { member.setNickname("step3"); memberRepository.save(member); System.out.println("step3Transaction"); throw new Exception(); }
참고
반응형'Spring > Spring 기본 지식' 카테고리의 다른 글
spring @ControllerAdvice, @ExceptionHandler (2) 2021.04.07 Spring DataSourceTransactionManager (0) 2020.07.01 MyBatis 속성 (0) 2020.05.06 Spring AOP 설정방법 - @annotation 두 번째(백기선님) (0) 2020.03.30 Spring AOP 설정방법 - execution (0) 2020.03.30