Loading...
Spring Framework Reference Documentation 7.0.2의 Rolling Back a Declarative Transaction의 한국어 번역본입니다.
아래의 경우에 피드백에서 신고해주신다면 반영하겠습니다.
감사합니다 :)
이전 섹션에서는 일반적으로 애플리케이션에서 서비스 계층 클래스인 class에 대해 어떻게 선언적으로 트랜잭션 설정을 지정하는지에 대한 기본 사항을 설명했습니다. 이 섹션에서는 XML 설정에서 간단하고 선언적인 방식으로 트랜잭션의 롤백을 제어하는 방법을 설명합니다. @Transactional 어노테이션을 사용하여 선언적으로 롤백 시맨틱스를 제어하는 방법에 대한 자세한 내용은
@Transactional Settings를 참고하십시오.
Spring Framework의 트랜잭션 인프라스트럭처에 트랜잭션의 작업을 롤백해야 함을 알리는 권장 방법은 현재 트랜잭션 컨텍스트에서 실행 중인 코드에서 Exception을 throw하는 것입니다. Spring Framework의 트랜잭션 인프라스트럭처 코드는 호출 스택을 따라 전파되는 처리되지 않은 Exception을 catch하고 트랜잭션을 롤백 대상으로 표시할지 여부를 결정합니다.
기본 설정에서 Spring Framework의 트랜잭션 인프라스트럭처 코드는 런타임, 언체크 예외의 경우에만 트랜잭션을 롤백 대상으로 표시합니다. 즉, throw된 예외가 RuntimeException의 인스턴스이거나 서브클래스인 경우입니다. (Error 인스턴스도 기본적으로 롤백을 발생시킵니다).
기본 설정은 또한 Vavr의 Try 메서드가 'Failure'를 반환할 때 트랜잭션 롤백을 트리거하는 것을 지원합니다.
이를 통해 Try를 사용하여 함수형 스타일 오류를 처리하면서 실패가 발생한 경우 트랜잭션이 자동으로 롤백되도록 할 수 있습니다. Vavr의 Try에 대한 자세한 내용은 official Vavr documentation를 참고하십시오.
다음은 트랜잭션 메서드에서 Vavr의 Try를 사용하는 방법의 예입니다:
1@Transactional 2public Try<String> myTransactionalMethod() { 3 // If myDataAccessOperation throws an exception, it will be caught by the 4 // Try instance created with Try.of() and wrapped inside the Failure class 5 // which can be checked using the isFailure() method on the Try instance. 6 return Try.of(delegate::myDataAccessOperation); 7}
Spring Framework 6.1부터는 CompletableFuture
(및 일반 Future) 반환 값에 대한 특별한 처리가 도입되어,
original 메서드에서 반환될 당시 exceptionally completed된 그러한 handle에 대해 롤백을 트리거합니다.
이는 actual 메서드 구현이 CompletableFuture 시그니처를 준수해야 할 수 있는 @Async 메서드를 위한 것으로
(런타임 시 @Async 처리에 의한 프록시 호출을 위한 실제 비동기 handle로 자동 변환됨),
예외를 rethrow하기보다는 반환된 handle에서 노출하는 것을 선호하는 경우를 위한 것입니다:
1@Transactional @Async 2public CompletableFuture<String> myTransactionalMethod() { 3 try { 4 return CompletableFuture.completedFuture(delegate.myDataAccessOperation()); 5 } 6 catch (DataAccessException ex) { 7 return CompletableFuture.failedFuture(ex); 8 } 9}
기본 설정에서 트랜잭션 메서드에서 throw되는 체크 예외는 롤백을 발생시키지 않습니다.
_rollback rules_를 지정하여 어떤 Exception 타입이 트랜잭션을 롤백 대상으로 표시할지, 체크 예외를 포함하여 정확히 설정할 수 있습니다.
Rollback rules
Rollback rules는 특정 예외가 throw될 때 트랜잭션을 롤백해야 하는지를 결정하며, rule은 예외 타입 또는 예외 패턴에 기반합니다.
Rollback rules는 패턴으로 rule을 정의할 수 있게 해주는 rollback-for 및 no-rollback-for
attribute를 통해 XML에서 설정할 수 있습니다.
@Transactional을 사용할 때,
rollback rules는 예외 타입 또는 패턴에 기반하여 rule을 정의할 수 있게 해주는
rollbackFor/noRollbackFor 및 rollbackForClassName/noRollbackForClassName attribute를 통해 설정할 수 있습니다.
예를 들어 rollbackFor를 통해 롤백 rule이 예외 타입으로 정의되면,
해당 타입은 throw된 예외 타입과의 매칭에 사용됩니다.
구체적으로, 설정된 예외 타입 C가 주어졌을 때,
타입 T의 throw된 예외는 T가 C와 같거나 C의 서브클래스인 경우 C에 대한 match로 간주됩니다.
이는 타입 안전성을 제공하며 패턴을 사용할 때 발생할 수 있는 의도치 않은 match를 방지합니다.
예를 들어, jakarta.servlet.ServletException.class 값은
jakarta.servlet.ServletException 타입 및 그 서브클래스인 throw된 예외에만 match됩니다.
Rollback rule이 예외 패턴으로 정의되는 경우,
패턴은 예외 타입(반드시 Throwable의 서브클래스여야 함)의 완전 수식 클래스 이름이거나
완전 수식 클래스 이름의 서브스트링일 수 있으며, 현재 와일드카드는 지원되지 않습니다.
예를 들어 "jakarta.servlet.ServletException" 또는 "ServletException" 값은
jakarta.servlet.ServletException 및 그 서브클래스와 match됩니다.
패턴의 구체성을 얼마나 높일지와 패키지 정보를 포함할지(필수는 아님)를 신중하게 고려해야 합니다. 예를 들어
"Exception"은 거의 모든 것과 match되어 다른 rule을 숨길 가능성이 큽니다."Exception"이 모든 체크 예외에 대한 rule을 정의하기 위한 것이라면"java.lang.Exception"이 올바른 값입니다."BaseBusinessException"과 같이 보다 고유한 예외 이름의 경우 예외 패턴에 완전 수식 클래스 이름을 사용할 필요가 없을 가능성이 큽니다.<br>또한 패턴 기반 rollback rules는 비슷한 이름의 예외 및 중첩 클래스에 대해 의도치 않은 match를 초래할 수 있습니다. 이는 throw된 예외의 이름에 rollback rule에 대해 설정된 예외 패턴이 포함되어 있으면 해당 패턴 기반 rollback rule에 대한 match로 간주되기 때문입니다.<br>예를 들어"com.example.CustomException"에 match하도록 설정된 rule이 주어지면, 이 rule은com.example.CustomExceptionV2(동일한 패키지의CustomException이지만 추가 suffix가 있는 예외) 또는com.example.CustomException$AnotherException(CustomException에 중첩 클래스로 선언된 예외)이라는 이름의 예외와 match됩니다.
다음 XML snippet은 rollback-for attribute를 통해 _예외 패턴_을 제공하여
체크되고 애플리케이션 특화인 Exception 타입에 대해 롤백을 설정하는 방법을 보여줍니다:
1<tx:advice id="txAdvice" transaction-manager="txManager"> 2 <tx:attributes> 3 <tx:method name="get*" read-only="true" rollback-for="NoProductInStockException"/> 4 <tx:method name="*"/> 5 </tx:attributes> 6</tx:advice>
예외가 throw될 때 트랜잭션이 롤백되는 것을 원하지 않는 경우,
'no rollback' rule을 지정할 수도 있습니다.
다음 예제는 처리되지 않은 InstrumentNotFoundException이 발생하더라도
Spring Framework의 트랜잭션 인프라스트럭처에 attendant 트랜잭션을 커밋하도록 지시합니다:
1<tx:advice id="txAdvice"> 2 <tx:attributes> 3 <tx:method name="updateStock" no-rollback-for="InstrumentNotFoundException"/> 4 <tx:method name="*"/> 5 </tx:attributes> 6</tx:advice>
Spring Framework의 트랜잭션 인프라스트럭처가 예외를 catch하고,
트랜잭션을 롤백 대상으로 표시할지 여부를 결정하기 위해
설정된 rollback rules를 확인할 때는 가장 강한 match rule이 우선합니다.
따라서 다음 설정의 경우, InstrumentNotFoundException 이외의 모든 예외는
attendant 트랜잭션의 롤백을 초래합니다:
1<tx:advice id="txAdvice"> 2 <tx:attributes> 3 <tx:method name="*" rollback-for="Throwable" no-rollback-for="InstrumentNotFoundException"/> 4 </tx:attributes> 5</tx:advice>
필요한 롤백을 프로그래밍 방식으로 표시할 수도 있습니다. 비록 단순하지만, 이 과정은 상당히 침투적이며 코드를 Spring Framework의 트랜잭션 인프라스트럭처에 강하게 결합시킵니다.
다음 예제는 필요한 롤백을 프로그래밍 방식으로 표시하는 방법을 보여줍니다:
1public void resolvePosition() { 2 try { 3 // some business logic... 4 } 5 catch (NoProductInStockException ex) { 6 // trigger rollback programmatically 7 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); 8 } 9}
1fun resolvePosition() { 2 try { 3 // some business logic... 4 } 5 catch (ex: NoProductInStockException) { 6 // trigger rollback programmatically 7 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); 8 } 9}
가능한 한 롤백에 대해 선언적 접근 방식을 사용할 것을 강력히 권장합니다. 프로그래밍 방식 롤백은 꼭 필요할 경우 사용할 수 있지만, 그 사용은 깔끔한 POJO 기반 아키텍처를 달성하려는 목적에 반하는 것입니다.
Example of Declarative Transaction Implementation
Configuring Different Transactional Semantics for Different Beans