-
[Spring] Transactional Propagation 정리하기Server/Spring 2021. 11. 28. 23:52728x90반응형
@Transactional Propagation 알아보기
이번 글에서는
Spring Transactional 어노테이션에서 propagation 특징
에 대해서 정리해보려 합니다.Propagation
옵션 설명 REQUIRED 기본 옵션
부모 트랜잭션이 존재한다면 부모 트랜잭션에 합류, 그렇지 않다면 새로운 트랜잭션을 만든다.
중간에 자식/부모에서 rollback이 발생된다면 자식과 부모 모두 rollback 한다.REQUIRES_NEW 무조건 새로운 트랜잭션을 만든다.
nested한 방식으로 메소드 호출이 이루어지더라도 rollback은 각각 이루어 진다.MANDATORY 무조건 부모 트랜잭션에 합류시킨다.
부모 트랜잭션이 존재하지 않는다면 예외를 발생시킨다.SUPPORTS 메소드가 트랜잭션을 필요로 하지는 않지만, 진행 중인 트랜잭션이 존재하면 트랜잭션을 사용한다는 것을 의미한다. 진행 중인 트랜잭션이 존재하지 않더라도 메소드는 정상적으로 동작한다. NESTED 부모 트랜잭션이 존재하면 부모 트랜잭션에 중첩시키고, 부모 트랜잭션이 존재하지 않는다면 새로운 트랜잭션을 생성한다.
부모 트랜잭션에 예외가 발생하면 자식 트랜잭션도 rollback한다.
자식 트랜잭션에 예외가 발생하더라도 부모 트랜잭션은 rollback하지 않는다. 이때 롤백은 부모 트랜잭션에서 자식 트랜잭션을 호출하는 지점까지만 롤백된다. 이후 부모 트랜잭션에서 문제가 없으면 부모 트랜잭션은 끝까지 commit 된다.
현재 트랜잭션이 있으면 중첩 트랜잭션 내에서 실행하고, 그렇지 않으면 REQUIRED 처럼 동작합니다.NEVER 메소드가 트랜잭션을 필요로 하지 않는다. 만약 진행 중인 트랜잭션이 존재하면 익셉션이 발생한다. 대부분의 글에서
Propagation
에 대해서 위와 같이 설명합니다. 말로만 들어도 어떤 말인지 알 수 있지만 저는 완벽하게 이해가 되지 않아서 직접 예제 코드를 작성해보면서 테스트 해보았습니다. 하나씩 알아보겠습니다.REQUIRED
@Transactional
에 아무 설정을 하지 않고 사용하면Propagation Default인 REQUIRED
가 설정됩니다.REQUIRED
는DEFAULT
설정 값이다 보니 많은 사람들이 제일 많이 사용하고 있는Propagation
속성 입니다. 즉, 위의 글에 적혀 있던 대로자식/부모에서 rollback이 발생된다면 자식과 부모 모두 rollback 한다.
라는 말을 이해할 수 있습니다. 즉, 위의 예제코드 처럼 자식 트랜잭션에서 예외가 발생하면부모
,자식
모두 롤백되어 어떤 유저도 저장되지 않습니다. (부모에서 예외가 발생해도 마찬가지 입니다.)자식에서 예외 발생한 것을 부모에서 예외 처리
그러면 위와 같이
자식에서 발생한 예외
를부모 트랜잭션
에서try-catch
로 예외 처리를 하는 경우에는 어떻게 될까요?try-catch
를 하였더라도예외
가 발생하면서 전부RollBack
되는 것을 볼 수 있습니다.REQUIRES_NEW
자식에서 예외 발생
이번에는 자식에게
REQUIRES_NEW
옵션을 준 후에 위의 코드와 같이 테스트 해보았습니다. 위에서 적었던 설명으로 보면부모
,자식
트랜잭션이 각각 열리기 때문에 자식에서 예외가 발생해도 부모에서save
한 것은 저장이 되는 것을 예상했습니다.그런데 부모에서 저장한 유저도
INSERT
되지 않은 것을 볼 수 있습니다.REQUIRES_NEW
는자식 트랜잭션에서 발생한 것이 부모 트랜잭션 까지 전파되지 않는다
의 말은 틀린말이 아닌가 생각이 들었습니다.댓글로 첨언해주신 것을 기반으로 좀 더 설명을 보강하자면 "트랜잭션이 전파"되는 것과 "예외가 전파" 되는 것은 다르다는 특징이 있습니다. 자식 쪽에 예외가 발생할 경우 자식 쪽은 트랜잭션이 롤백이 되는 것이 맞습니다. 그런데 이 때 자식 쪽에서 발생한 예외가 부모 쪽으로 전파되기 때문에 부모 쪽에서도 예외가 발생하여 롤백이 일어난 것으로 이해해야 맞을 것 같습니다.
자식에서 발생한 예외 부모에서 예외 처리
그래서 이번에는
부모에서 예외 처리
를 했을 때는 어떻게 처리되는지 보기 위해서 예외 처리하고 실행해보겠습니다. 이번에는자식에서 발생한 예외를 부모에서 try-catch
로 묶어주었는데요. 그랬더니 자식 트랜잭션을 호출하기 전까지의 쿼리만 커밋된 것을 확인할 수 있습니다. 예외 처리를 해주어야부모 트랜잭션도 자식 트랜잭션에 영향을 받지 않고 커밋
을 하는 것 같습니다.부모에서 예외 발생
이번에는
부모에서 예외가 발생
한 경우에는 어떻게 되는지 테스트 해보겠습니다.REQUIRES_NEW
는부모 트랜잭션
에서 예외가 발생해도자식 트랜잭션
에서는 꼭 커밋되어야 하는 상황에서 사용하면 좋을 것 같습니다.MANDATORY
MANDATORY
는 부모 트랜잭션이 존재하면 무조건 부모 트랜잭션에 합류시키고, 부모 트랜잭션에 트랜잭션이 시작된 것이 없다면 예외를 발생시킵니다. 즉, 혼자서는 독립적으로 트랜잭션을 진행하면 안되는 경우에 사용합니다.예측 했던 대로 부모에서 트랜잭션을 시작하지 않아서 위와 같은 에러가 발생한 것을 볼 수 있습니다.
그리고 데이터베이스에 어떻게 저장되었는지 확인해보면 부모에서 첫 번째로 저장한
User 1번
만 저장되고 나머지는 저장되지 않은 것을 볼 수 있습니다.NESTED
부모에서 예외 발생
NESTED
속성에서는 부모 트랜잭션에서 에러가 발생하면 자식 트랜잭션은 어떻게 되는지 알아보겠습니다. 위의 코드를 실행하면자식
트랜잭션도 커밋이 되지 않습니다. 이유는 부모 트랜잭션이 존재하면 자식 트랜잭션도 부모 트랜잭션에 합류하기 때문입니다.자식에서 예외 발생
그리고 자식에서 예외가 발생하여도 마찬가지로 어떤 값도
User
에 저장되지 않습니다.그리고 예외를 발생했을 때 에러 로그를 보면
JpaDialect does not support savepoints - check your JPA provider's capabilities
가 발생합니다.NESTED
는JDBC 3.0 드라이버를 사용할 때에만 적용된다
라는 특징이 있습니다.자식에서 예외 발생한 예외 부모에서 예외 처리
이번에는
부모 트랜잭션
에서 예외 처리를 했을 때의 경우를 해보겠습니다.이번에도
자식 트랜잭션
을 호출하기 전 부모 트랜잭션에서 호출한INSERT
쿼리가 커밋된 것을 볼 수 있습니다.부모 트랜잭션이 없을 경우
NESTED
속성은 부모 트랜잭션이 존재하지 않는다면 새로운 트랜잭션을 생성한다고 했는데요.그래서 위처럼
부모 트랜잭션
이 없을 때는 자식 트랜잭션에서 새로 열리다 보니 자식 트랜잭션에서 예외가 발생해도 부모 트랜잭션에서는 1번 유저가 저장 커밋이 된 것을 볼 수 있습니다.NEVER
NEVER
는 메소드가 트랜잭션을 필요로 하지 않는다. 만약 진행 중인 트랜잭션이 존재하면 익셉션이 발생합니다.위처럼 부모에서 트랜잭션이 존재한다면
Existing transaction found for transaction marked with propagation 'never'
에러가 발생하는 것을 볼 수 있습니다.이번 글의 코드를 확인하고 싶다면 여기 에서 확인할 수 있습니다.
반응형'Server > Spring' 카테고리의 다른 글
[Spring] AWS EC2에서 Spring Access log, logger log 저장하는 법 (0) 2021.12.09 [Spring] 스프링에서 의존성 주입을 하는 3가지 방법 (0) 2021.11.29 [Spring] Test에서 public이 아닌 필드 Reflection 으로 값 넣어주는 법 (0) 2021.11.26 [Spring] 프로젝트에서 MyBatis ResultMap을 사용하게된 이유 (2) 2021.11.18 [Spring] Primitive boolean Type is Remove Jackson 직렬화 문제 해결하기 (0) 2021.11.07