Loading...
Spring Framework Reference Documentation 7.0.2의 Using @Transactional의 한국어 번역본입니다.
아래의 경우에 피드백에서 신고해주신다면 반영하겠습니다.
감사합니다 :)
@Transactionaltransaction 설정에 대한 XML 기반 선언적 접근 방식에 더하여, annotation 기반 접근 방식을 사용할 수 있습니다. Java 소스 코드에서 transaction semantics를 직접 선언하면 선언이 영향을 받는 코드에 훨씬 더 가깝게 위치하게 됩니다.
과도한 결합의 위험은 크지 않은데, transactionally하게 사용되도록 의도된 코드는 어차피 거의 항상 그런 방식으로 배포되기 때문입니다.
표준
jakarta.transaction.Transactionalannotation도 Spring 고유 annotation의<br>대체제로 지원됩니다. 더 자세한 내용은 JTA 문서를 참조하십시오.<br>
@Transactional annotation 사용이 제공하는 사용 편의성은 다음에 이어지는
설명과 함께 예제로 보여주는 것이 가장 좋습니다.
다음 클래스 정의를 고려해 보십시오:
1// the service class that we want to make transactional 2@Transactional 3public class DefaultFooService implements FooService { 4 5 @Override 6 public Foo getFoo(String fooName) { 7 // ... 8 } 9 10 @Override 11 public Foo getFoo(String fooName, String barName) { 12 // ... 13 } 14 15 @Override 16 public void insertFoo(Foo foo) { 17 // ... 18 } 19 20 @Override 21 public void updateFoo(Foo foo) { 22 // ... 23 } 24}
1// the service class that we want to make transactional 2@Transactional 3class DefaultFooService : FooService { 4 5 override fun getFoo(fooName: String): Foo { 6 // ... 7 } 8 9 override fun getFoo(fooName: String, barName: String): Foo { 10 // ... 11 } 12 13 override fun insertFoo(foo: Foo) { 14 // ... 15 } 16 17 override fun updateFoo(foo: Foo) { 18 // ... 19 } 20}
위와 같이 클래스 레벨에서 사용될 때, annotation은 선언하는 클래스(및 그 서브클래스)의 모든 메서드에 대한 기본값을 나타냅니다. 또는 각 메서드를 개별적으로 annotation할 수도 있습니다.
Spring이 어떤 메서드를 transactional로 간주하는지에 대한 자세한 내용은 method visibility를 참조하십시오. 클래스 레벨 annotation은 클래스 계층 구조 상위의 상위 클래스에는 적용되지 않는다는 점에 유의하십시오. 이런 시나리오에서는, 서브클래스 레벨 annotation에 참여하도록 상속된 메서드를 로컬에서 재선언해야 합니다.
위와 같은 POJO 클래스가 Spring 컨텍스트에서 빈으로 정의되면,
@Configuration 클래스의 @EnableTransactionManagement annotation을 통해
빈 인스턴스를 transactional로 만들 수 있습니다. 전체 내용은
javadoc을
참조하십시오.
XML 설정에서 <tx:annotation-driven/> 태그는 유사한 편의성을 제공합니다:
1<!-- from the file 'context.xml' --> 2<?xml version="1.0" encoding="UTF-8"?> 3<beans xmlns="http://www.springframework.org/schema/beans" 4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 5 xmlns:aop="http://www.springframework.org/schema/aop" 6 xmlns:tx="http://www.springframework.org/schema/tx" 7 xsi:schemaLocation=" 8 http://www.springframework.org/schema/beans 9 https://www.springframework.org/schema/beans/spring-beans.xsd 10 http://www.springframework.org/schema/tx 11 https://www.springframework.org/schema/tx/spring-tx.xsd 12 http://www.springframework.org/schema/aop 13 https://www.springframework.org/schema/aop/spring-aop.xsd"> 14 15 <!-- this is the service object that we want to make transactional --> 16 <bean id="fooService" class="x.y.service.DefaultFooService"/> 17 18 <!-- enable the configuration of transactional behavior based on annotations --> 19 <!-- a TransactionManager is still required --> 20 <tx:annotation-driven transaction-manager="txManager"/> (1) 21 22 <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 23 <!-- (this dependency is defined somewhere else) --> 24 <property name="dataSource" ref="dataSource"/> 25 </bean> 26 27 <!-- other <bean/> definitions here --> 28 29</beans>
| 1 | 빈 인스턴스를 transactional로 만드는 라인입니다. |
<tx:annotation-driven/>태그에서transaction-managerattribute는<br>연결하려는TransactionManager의 빈 이름이transactionManager인 경우<br>생략할 수 있습니다. 의존성 주입하려는TransactionManager빈이<br>다른 이름을 가진 경우에는, 앞의 예제에서처럼transaction-manager<br>attribute를 사용해야 합니다. |
Reactive transactional 메서드는 다음 목록에서 보이는 것처럼, 명령형 프로그래밍 방식과 달리 reactive 반환 타입을 사용합니다:
1// the reactive service class that we want to make transactional 2@Transactional 3public class DefaultFooService implements FooService { 4 5 @Override 6 public Publisher<Foo> getFoo(String fooName) { 7 // ... 8 } 9 10 @Override 11 public Mono<Foo> getFoo(String fooName, String barName) { 12 // ... 13 } 14 15 @Override 16 public Mono<Void> insertFoo(Foo foo) { 17 // ... 18 } 19 20 @Override 21 public Mono<Void> updateFoo(Foo foo) { 22 // ... 23 } 24}
1// the reactive service class that we want to make transactional 2@Transactional 3class DefaultFooService : FooService { 4 5 override fun getFoo(fooName: String): Flow<Foo> { 6 // ... 7 } 8 9 override fun getFoo(fooName: String, barName: String): Mono<Foo> { 10 // ... 11 } 12 13 override fun insertFoo(foo: Foo): Mono<Void> { 14 // ... 15 } 16 17 override fun updateFoo(foo: Foo): Mono<Void> { 18 // ... 19 } 20}
반환된 Publisher에 대해서는 Reactive Streams 취소 신호와 관련된
특별한 고려사항이 있다는 점에 유의하십시오. 자세한 내용은
"Using the TransactionalOperator"의
Cancel Signals
섹션을 참조하십시오.
proxy 모드에서의 메서드 가시성과
@Transactional<br><br>@Transactionalannotation은 일반적으로public가시성을 가진 메서드에 사용됩니다.<br>6.0부터는 기본적으로 클래스 기반 proxy에 대해protected또는 패키지 가시성 메서드도<br>transactional로 만들 수 있습니다. 인터페이스 기반 proxy에서 transactional 메서드는<br>항상public이어야 하며 proxy된 인터페이스에 정의되어야 한다는 점에 유의하십시오.<br>두 종류의 proxy 모두에 대해, proxy를 통해 들어오는 외부 메서드 호출만 가로챕니다.<br>서로 다른 종류의 proxy에 걸쳐 메서드 가시성을 일관되게 처리하고 싶다면 (5.3까지의<br>기본값이었던),publicMethodsOnly를 지정하는 것을 고려하십시오:<br><br>```java /**
@Transactional 테스트 메서드를<br>지원합니다. 예제는 테스트 장의<br>Transaction Management를<br>참조하십시오. |@Transactional annotation은 인터페이스 정의, 인터페이스의 메서드,
클래스 정의, 클래스의 메서드에 적용할 수 있습니다. 그러나 @Transactional
annotation의 존재만으로는 transactional 동작이 활성화되기에 충분하지 않습니다.
@Transactional annotation은 단지 메타데이터일 뿐이며, 해당 메타데이터를 소비하는
런타임 인프라가 이 메타데이터를 사용하여 적절한 빈을 transactional 동작으로
설정합니다. 앞의 예제에서 <tx:annotation-driven/> element는
런타임에 실제 트랜잭션 관리를 켭니다.
Spring 팀은 5.0부터 인터페이스 기반 및 target-class proxy 모두에 대해<br>인터페이스의 annotation된 메서드도 동작하더라도, 인터페이스에 있는 annotation된 메서드에<br>의존하기보다는 구체 클래스의 메서드에
@Transactionalannotation을 붙일 것을<br>권장합니다. Java annotation은 인터페이스로부터 상속되지 않기 때문에, AspectJ 모드를<br>사용할 때 weaving 인프라는 여전히 인터페이스에 선언된 annotation을 인식하지<br>못하며, 그 결과 aspect가 적용되지 않습니다. 결과적으로 트랜잭션 annotation이<br>조용히 무시될 수 있습니다. 롤백 시나리오를 테스트할 때까지 코드는 "동작하는"<br>것처럼 보일 수 있습니다. |
proxy 모드(기본값)에서는 proxy를 통해 들어오는 외부 메서드 호출만<br>가로챕니다. 이는 self-invocation(실질적으로 target 객체 내의 한 메서드가<br>target 객체의 다른 메서드를 호출하는 것)이, 호출된 메서드가
@Transactional로<br>표시되어 있더라도 런타임에 실제 트랜잭션으로 이어지지 않는다는 것을 의미합니다.<br>또한 proxy는 기대한 동작을 제공하기 위해 완전히 초기화되어 있어야 하므로,<br>@PostConstruct메서드와 같은 초기화 코드에서 이 기능에 의존해서는<br>안 됩니다. |
self-invocation도 트랜잭션으로 래핑되기를 기대한다면 (아래 표의 mode
attribute를 참조), AspectJ 모드 사용을 고려하십시오. 이 경우 애초에 proxy가
존재하지 않습니다. 대신 target 클래스가 weaving(즉, 바이트 코드가 수정됨)되어,
어떤 종류의 메서드에도 @Transactional 런타임 동작을 지원합니다.
| XML Attribute | Annotation Attribute | Default | Description |
|---|---|---|---|
transaction-manager | N/A (see TransactionManagementConfigurer javadoc) | transactionManager | 사용할 트랜잭션 매니저의 이름입니다. 트랜잭션 매니저의 이름이 앞의 예제처럼<br>transactionManager가 아닌 경우에만 필요합니다. |
mode | mode | proxy | 기본 모드(proxy)는 annotation이 붙은 빈을 Spring의 AOP 프레임워크를 사용하여<br>proxy되도록 처리합니다(앞에서 논의한 proxy 시맨틱을 따르며, proxy를 통해<br>들어오는 메서드 호출에만 적용됩니다). 대안인 모드(aspectj)는 대신 영향을 받는 클래스를<br>Spring의 AspectJ 트랜잭션 aspect로 weaving하여, target 클래스 바이트 코드를 수정하고<br>어떤 종류의 메서드 호출에도 적용되도록 합니다. AspectJ weaving에는 클래스패스에<br>spring-aspects.jar가 필요하며, 로드 타임 weaving(또는 컴파일 타임 weaving)이<br>활성화되어 있어야 합니다. (로드 타임 weaving을 설정하는 방법에 대한 자세한 내용은<br>Spring configuration을<br>참조하십시오.) |
proxy-target-class | proxyTargetClass | false | proxy 모드에만 적용됩니다. @Transactional annotation이 붙은 클래스에 대해 생성되는<br>transactional proxy의 타입을 제어합니다. proxy-target-class attribute가 true로<br>설정되면 클래스 기반 proxy가 생성됩니다. proxy-target-class가 false이거나<br>attribute가 생략되면, 표준 JDK 인터페이스 기반 proxy가 생성됩니다. (서로 다른 proxy 타입에<br>대한 자세한 설명은 Proxying Mechanisms을<br>참조하십시오.) |
order | order | Ordered.LOWEST_PRECEDENCE | @Transactional로 annotation된 빈에 적용되는 트랜잭션 어드바이스의 순서를 정의합니다.<br>AOP 어드바이스 순서와 관련된 규칙에 대한 자세한 내용은<br>Advice Ordering을<br>참조하십시오. 순서가 지정되지 않으면 AOP 서브시스템이 어드바이스의 순서를 결정합니다. |
Table 1. Annotation driven transaction settings
@Transactionalannotation을 처리하기 위한 기본 어드바이스 모드는proxy이며,<br>이는 proxy를 통한 호출만 가로챌 수 있습니다. 동일한 클래스 내의 로컬 호출은<br>이 방식으로는 가로챌 수 없습니다. 보다 고급의 가로채기 모드가 필요하다면,<br>컴파일 타임 또는 로드 타임 weaving과 함께aspectj모드로 전환하는 것을 고려하십시오. |
proxy-target-classattribute는@Transactionalannotation이 붙은 클래스에 대해<br>생성되는 transactional proxy의 타입을 제어합니다.proxy-target-class가true로<br>설정되면 클래스 기반 proxy가 생성됩니다.proxy-target-class가false이거나<br>attribute가 생략되면, 표준 JDK 인터페이스 기반 proxy가 생성됩니다.<br>(서로 다른 proxy 타입에 대한 논의는<br>Proxying Mechanisms을<br>참조하십시오.) |
@EnableTransactionManagement와<tx:annotation-driven/>는 자신들이 정의된<br>동일한 애플리케이션 컨텍스트의 빈에서만@Transactional을 찾습니다. 이는<br>DispatcherServlet용WebApplicationContext에 annotation 기반 설정을<br>두면, 컨트롤러에서만@Transactional빈을 찾고 서비스에서는 찾지 않는다는<br>의미입니다. 자세한 내용은 MVC를<br>참조하십시오. |
가장 파생된 위치가 메서드에 대한 transactional 설정을 평가할 때 우선합니다.
다음 예제에서, DefaultFooService 클래스는 클래스 레벨에서 읽기 전용
트랜잭션에 대한 설정으로 annotation되어 있지만, 동일한 클래스의
updateFoo(Foo) 메서드에 있는 @Transactional annotation이 클래스 레벨에 정의된
transactional 설정보다 우선합니다.
1@Transactional(readOnly = true) 2public class DefaultFooService implements FooService { 3 4 public Foo getFoo(String fooName) { 5 // ... 6 } 7 8 // these settings have precedence for this method 9 @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW) 10 public void updateFoo(Foo foo) { 11 // ... 12 } 13}
1@Transactional(readOnly = true) 2class DefaultFooService : FooService { 3 4 override fun getFoo(fooName: String): Foo { 5 // ... 6 } 7 8 // these settings have precedence for this method 9 @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW) 10 override fun updateFoo(foo: Foo) { 11 // ... 12 } 13}
@Transactional Settings@Transactional annotation은 인터페이스, 클래스 또는 메서드가
transactional semantics(예: "이 메서드가 호출될 때, 기존 트랜잭션을 일시 중단하고
완전히 새로운 읽기 전용 트랜잭션을 시작한다")를 가져야 함을 지정하는 메타데이터입니다.
기본 @Transactional 설정은 다음과 같습니다:
PROPAGATION_REQUIRED.입니다.ISOLATION_DEFAULT.입니다.RuntimeException 또는 Error는 롤백을 트리거하며, 체크된 Exception은
그렇지 않습니다.이러한 기본 설정은 변경할 수 있습니다.
다음 표는 @Transactional
annotation의 다양한 프로퍼티를 요약한 것입니다:
| Property | Type | Description |
|---|---|---|
| value | String | 사용할 트랜잭션 매니저를 지정하는 선택적 한정자입니다. |
transactionManager | String | value의 별칭입니다. |
label | Array of String labels to add an expressive description to the transaction. | label은 트랜잭션 매니저가 실제 트랜잭션에 구현별 동작을<br>연관시키기 위해 평가할 수 있습니다. |
| propagation | enum: Propagation | 선택적 전파 설정입니다. |
isolation | enum: Isolation | 선택적 격리 수준입니다. REQUIRED 또는 REQUIRES_NEW 전파 값에만<br>적용됩니다. |
timeout | int (in seconds of granularity) | 선택적 트랜잭션 타임아웃입니다. REQUIRED 또는 REQUIRES_NEW 전파 값에만<br>적용됩니다. |
timeoutString | String (in seconds of granularity) | 예를 들어 플레이스홀더로서, 초 단위의 timeout을 String 값으로 지정하기 위한<br>대안입니다. |
readOnly | boolean | 읽기-쓰기 대 읽기 전용 트랜잭션입니다. REQUIRED 또는 REQUIRES_NEW 값에만<br>적용됩니다. |
rollbackFor | Array of Class objects, which must be derived from Throwable. | 롤백을 유발해야 하는 예외 타입의 선택적 배열입니다. |
rollbackForClassName | Array of exception name patterns. | 롤백을 유발해야 하는 예외 이름 패턴의 선택적 배열입니다. |
noRollbackFor | Array of Class objects, which must be derived from Throwable. | 롤백을 유발해서는 안 되는 예외 타입의 선택적 배열입니다. |
noRollbackForClassName | Array of exception name patterns. | 롤백을 유발해서는 안 되는 예외 이름 패턴의 선택적 배열입니다. |
Table 2. @Transactional Settings
롤백 규칙 시맨틱, 패턴, 패턴 기반 롤백 규칙에 대한<br>의도치 않은 매치 가능성에 대한 경고 등에 대해서는<br>Rollback rules를<br>참조하십시오. |
6.2부터는 예를 들어
@EnableTransactionManagement(rollbackOn=ALL_EXCEPTIONS)를<br>통해 기본 롤백 동작을 전역적으로 변경할 수 있으며, 이로 인해 트랜잭션<br>내에서 발생하는 모든 예외(체크 예외 포함)에 대해 롤백이 발생합니다.<br>추가 커스터마이징을 위해,AnnotationTransactionAttributeSource는 커스텀 기본 규칙을<br>위한addDefaultRollbackRule(RollbackRuleAttribute)메서드를 제공합니다.<br>트랜잭션별 롤백 규칙은 기본 동작을 오버라이드하지만, 지정되지 않은<br>예외에 대해서는 선택된 기본값을 유지한다는 점에 유의하십시오. 이는 Spring의<br>@Transactional뿐 아니라 JTA의jakarta.transaction.Transactionalannotation에도<br>해당됩니다.<br>커밋 동작을 가진 EJB 스타일 비즈니스 예외에 의존하지 않는 한, (잠재적으로<br>우발적인) 체크 예외의 경우에도 일관된 롤백 시맨틱을 위해<br>ALL_EXCEPTIONS로 전환하는 것이 바람직합니다. 또한 체크 예외에 대한 강제가<br>전혀 없는 Kotlin 기반 애플리케이션의 경우에도 이러한 전환이 바람직합니다. |
현재는 트랜잭션 이름에 대해 명시적으로 제어할 수 없습니다. 여기서 'name'이란
트랜잭션 모니터와 로깅 출력에 나타나는 트랜잭션 이름을 의미합니다.
선언적 트랜잭션의 경우, 트랜잭션 이름은 항상 트랜잭션 어드바이스가 적용된 클래스의
완전 수식 클래스 이름 + . + 메서드 이름입니다. 예를 들어,
BusinessService 클래스의 handlePayment(..) 메서드가 트랜잭션을 시작했다면,
트랜잭션 이름은 com.example.BusinessService.handlePayment가 됩니다.
@Transactional대부분의 Spring 애플리케이션은 하나의 트랜잭션 매니저만 필요하지만,
단일 애플리케이션에서 여러 개의 독립적인 트랜잭션 매니저를 원할 수 있는
상황이 있을 수 있습니다. @Transactional annotation의 value 또는
transactionManager attribute를 사용하여 사용할 TransactionManager의
식별자를 선택적으로 지정할 수 있습니다.
이는 트랜잭션 매니저 빈의 빈 이름 또는 한정자 값이 될 수 있습니다. 예를 들어 한정자 표기법을 사용하면, 애플리케이션 컨텍스트에서 다음 Java 코드를 다음 트랜잭션 매니저 빈 선언과 결합할 수 있습니다:
1public class TransactionalService { 2 3 @Transactional("order") 4 public void setSomething(String name) { ... } 5 6 @Transactional("account") 7 public void doSomething() { ... } 8 9 @Transactional("reactive-account") 10 public Mono<Void> doSomethingReactive() { ... } 11}
1class TransactionalService { 2 3 @Transactional("order") 4 fun setSomething(name: String) { 5 // ... 6 } 7 8 @Transactional("account") 9 fun doSomething() { 10 // ... 11 } 12 13 @Transactional("reactive-account") 14 fun doSomethingReactive(): Mono<Void> { 15 // ... 16 } 17}
다음 목록은 빈 선언을 보여줍니다:
1<tx:annotation-driven/> 2 3 <bean id="transactionManager1" class="org.springframework.jdbc.support.JdbcTransactionManager"> 4 ... 5 <qualifier value="order"/> 6 </bean> 7 8 <bean id="transactionManager2" class="org.springframework.jdbc.support.JdbcTransactionManager"> 9 ... 10 <qualifier value="account"/> 11 </bean> 12 13 <bean id="transactionManager3" class="org.springframework.data.r2dbc.connection.R2dbcTransactionManager"> 14 ... 15 <qualifier value="reactive-account"/> 16 </bean>
이 경우, TransactionalService의 개별 메서드는 order, account,
reactive-account 한정자로 구분되는 별도의 트랜잭션 매니저에서 실행됩니다.
특별히 한정자가 지정된 TransactionManager 빈을 찾지 못하면,
기본 <tx:annotation-driven> 대상 빈 이름인 transactionManager가
여전히 사용됩니다.
동일한 클래스의 모든 transactional 메서드가 동일한 한정자를 공유하는 경우,<br>대신 타입 레벨
org.springframework.beans.factory.annotation.Qualifierannotation을<br>선언하는 것을 고려하십시오. 그 값이 특정 트랜잭션 매니저의 한정자 값(또는 빈 이름)과<br>일치하면, 해당 트랜잭션 매니저가@Transactional자체에 특정 한정자가<br>없는 트랜잭션 정의에 사용됩니다. 이러한 타입 레벨 한정자는 구체 클래스에<br>선언할 수 있으며, 기본 클래스의 트랜잭션 정의에도 적용됩니다. 이는 기본 클래스의<br>한정자가 없는 메서드에 대해 기본 트랜잭션 매니저 선택을 효과적으로 오버라이드합니다.<br>마지막으로, 이러한 타입 레벨 빈 한정자는 예를 들어 값이 "order"인 경우<br>자동 주입 목적(예: order 리포지토리 식별)과 트랜잭션 매니저 선택 모두에 사용할 수<br>있습니다. 자동 주입 대상 빈과 연관된 트랜잭션 매니저 정의가 동일한 한정자<br>값을 선언하기만 하면 됩니다. 이러한 한정자 값은 ID로 사용될 필요는 없으며,<br>타입 매칭 빈 집합 내에서만 고유하면 됩니다. |
여러 다른 메서드에서 @Transactional과 함께 동일한 attribute를 반복해서 사용한다면,
Spring’s meta-annotation support를
통해 특정 사용 사례를 위한 커스텀 합성 annotation을 정의할 수 있습니다.
예를 들어,
다음 annotation 정의를 고려해 보십시오:
1@Target({ElementType.METHOD, ElementType.TYPE}) 2@Retention(RetentionPolicy.RUNTIME) 3@Transactional(transactionManager = "order", label = "causal-consistency") 4public @interface OrderTx { 5} 6 7@Target({ElementType.METHOD, ElementType.TYPE}) 8@Retention(RetentionPolicy.RUNTIME) 9@Transactional(transactionManager = "account", label = "retryable") 10public @interface AccountTx { 11}
1@Target(AnnotationTarget.FUNCTION, AnnotationTarget.TYPE) 2@Retention(AnnotationRetention.RUNTIME) 3@Transactional(transactionManager = "order", label = ["causal-consistency"]) 4annotation class OrderTx 5 6@Target(AnnotationTarget.FUNCTION, AnnotationTarget.TYPE) 7@Retention(AnnotationRetention.RUNTIME) 8@Transactional(transactionManager = "account", label = ["retryable"]) 9annotation class AccountTx
앞의 annotation을 사용하면, 이전 섹션의 예제를 다음과 같이 작성할 수 있습니다:
1public class TransactionalService { 2 3 @OrderTx 4 public void setSomething(String name) { 5 // ... 6 } 7 8 @AccountTx 9 public void doSomething() { 10 // ... 11 } 12}
1class TransactionalService { 2 3 @OrderTx 4 fun setSomething(name: String) { 5 // ... 6 } 7 8 @AccountTx 9 fun doSomething() { 10 // ... 11 } 12}
앞의 예제에서는 트랜잭션 매니저 한정자와 transactional label을 정의하는 syntax를 사용했지만, 전파 동작, 롤백 규칙, 타임아웃 및 기타 기능도 포함할 수 있습니다.
<tx:advice/> Settings
Transaction Propagation