Loading...
Spring Framework Reference Documentation 7.0.2의 Programmatic Transaction Management의 한국어 번역본입니다.
아래의 경우에 피드백에서 신고해주신다면 반영하겠습니다.
감사합니다 :)
The Spring Framework는 다음을 사용해서 두 가지 방식의 프로그래매틱 트랜잭션 관리를 제공합니다:
TransactionTemplate 또는 TransactionalOperator.TransactionManager 구현을 직접 사용하는 방식.Spring team은 일반적으로 명령형 플로우에서의 프로그래매틱 트랜잭션 관리에는 TransactionTemplate을, 리액티브 코드에는 TransactionalOperator를 사용할 것을 권장합니다. 두 번째 접근 방식은 JTA UserTransaction API를 사용하는 것과 비슷하지만, 예외 처리가 덜 번거롭습니다.
TransactionTemplateTransactionTemplate은 JdbcTemplate과 같은 다른 Spring 템플릿과 동일한 접근 방식을 채택합니다. 이는 콜백 방식을 사용하여 (애플리케이션 코드가 보일러플레이트한 트랜잭션 리소스 획득과 해제를 직접 처리하지 않도록 해 주며) 코드가 의도 중심이 되도록 해서, 코드가 오로지 당신이 하고자 하는 것에만 집중하게 합니다.
아래 예제에서 보듯이,
TransactionTemplate을 사용하면 Spring의 트랜잭션 인프라스트럭처와 API에 완전히 결합됩니다. 프로그래매틱 트랜잭션 관리가 당신의 개발 요구 사항에 적합한지는 스스로 결정해야 합니다.
트랜잭션 컨텍스트에서 실행되어야 하고 TransactionTemplate을 명시적으로 사용하는 애플리케이션 코드는 다음 예제와 비슷합니다. 애플리케이션 개발자인 당신은 트랜잭션 컨텍스트에서 실행되어야 하는 코드를 담고 있는 TransactionCallback 구현(일반적으로 익명 내부 클래스로 표현됨)을 작성할 수 있습니다.
그런 다음, 당신이 작성한 커스텀 TransactionCallback 인스턴스를 TransactionTemplate에서 노출하는 execute(..) 메서드에 전달할 수 있습니다. 다음 예제는 그 방법을 보여 줍니다:
1public class SimpleService implements Service { 2 3 // single TransactionTemplate shared amongst all methods in this instance 4 private final TransactionTemplate transactionTemplate; 5 6 // use constructor-injection to supply the PlatformTransactionManager 7 public SimpleService(PlatformTransactionManager transactionManager) { 8 this.transactionTemplate = new TransactionTemplate(transactionManager); 9 } 10 11 public Object someServiceMethod() { 12 return transactionTemplate.execute(new TransactionCallback() { 13 // the code in this method runs in a transactional context 14 public Object doInTransaction(TransactionStatus status) { 15 updateOperation1(); 16 return resultOfUpdateOperation2(); 17 } 18 }); 19 } 20}
1// use constructor-injection to supply the PlatformTransactionManager 2class SimpleService(transactionManager: PlatformTransactionManager) : Service { 3 4 // single TransactionTemplate shared amongst all methods in this instance 5 private val transactionTemplate = TransactionTemplate(transactionManager) 6 7 fun someServiceMethod() = transactionTemplate.execute<Any?> { 8 updateOperation1() 9 resultOfUpdateOperation2() 10 } 11}
반환 값이 없는 경우, 다음과 같이 익명 클래스와 함께 편리한 TransactionCallbackWithoutResult 클래스를 사용할 수 있습니다:
1transactionTemplate.execute(new TransactionCallbackWithoutResult() { 2 protected void doInTransactionWithoutResult(TransactionStatus status) { 3 updateOperation1(); 4 updateOperation2(); 5 } 6});
1transactionTemplate.execute(object : TransactionCallbackWithoutResult() { 2 override fun doInTransactionWithoutResult(status: TransactionStatus) { 3 updateOperation1() 4 updateOperation2() 5 } 6})
콜백 내부의 코드는 다음과 같이 제공된 TransactionStatus 객체의 setRollbackOnly() 메서드를 호출하여 트랜잭션을 롤백할 수 있습니다:
1transactionTemplate.execute(new TransactionCallbackWithoutResult() { 2 3 protected void doInTransactionWithoutResult(TransactionStatus status) { 4 try { 5 updateOperation1(); 6 updateOperation2(); 7 } catch (SomeBusinessException ex) { 8 status.setRollbackOnly(); 9 } 10 } 11});
1transactionTemplate.execute(object : TransactionCallbackWithoutResult() { 2 3 override fun doInTransactionWithoutResult(status: TransactionStatus) { 4 try { 5 updateOperation1() 6 updateOperation2() 7 } catch (ex: SomeBusinessException) { 8 status.setRollbackOnly() 9 } 10 } 11})
전파 모드, 격리 수준, 타임아웃 등과 같은 트랜잭션 설정은 TransactionTemplate에서 프로그래밍 방식으로 또는 설정으로 지정할 수 있습니다. 기본적으로, TransactionTemplate 인스턴스는 기본 트랜잭션 설정을 가집니다.
다음 예제는 특정 TransactionTemplate에 대해 트랜잭션 설정을 프로그래밍 방식으로 커스터마이징하는 방법을 보여 줍니다:
1public class SimpleService implements Service { 2 3 private final TransactionTemplate transactionTemplate; 4 5 public SimpleService(PlatformTransactionManager transactionManager) { 6 this.transactionTemplate = new TransactionTemplate(transactionManager); 7 8 // the transaction settings can be set here explicitly if so desired 9 this.transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_UNCOMMITTED); 10 this.transactionTemplate.setTimeout(30); // 30 seconds 11 // and so forth... 12 } 13}
1class SimpleService(transactionManager: PlatformTransactionManager) : Service { 2 3 private val transactionTemplate = TransactionTemplate(transactionManager).apply { 4 // the transaction settings can be set here explicitly if so desired 5 isolationLevel = TransactionDefinition.ISOLATION_READ_UNCOMMITTED 6 timeout = 30 // 30 seconds 7 // and so forth... 8 } 9}
다음 예제는 Spring XML 설정을 사용하여 커스텀 트랜잭션 설정을 가진 TransactionTemplate을 정의합니다:
1<bean id="sharedTransactionTemplate" 2 class="org.springframework.transaction.support.TransactionTemplate"> 3 <property name="isolationLevelName" value="ISOLATION_READ_UNCOMMITTED"/> 4 <property name="timeout" value="30"/> 5</bean>
그런 다음 필요한 만큼 많은 서비스에 sharedTransactionTemplate을 주입할 수 있습니다.
마지막으로, TransactionTemplate 클래스의 인스턴스는 어떤 대화형 상태도 유지하지 않기 때문에 스레드 세이프합니다. 그러나 TransactionTemplate 인스턴스는 설정 상태는 유지합니다. 따라서 여러 클래스가 하나의 TransactionTemplate 인스턴스를 공유할 수 있지만, 어떤 클래스가 서로 다른 설정(예: 다른 격리 수준)의 TransactionTemplate을 사용해야 하는 경우, 두 개의 서로 다른 TransactionTemplate 인스턴스를 생성해야 합니다.
TransactionalOperatorTransactionalOperator는 다른 리액티브 오퍼레이터와 비슷한 오퍼레이터 디자인을 따릅니다. 이는 콜백 방식을 사용하여 (애플리케이션 코드가 보일러플레이트한 트랜잭션 리소스 획득과 해제를 직접 처리하지 않도록 해 주며) 코드가 의도 중심이 되도록 해서, 코드가 오로지 당신이 하고자 하는 것에만 집중하게 합니다.
아래 예제에서 보듯이,
TransactionalOperator를 사용하면 Spring의 트랜잭션 인프라스트럭처와 API에 완전히 결합됩니다. 프로그래매틱 트랜잭션 관리가 당신의 개발 요구 사항에 적합한지는 스스로 결정해야 합니다.
트랜잭션 컨텍스트에서 실행되어야 하고 TransactionalOperator를 명시적으로 사용하는 애플리케이션 코드는 다음 예제와 비슷합니다:
1public class SimpleService implements Service { 2 3 // single TransactionalOperator shared amongst all methods in this instance 4 private final TransactionalOperator transactionalOperator; 5 6 // use constructor-injection to supply the ReactiveTransactionManager 7 public SimpleService(ReactiveTransactionManager transactionManager) { 8 this.transactionalOperator = TransactionalOperator.create(transactionManager); 9 } 10 11 public Mono<Object> someServiceMethod() { 12 13 // the code in this method runs in a transactional context 14 15 Mono<Object> update = updateOperation1(); 16 17 return update.then(resultOfUpdateOperation2).as(transactionalOperator::transactional); 18 } 19}
1// use constructor-injection to supply the ReactiveTransactionManager 2class SimpleService(transactionManager: ReactiveTransactionManager) : Service { 3 4 // single TransactionalOperator shared amongst all methods in this instance 5 private val transactionalOperator = TransactionalOperator.create(transactionManager) 6 7 suspend fun someServiceMethod() = transactionalOperator.executeAndAwait<Any?> { 8 updateOperation1() 9 resultOfUpdateOperation2() 10 } 11}
TransactionalOperator는 두 가지 방식으로 사용할 수 있습니다:
mono.as(transactionalOperator::transactional))transactionalOperator.execute(TransactionCallback<T>))콜백 내부의 코드는 다음과 같이 제공된 ReactiveTransaction 객체의 setRollbackOnly() 메서드를 호출하여 트랜잭션을 롤백할 수 있습니다:
1transactionalOperator.execute(new TransactionCallback<>() { 2 3 public Mono<Object> doInTransaction(ReactiveTransaction status) { 4 return updateOperation1().then(updateOperation2) 5 .doOnError(SomeBusinessException.class, e -> status.setRollbackOnly()); 6 } 7 } 8});
1transactionalOperator.execute(object : TransactionCallback() { 2 3 override fun doInTransactionWithoutResult(status: ReactiveTransaction) { 4 updateOperation1().then(updateOperation2) 5 .doOnError(SomeBusinessException.class, e -> status.setRollbackOnly()) 6 } 7})
Reactive Streams에서, Subscriber는 자신의 Subscription을 취소하고 자신의 Publisher를 중지할 수 있습니다. Project Reactor의 오퍼레이터뿐만 아니라 next(), take(long), timeout(Duration) 등의 다른 라이브러리의 오퍼레이터도 취소를 발생시킬 수 있습니다.
취소가 에러 때문인지 아니면 단순히 더 이상 소비할 관심이 없기 때문인지 그 이유를 알 방법은 없습니다. 5.3 버전부터 취소 시그널은 롤백을 유발합니다. 그 결과 트랜잭션 Publisher의 다운스트림에서 사용되는 오퍼레이터를 신중히 고려하는 것이 중요합니다. 특히 Flux나 다른 멀티 값 Publisher의 경우, 트랜잭션이 완료될 수 있도록 전체 출력이 소비되어야 합니다.
전파 모드, 격리 수준, 타임아웃 등과 같은 트랜잭션 설정은 TransactionalOperator에 대해 지정할 수 있습니다. 기본적으로, TransactionalOperator 인스턴스는 기본 트랜잭션 설정을 가집니다.
다음 예제는 특정 TransactionalOperator에 대해 트랜잭션 설정을 커스터마이징하는 방법을 보여 줍니다:
1public class SimpleService implements Service { 2 3 private final TransactionalOperator transactionalOperator; 4 5 public SimpleService(ReactiveTransactionManager transactionManager) { 6 DefaultTransactionDefinition definition = new DefaultTransactionDefinition(); 7 8 // the transaction settings can be set here explicitly if so desired 9 definition.setIsolationLevel(TransactionDefinition.ISOLATION_READ_UNCOMMITTED); 10 definition.setTimeout(30); // 30 seconds 11 // and so forth... 12 13 this.transactionalOperator = TransactionalOperator.create(transactionManager, definition); 14 } 15}
1class SimpleService(transactionManager: ReactiveTransactionManager) : Service { 2 3 private val definition = DefaultTransactionDefinition().apply { 4 // the transaction settings can be set here explicitly if so desired 5 isolationLevel = TransactionDefinition.ISOLATION_READ_UNCOMMITTED 6 timeout = 30 // 30 seconds 7 // and so forth... 8 } 9 private val transactionalOperator = TransactionalOperator(transactionManager, definition) 10}
TransactionManager다음 섹션에서는 명령형 및 리액티브 트랜잭션 매니저의 프로그래매틱 사용 방법을 설명합니다.
PlatformTransactionManager명령형 트랜잭션의 경우, org.springframework.transaction.PlatformTransactionManager를 직접 사용하여 트랜잭션을 관리할 수 있습니다. 이를 위해 사용하는 PlatformTransactionManager 구현을 빈 참조를 통해 빈에 전달합니다.
그런 다음, TransactionDefinition 및 TransactionStatus 객체를 사용하여 트랜잭션을 시작하고, 롤백하고, 커밋할 수 있습니다. 다음 예제는 그 방법을 보여 줍니다:
1DefaultTransactionDefinition def = new DefaultTransactionDefinition(); 2// explicitly setting the transaction name is something that can be done only programmatically 3def.setName("SomeTxName"); 4def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); 5 6TransactionStatus status = txManager.getTransaction(def); 7try { 8 // put your business logic here 9} catch (MyException ex) { 10 txManager.rollback(status); 11 throw ex; 12} 13txManager.commit(status);
1val def = DefaultTransactionDefinition() 2// explicitly setting the transaction name is something that can be done only programmatically 3def.setName("SomeTxName") 4def.propagationBehavior = TransactionDefinition.PROPAGATION_REQUIRED 5 6val status = txManager.getTransaction(def) 7try { 8 // put your business logic here 9} catch (ex: MyException) { 10 txManager.rollback(status) 11 throw ex 12} 13 14txManager.commit(status)
ReactiveTransactionManager리액티브 트랜잭션을 사용할 때는 org.springframework.transaction.ReactiveTransactionManager를 직접 사용하여 트랜잭션을 관리할 수 있습니다. 이를 위해 사용하는 ReactiveTransactionManager 구현을 빈 참조를 통해 빈에 전달합니다.
그런 다음, TransactionDefinition 및 ReactiveTransaction 객체를 사용하여 트랜잭션을 시작하고, 롤백하고, 커밋할 수 있습니다. 다음 예제는 그 방법을 보여 줍니다:
1DefaultTransactionDefinition def = new DefaultTransactionDefinition(); 2// explicitly setting the transaction name is something that can be done only programmatically 3def.setName("SomeTxName"); 4def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); 5 6Mono<ReactiveTransaction> reactiveTx = txManager.getReactiveTransaction(def); 7 8reactiveTx.flatMap(status -> { 9 10 Mono<Object> tx = ...; // put your business logic here 11 12 return tx.then(txManager.commit(status)) 13 .onErrorResume(ex -> txManager.rollback(status).then(Mono.error(ex))); 14});
1val def = DefaultTransactionDefinition() 2// explicitly setting the transaction name is something that can be done only programmatically 3def.setName("SomeTxName") 4def.propagationBehavior = TransactionDefinition.PROPAGATION_REQUIRED 5 6val reactiveTx = txManager.getReactiveTransaction(def) 7reactiveTx.flatMap { status -> 8 9 val tx = ... // put your business logic here 10 11 tx.then(txManager.commit(status)) 12 .onErrorResume { ex -> txManager.rollback(status).then(Mono.error(ex)) } 13}
Using @Transactional with AspectJ
Choosing Between Programmatic and Declarative Transaction Management