Loading...
Spring Framework Reference Documentation 7.0.2의 Understanding the Spring Framework Transaction Abstraction의 한국어 번역본입니다.
아래의 경우에 피드백에서 신고해주신다면 반영하겠습니다.
감사합니다 :)
Spring transaction abstraction의 핵심은 transaction strategy 개념입니다. transaction strategy는 TransactionManager에 의해 정의되며, 구체적으로 명령형 transaction 관리를 위한 org.springframework.transaction.PlatformTransactionManager 인터페이스와 reactive transaction 관리를 위한 org.springframework.transaction.ReactiveTransactionManager 인터페이스에 의해 정의됩니다. 다음 예시는 PlatformTransactionManager API의 정의를 보여 줍니다:
1public interface PlatformTransactionManager extends TransactionManager { 2 3 TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException; 4 5 void commit(TransactionStatus status) throws TransactionException; 6 7 void rollback(TransactionStatus status) throws TransactionException; 8}
이것은 주로 service provider interface(SPI)이지만, 애플리케이션 코드에서 프로그래밍 방식으로 사용할 수도 있습니다. PlatformTransactionManager는 인터페이스이기 때문에 필요에 따라 쉽게 mock 또는 stub 할 수 있습니다. JNDI와 같은 lookup strategy에 묶여 있지 않습니다.
PlatformTransactionManager 구현체는 Spring Framework IoC 컨테이너 안에서 다른 객체(또는 빈)와 마찬가지로 정의됩니다. 이 장점만으로도, JTA를 사용할 때조차 Spring Framework transaction을 가치 있는 추상화로 만들어 줍니다. JTA를 직접 사용하는 경우보다 transactional 코드를 훨씬 더 쉽게 테스트할 수 있습니다.
다시 말해, Spring의 철학에 따라, PlatformTransactionManager 인터페이스의 어떤 메서드에서도 던져질 수 있는 TransactionException은 unchecked입니다(즉, java.lang.RuntimeException 클래스를 확장합니다). Transaction 인프라 실패는 거의 항상 치명적입니다. 애플리케이션 코드가 transaction 실패로부터 실제로 복구할 수 있는 드문 경우에는, 애플리케이션 개발자가 여전히 TransactionException을 catch하고 처리하도록 선택할 수 있습니다.
중요한 점은, 개발자가 그렇게 하도록 강요받지 않는다는 것입니다.
getTransaction(..) 메서드는 TransactionDefinition 매개변수에 따라 TransactionStatus 객체를 반환합니다. 반환된 TransactionStatus는 새로운 transaction을 나타낼 수도 있고, 현재 호출 스택에 일치하는 transaction이 존재하는 경우 기존 transaction을 나타낼 수도 있습니다.
후자의 경우의 의미는, Jakarta EE transaction 컨텍스트와 마찬가지로, TransactionStatus가 실행 스레드에 연관되어 있다는 것입니다.
Spring은 또한 reactive 타입 또는 Kotlin Coroutines를 사용하는 reactive 애플리케이션을 위한 transaction 관리 추상화도 제공합니다. 다음 예시는 org.springframework.transaction.ReactiveTransactionManager에 의해 정의되는 transaction strategy를 보여 줍니다:
1public interface ReactiveTransactionManager extends TransactionManager { 2 3 Mono<ReactiveTransaction> getReactiveTransaction(TransactionDefinition definition) throws TransactionException; 4 5 Mono<Void> commit(ReactiveTransaction status) throws TransactionException; 6 7 Mono<Void> rollback(ReactiveTransaction status) throws TransactionException; 8}
Reactive transaction manager는 주로 service provider interface(SPI)이지만, 애플리케이션 코드에서 프로그래밍 방식으로 사용할 수도 있습니다. ReactiveTransactionManager는 인터페이스이기 때문에 필요에 따라 쉽게 mock 또는 stub 할 수 있습니다.
TransactionDefinition 인터페이스는 다음을 지정합니다:
Propagation: 일반적으로, transaction 범위 안의 모든 코드는 그 transaction 안에서 실행됩니다. 그러나, transaction 컨텍스트가 이미 존재할 때 transactional 메서드가 실행되는 경우의 동작을 지정할 수 있습니다. 예를 들어, 코드가 기존 transaction 안에서 계속 실행될 수 있습니다(일반적인 경우). 또는 기존 transaction이 suspend되고 새로운 transaction이 생성될 수 있습니다. Spring은 EJB CMT에서 익숙한 모든 transaction propagation 옵션을 제공합니다. Spring에서의 transaction propagation 의미론에 대해 읽으려면 Transaction Propagation을 참조하십시오.
Isolation: 이 transaction이 다른 transaction의 작업으로부터 어느 정도까지 분리되는지를 의미합니다. 예를 들어, 이 transaction이 다른 transaction의 uncommitted write를 볼 수 있는지 여부입니다.
Timeout: 이 transaction이 timeout되어 underlying transaction 인프라에 의해 자동으로 rollback되기 전까지 얼마나 오래 실행되는지를 의미합니다.
Read-only status: 코드가 데이터를 읽기만 하고 수정하지 않을 때 read-only transaction을 사용할 수 있습니다. Read-only transaction은 Hibernate를 사용할 때와 같이 일부 경우에 유용한 최적화가 될 수 있습니다.
이러한 설정은 표준적인 transaction 개념을 반영합니다. 필요하다면, transaction isolation level 및 기타 핵심 transaction 개념을 다루는 자료를 참조하십시오. 이러한 개념을 이해하는 것은 Spring Framework 또는 어떤 transaction 관리 솔루션을 사용하더라도 필수적입니다.
TransactionStatus 인터페이스는 transactional 코드가 transaction 실행을 제어하고 transaction 상태를 조회하기 위한 간단한 방법을 제공합니다. 이러한 개념은 모든 transaction API에 공통적이므로 익숙할 것입니다. 다음 예시는 TransactionStatus 인터페이스를 보여 줍니다:
1public interface TransactionStatus extends TransactionExecution, SavepointManager, Flushable { 2 3 @Override 4 boolean isNewTransaction(); 5 6 boolean hasSavepoint(); 7 8 @Override 9 void setRollbackOnly(); 10 11 @Override 12 boolean isRollbackOnly(); 13 14 void flush(); 15 16 @Override 17 boolean isCompleted(); 18}
Spring에서 선언적 또는 프로그래밍 방식 transaction 관리 중 어느 것을 선택하든, 올바른 TransactionManager 구현체를 정의하는 것은 절대적으로 필수적입니다. 일반적으로 의존성 주입을 통해 이 구현체를 정의합니다.
TransactionManager 구현체는 보통 자신이 동작하는 환경(JDBC, JTA, Hibernate 등)에 대한 지식을 필요로 합니다. 다음 예시는 local PlatformTransactionManager 구현체를 정의하는 방법을 보여 줍니다(이 경우 plain JDBC를 사용).
다음과 같이 빈을 생성하여 JDBC DataSource를 정의할 수 있습니다:
1<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> 2 <property name="driverClassName" value="${jdbc.driverClassName}" /> 3 <property name="url" value="${jdbc.url}" /> 4 <property name="username" value="${jdbc.username}" /> 5 <property name="password" value="${jdbc.password}" /> 6</bean>
관련된 PlatformTransactionManager 빈 정의는 그 다음 DataSource 정의에 대한 reference를 가집니다. 다음 예제와 유사해야 합니다:
1<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 2 <property name="dataSource" ref="dataSource"/> 3</bean>
Jakarta EE 컨테이너에서 JTA를 사용하는 경우, Spring의 JtaTransactionManager와 함께 JNDI를 통해 얻은 컨테이너 DataSource를 사용합니다. 다음 예제는 JTA 및 JNDI lookup 버전이 어떻게 보이는지 보여 줍니다:
1<?xml version="1.0" encoding="UTF-8"?> 2<beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:jee="http://www.springframework.org/schema/jee" 5 xsi:schemaLocation=" 6 http://www.springframework.org/schema/beans 7 https://www.springframework.org/schema/beans/spring-beans.xsd 8 http://www.springframework.org/schema/jee 9 https://www.springframework.org/schema/jee/spring-jee.xsd"> 10 11 <jee:jndi-lookup id="dataSource" jndi-name="jdbc/jpetstore"/> 12 13 <bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager" /> 14 15 <!-- other <bean/> definitions here --> 16 17</beans>
JtaTransactionManager는 컨테이너의 global transaction 관리 인프라를 사용하기 때문에 DataSource(또는 다른 특정 리소스)에 대해 알 필요가 없습니다.
위의
dataSource빈 정의는jeenamespace의<jndi-lookup/>태그를 사용합니다. 자세한 내용은 The JEE Schema를 참조하십시오.
JTA를 사용하는 경우, JDBC, Hibernate JPA 또는 다른 어떤 지원되는 기술을 사용하든지 간에 transaction manager 정의는 동일하게 보여야 합니다. 이는 JTA transaction이 global transaction이며, 어떤 transactional 리소스든 enlist할 수 있기 때문입니다.
모든 Spring transaction 설정에서 애플리케이션 코드는 변경될 필요가 없습니다. Local에서 global transaction으로 또는 그 반대로 이동하는 것과 같은 변경이라 하더라도, 단지 설정을 변경하는 것만으로 transaction이 관리되는 방식을 변경할 수 있습니다.
다음 예제에서 보듯이 Hibernate local transaction도 쉽게 사용할 수 있습니다. 이 경우, 애플리케이션 코드가 Hibernate Session 인스턴스를 얻는 데 사용할 수 있는 Hibernate LocalSessionFactoryBean을 정의해야 합니다.
DataSource 빈 정의는 앞에서 보여 준 local JDBC 예제와 유사하며, 따라서 다음 예제에는 표시되지 않습니다.
Jakarta EE 컨테이너에 의해 관리되고 JNDI를 통해 lookup되는
DataSource(어떤 non-JTA transaction manager에서 사용되는 경우)는 non-transactional이어야 합니다. 이는 Jakarta EE 컨테이너가 아니라 Spring Framework가 transaction을 관리하기 때문입니다.
이 경우 txManager 빈은 HibernateTransactionManager 타입입니다. DataSourceTransactionManager가 DataSource에 대한 reference를 필요로 하는 것과 같은 방식으로, HibernateTransactionManager는 SessionFactory에 대한 reference를 필요로 합니다. 다음 예제는 sessionFactory와 txManager 빈을 선언합니다:
1<bean id="sessionFactory" class="org.springframework.orm.jpa.hibernate.LocalSessionFactoryBean"> 2 <property name="dataSource" ref="dataSource"/> 3 <property name="mappingResources"> 4 <list> 5 <value>org/springframework/samples/petclinic/hibernate/petclinic.hbm.xml</value> 6 </list> 7 </property> 8 <property name="hibernateProperties"> 9 <value> 10 hibernate.dialect=${hibernate.dialect} 11 </value> 12 </property> 13</bean> 14 15<bean id="txManager" class="org.springframework.orm.jpa.hibernate.HibernateTransactionManager"> 16 <property name="sessionFactory" ref="sessionFactory"/> 17</bean>
Hibernate와 Jakarta EE 컨테이너 관리 JTA transaction을 사용하는 경우, 다음 예제에서 보듯이 JDBC에 대한 이전 JTA 예제와 동일한 JtaTransactionManager를 사용해야 합니다. 또한, transaction coordinator 및 필요할 경우 connection release mode 설정을 통해 Hibernate가 JTA를 인식하도록 만드는 것이 권장됩니다:
1<bean id="sessionFactory" class="org.springframework.orm.jpa.hibernate.LocalSessionFactoryBean"> 2 <property name="dataSource" ref="dataSource"/> 3 <property name="mappingResources"> 4 <list> 5 <value>org/springframework/samples/petclinic/hibernate/petclinic.hbm.xml</value> 6 </list> 7 </property> 8 <property name="hibernateProperties"> 9 <value> 10 hibernate.dialect=${hibernate.dialect} 11 hibernate.transaction.coordinator_class=jta 12 hibernate.connection.handling_mode=DELAYED_ACQUISITION_AND_RELEASE_AFTER_STATEMENT 13 </value> 14 </property> 15</bean> 16 17<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>
또는, 동일한 default를 강제하기 위해 JtaTransactionManager를 LocalSessionFactoryBean에 전달할 수도 있습니다:
1<bean id="sessionFactory" class="org.springframework.orm.jpa.hibernate.LocalSessionFactoryBean"> 2 <property name="dataSource" ref="dataSource"/> 3 <property name="mappingResources"> 4 <list> 5 <value>org/springframework/samples/petclinic/hibernate/petclinic.hbm.xml</value> 6 </list> 7 </property> 8 <property name="hibernateProperties"> 9 <value> 10 hibernate.dialect=${hibernate.dialect} 11 </value> 12 </property> 13 <property name="jtaTransactionManager" ref="txManager"/> 14</bean> 15 16<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>
Advantages of the Spring Framework’s Transaction Support Model
Synchronizing Resources with Transactions