Declarative Transaction Management

본 문서에서는 코드에서 직접적으로 Transaction 처리하지 않고, 선언적으로 Transaction을 관리할 수 있는 방법에 대해 살펴보기로 하자. Spring에서는 선언적인 Transaction 관리를 위해 다음과 같이 3가지 방법을 제공한다.

Annotation을 이용한 Transaction 관리

Configuration

본 매뉴얼 >> Tech. Service >> Transaction 을 참고하여 Transaction 서비스의 속성을 정의한다.
다음은 Transaction 서비스의 속성을 정의한 XML(applicationContext-transaction-datasource-oracle.xml ) 파일로, Transaction을 관리하는 실질적 역할을 수행하는 TransactionManager가 정의되어 있다.
<bean id="oracleTransactionManagerDataSource" 
      class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<property name="dataSource"><ref bean="oracle_datasource"/></property>
</bean>

또한, @Transactional이 적용된 클래스들을 런타임시에 Proxy 클래스로 대체시켜, Transaction 관리 대상인 메소드가 호출되면 Proxy에서 Transaction 서비스를 통해 Transaction이 시작된 후 해당 메소드가 호출될 수 있게 하기 위해 Spring 속성 정의 XML 파일에 다음과 같이 추가해주어야 한다.
<tx:annotation-driven transaction-manager="oracleTransactionManagerDataSource"/>

Transaction 관리 대상 정의

Spring에서 제공하는 @Transactional Annotation을 이용하여 Transaction 관리 대상 클래스 또는 메소드를 식별한다.
다음은 UserServiceImplWithAnnotation 의 일부로 updateUserList() 메소드에 대해 Transaction 관리 여부를 정의하고 있음을 알 수 있다.
// 메소드 실행중 EmpException이 발생한 경우 rollback을 수행하지 않음.
@Transactional(noRollbackFor={EmpException.class})
public void updateUserList(UserVO newUser, UserVO updateUser) throws EmpException {
	String userName = "";
	try {
		userName = newUser.getUserName();
		userDAO.addUser(newUser);
		
		userName = updateUser.getUserName();
		userDAO.updateUser(updateUser);
	}
	catch (Exception e) {
		// 중략
	}
}
위 샘플 코드에 정의된 바와 같이 Transaction 관리를 위해 @Transactional Annotation에는 다음과 같은 상세 속성 정보를 부여할 수 있다.
속성 설명
isolation Transaction의 isolation Level 정의하는 요소. 별도로 정의하지 않으면 DB의 Isolation Level을 따름.
Isolation.DEFAULT, Isolation.READ_COMMITTED, Isolation.READ_UNCOMMITTED, Isolation.REPEATABLE_READ, Isolation.SERIALIZABLE 중 선택하여 정의할 수 있다. (Default = Isolation.DEFAULT)
각 Isolation Level에 대한 자세한 내용은 본 페이지의 [참고] Propagation Behavior, Isolation Level 를 참고하도록 한다.
noRollbackFor 정의된 Exception 목록에 대해서는 rollback을 수행하지 않음.
noRollbackForClassname Class 객체가 아닌 문자열을 이용하여 rollback을 수행하지 않아야 할 Exception 목록 정의
propagation Transaction의 propagation 유형을 정의하기 위한 요소.
Propagation.MANDATORY, Propagation.NESTED, Propagation.NEVER, Propagation.NOT_SUPPORTED, Propagation.REQUIRED, Propagation.REQUIRES_NEW, Propagation.SUPPORTS 중 선택하여 정의할 수 있다. (Default = Propagation.REQUIRED)
각 Propagation 유형에 대한 자세한 내용은 본 페이지의 [참고] Propagation Behavior, Isolation Level 를 참고하도록 한다.
readOnly 해당 Transaction을 읽기 전용 모드로 처리 (Default = false)
rollbackFor 정의된 Exception 목록에 대해서는 rollback 수행
rollbackForClassName Class 객체가 아닌 문자열을 이용하여 rollback을 수행해야 할 Exception 목록 정의
timeout 지정한 시간 내에 해당 메소드 수행이 완료되지 않은 경우 rollback 수행. -1일 경우 no timeout (Default = -1)

테스트 클래스 실행

Annotation을 이용한 Transaction 관리 방법에 대한 테스트는 UserServiceImplWithAnnotation 의 updateUserList 메소드에 정의된 트랜잭션 관리 속성 정보를 기반으로 한다. 테스트 클래스 구성은 본 페이지 내의 테스트 케이스 상세 를 참조하며, 보다 자세한 코드는 UserServiceWithAnnotationTest 를 참조하도록 한다.

XML 정의를 이용한 Transaction 관리

Configuration

본 매뉴얼 >> Tech. Service >> Transaction 을 참고하여 Transaction 서비스의 속성을 정의한다.
다음은 Transaction 서비스의 속성을 정의한 XML(applicationContext-transaction-datasource-oracle.xml ) 파일로, Transaction을 관리하는 실질적 역할을 수행하는 TransactionManager가 정의되어 있다.
<bean id="oracleTransactionManagerDataSource" 
      class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<property name="dataSource"><ref bean="oracle_datasource"/></property>
</bean>

Transaction 관리 대상 정의

다음 Spring 속성 정의 XML(applicationContext-user-aop.xml )과 같이 <tx:advice>와 <aop:config>를 이용하여 Advice와 Pointcut을 정의한다. (단, <tx:advice>와 <aop:config>를 이용하기 위해서는 tx, aop namespace에 대한 정의가 필요하다.)
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
	http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
	http://www.springframework.org/schema/aop 
	http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
	http://www.springframework.org/schema/tx 
	http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">

<tx:advice id="txAdvice" transaction-manager="oracleTransactionManagerDataSource">
	<tx:attributes>
		<!-- 메소드 실행중 EmpException이 발생한 경우 rollback을 수행하지 않음 -->
		<tx:method name="*" no-rollback-for="com.sds.emp.common.EmpException"/>
	</tx:attributes>
</tx:advice>

<aop:config>
	<!-- pointcut 정의 : UserServiceImplWithXML 클래스의 모든 메소드 호출시 -->
	<aop:pointcut id="userServiceOperations" 
	     expression="execution(* com.sds.emp.user.services.impl.UserServiceImplWithXML.*(..))"/>
	<!-- advice 정의 : 위 tx 태그를 이용하여 정의한 advice 참조 -->
	<aop:advisor advice-ref="txAdvice" pointcut-ref="userServiceOperations"/>
</aop:config>
위 샘플 XML에서와 같이 Transaction 관리를 위해 <tx:advice> 하위 태그인 <tx:method>에는 다음과 같은 상세 속성 정보를 부여할 수 있다.
속성 설명
name 메소드명. 와일드카드 사용 가능
isolation Transaction의 isolation Level을 정의하는 요소. 별도로 정의하지 않으면 DB의 Isolation 레벨을 따름.
DEFAULT, READ_COMMITTED, READ_UNCOMMITTED, REPEATABLE_READ, SERIALIZABLE 중 선택하여 정의할 수 있다. (Default = DEFAULT)
각 Isolation Level에 대한 자세한 내용은 본 페이지의 [참고] Propagation Behavior, Isolation Level 를 참고하도록 한다.
no-rollback-for 정의된 Exception 목록에 대해서는 rollback을 수행하지 않음.
propagation Transaction의 propagation 유형을 정의하기 위한 요소.
MANDATORY, NESTED, NEVER, NOT_SUPPORTED, REQUIRED, REQUIRES_NEW, SUPPORTS 중 선택하여 정의할 수 있다. (Default = REQUIRED)
각 Propagation 유형에 대한 자세한 내용은 본 페이지의 [참고] Propagation Behavior, Isolation Level 를 참고하도록 한다.
read-only 해당 Transaction을 읽기 전용 모드로 처리. (Default = false)
rollback-for 정의된 Exception 목록에 대해서는 rollback 수행
timeout 지정한 시간 내에 해당 메소드 수행이 완료되지 않은 경우 rollback 수행. -1일 경우 no timeout. (Default = -1)

테스트 클래스 실행

XML 정의를 이용한 Transaction 관리 방법에 대한 테스트는 applicationContext-user-aop.xml 의 updateUserList 메소드에 정의된 트랜잭션 관리 속성 정보를 기반으로 한다. 테스트 클래스 구성은 본 페이지 내의 테스트 케이스 상세 를 참조하며, 보다 자세한 코드는 UserServiceWithXMLTest 를 참조하도록 한다.

TransactionProxyFactoryBean을 이용한 Transaction 관리

Configuration

TransactionProxyFactoryBean을 이용하여 Transaction을 관리하기 위해서는 Transaction 서비스와 TransactionProxyFactoryBean에 대한 속성 정의가 필요하다.
본 매뉴얼 >> Tech. Service >> Transaction 을 참고하여 Transaction 서비스의 속성을 정의한다.
다음은 Transaction 서비스의 속성을 정의한 XML(applicationContext-transaction-datasource-oracle.xml ) 파일로, Transaction을 관리하는 실질적 역할을 수행하는 TransactionManager가 정의되어 있다.
			
<bean id="oracleTransactionManagerDataSource" 
      class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<property name="dataSource"><ref bean="oracle_datasource"/></property>
</bean>

다음은 TransactionProxyFactoryBean의 속성을 정의한 XML(applicationContext-user.xml ) 파일의 일부 내용이다.
<bean id="txConfig" abstract="true"
	class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
	<property name="transactionManager" ref="oracleTransactionManagerDataSource"/>
	<property name="transactionAttributes">
		<props>
			<prop key="*">PROPAGATION_REQUIRED,-java.lang.Exception,
			              -anyframe.common.exception.BaseException,
			              +com.sds.emp.common.EmpException</prop>
		</props>
	</property>
</bean>	
위 샘플 XML에서와 같이 Transaction 관리를 위해 TransactionProxyFactoryBean에는 다음과 같은 상세 속성 정보를 부여할 수 있다.
Property Name
Description
Required
Default Value
transactionManager 사용할 Transaction manager 속성을 정의한 Transaction Manager의 id를 참조한다. Transaction Manager 속성 정의 내용은 하단의 내용을 참고하도록 한다.
Y
N/A
transactionAttributes target의 메소드 별로 transaction attributes를 정의할 수 있다. 여기에 Propagation Behavior, Isolation Level, "readOnly" flag, Rollback 규칙를 설정할 수 있다.(하단 '[참고] transactionAttributes 속성' 그림을 참고한다) e.g. key ="myMethod", value ="PROPAGATION_REQUIRED,readOnly"
  • Propagation Behavior(전달 행위): [Required] Transaction의 propagation 유형을 정의하기 위한 요소.
    PROPAGATION_MANDATORY, PROPAGATION_NESTED, PROPAGATION_NEVER, PROPAGATION_NOT_SUPPORTED, PROPAGATION_REQUIRED, PROPAGATION_REQUIRES_NEW, PROPAGATION_SUPPORTS 중 선택하여 정의할 수 있다. (Default = PROPAGATION_REQUIRED)
    각 Propagation 유형에 대한 자세한 내용은 본 페이지의 [참고] Propagation Behavior, Isolation Level 를 참고하도록 한다.
  • Isolation Level : [Optional] Transaction의 isolation Level을 정의하는 요소. 별도로 정의하지 않으면 DB의 Isolation 레벨을 따름.
    ISOLATION_DEFAULT, ISOLATION_READ_COMMITTED, ISOLATION_READ_UNCOMMITTED, ISOLATION_REPEATABLE_READ, ISOLATION_SERIALIZABLE 중 선택하여 정의할 수 있다. (Default = ISOLATION_DEFAULT)
    각 Isolation Level에 대한 자세한 내용은 본 페이지의 [참고] Propagation Behavior, Isolation Level 를 참고하도록 한다.
  • "readOnly" flag : [Optional] readOnly falg는 상응하는 Transaction이 읽기 전용 Transaction으로 최적화 되어야 한다는 것을 나타내는 일종의 최적화 힌트이다. 어떤 Transaction 전략은 중요한 최적화를 수행할 수 있는데, 예를 들어 Hibernate나 TopLink 같은 객체/관계 매핑 도구를 사용할 때 dirty checking("flush" 시도)을 회피하는 경우를 들 수 있다. 일반적으로 Transaction 내에서 SELECT 쿼리만을 실행하는 경우에 이 속성을 사용한다.
  • Rollback 규칙 : [Optional] Transaction의 Default 설정은 RuntimeException이 발생할 경우에는 Rollback, CheckedException이 발생하는 경우에는 Commit되도록 하고 있다. 그러나 Transaction의 속성을 지정할 때 Rollback 규칙을 이용하여 디폴트 설정을 변경하는 것이 가능하다. 하단 그림의 Rollback 규칙에서 마이너스(-)로 시작하는 Exception에 대해서는 무조건 Rollback, 플러스(+)로 시작하는 Exception은 무조건 Commit되도록 규칙을 변경할 수 있다. 이때 플러스(+)로 시작하는 Exception의 경우 Runtime Exception에 대해서도 무조건 Commit 처리되므로 신중히 사용하도록 한다. (ex. -java.lang.Exception,-anyframe.common.exception.BaseException)
Y
N/A
target TransactionProxyFactoryBean bean의 abstract attribute 값을 true로 놓고, 이 TransactionProxyFactoryBean bean을 parent로 정의한 bean 내에 target을 정의한다.
Y
N/A

[참고] transactionAttributes 속성

Transaction 관리 대상 정의

앞서 정의한 TransactionProxyFactoryBean을 이용하여 Transaction 관리 대상 Bean을 정의한다.
다음은 applicationContext-user.xml 의 일부로 앞서 정의한 TransactionProxyFactoryBean을 parent Bean으로 하고, target property 내에 Transaction 관리 대상 객체인 UserServiceImpl 클래스를 정의하고 있다. 이 때, proxyInterfaces property에 Transaction 관리 대상 객체를 Transaction Proxy로 감싸기 위한 해당 클래스의 인터페이스를 추가로 정의해 주어야 함을 잊지 않도록 하자.
<bean id="com.sds.emp.user.services.UserService" parent="txConfig">
	<property name="target">
		<bean class="com.sds.emp.user.services.impl.UserServiceImpl">
			<property name="userDAO">
				<bean class="com.sds.emp.user.services.impl.UserDAO">
					<property name="queryService" 
					    ref="oracle_queryservice" />
					<property name="propertiesService" 
					    ref="propertiesService" />
				</bean>
			</property>
		</bean>		
	</property>
	<property name="proxyInterfaces" value="com.sds.emp.user.services.UserService"/>
</bean>	

테스트 클래스 실행

TransactionProxyFactoryBean을 이용한 Transaction 관리 방법에 대한 테스트는 applicationContext-user.xml 의 updateUserList 메소드에 정의된 트랜잭션 관리 속성 정보를 기반으로 한다. 테스트 클래스 구성은 본 페이지 내의 테스트 케이스 상세 를 참조하며, 보다 자세한 코드는 UserServiceTest 를 참조하도록 한다.

[참고] Propagation Behavior, Isolation Level

다음에서는 Transaction 속성값으로 정의할 수 있는 Propagation Behavior와 Isolation Level에 보다 자세히 알아보기로 하자.

Propagation Behavior

Propagation Behavior(전달 행위)는 Transaction 전파 규칙을 정의하기 위해 사용된다.
Attribute Name
Description
PROPAGATION_MADATORY 반드시 Transaction 내에서 메소드가 실행되야 하고,Transaction이 없는 경우에는 예외를 발생시킨다.
PROPAGATION_NESTED Transaction에 있는 경우, 기존 Transaction 내의 nested transaction 형태로 메소드를 실행하고, nested transaction 자체적으로 commit, rollback이 가능하다. Transaction이 없는 경우, PROPAGATION_REQUIRED 속성으로 행동한다.
PROPAGATION_NEVER Transaction 컨텍스트 없이 실행되어야 하며 Transaction이 있으면 예외를 발생시킨다.
PROPAGATION_NOT_SUPPORTED Transaction 없이 메소드를 실행하며,기존의 Transaction이 있는 경우에는 이 Transaction을 호출된 메소드가 끝날 때까지 잠시 보류한다.
PROPAGATION_REQUIRED Transaction 컨텍스트 내에서 메소드가 실행되어야 한다. 기존 Transaction이 있는 경우에는 기존 Transaction 내에서 실행하고, 기존 Transaction이 없는 경우에는 새로운 Transaction을 생성한다.
PROPAGATION_REQUIRED_NEW 호출되는 메소드는 자신 만의 Transaction을 가지고 실행하고, 기존의 Transaction들은 보류된다.
PROPAGATION_SUPPORTS 새로운 Transaction을 필요로 하지는 않지만, 기존의 Transaction이 있는 경우에는 Transaction 내에서 메소드를 실행한다.

Isolation Level

Isolation Level(격리수준)은 Transaction에서 일관성이 없는 데이터를 허용하도록 하는 수준이며, 여러 Transaction들이 다른 Transaction의 방해로부터 보호되는 정도를 나타낸다.
예를 들어, 한 사용자가 어떠한 데이터를 수정하고 있는 경우 다른 사용자들이 그 데이터에 접근하는 것을 차단함으로써 완전한 데이터만을 사용자들에게 제공하게 된다. 또한, 많은 사용자들의 수정 작업으로 인하여 통계 자료를 작성할 수 없는 사용자를 위하여 읽기 작업을 수행할 수 있도록 Isolation Level을 변경할 수 있다.
Attribute Name
Description
ISOLATION_DEFAULT 개별적인 PlatformTransactionManager를 위한 디폴트 격리 레벨
ISOLATION_READ_COMMITTED 이 격리수준을 사용하는 메소드는 commit 되지 않은 데이터를 읽을 수 없다. 쓰기 락은 다른 Transaction에 의해 이미 변경된 데이터는 얻을수 없다. 따라서 조회 중인 commit 되지 않은 데이터는 불가능하다. 대개의 데이터베이스에서의 디폴트로 지원하는 격리 수준이다.
ISOLATION_READ_UNCOMMITTED 가장 낮은 Transaction 수준이다. 이 격리수준을 사용하는 메소드는 commit 되지 않은 데이터를 읽을 수 있다. 그러나 이 격리수준은 새로운 레코드가 추가되었는지 알수 없다.
ISOLATION_REPEATABLE_READ ISOLATION_READ_COMMITED 보다는 다소 조금 더 엄격한 격리 수준이다. 이 격리 수준은 다른 Transaction이 새로운 데이터를 입력했다면, 새롭게 입력된 데이터를 조회할 수 있다는 것을 의미한다.
ISOLATION_SERIALIZABLE 가장 높은 격리수준이다. 모든 Transaction(조회를 포함하여)은 각 라인이 실행될 때마다 기다려야 하기 때문에 매우 느리다. 이 격리수준을 사용하는 메소드는 데이터 상에 배타적 쓰기를 락을 얻음으로써 Transaction이 종료될 때까지 조회, 수정, 입력 데이터로부터 다른 Transaction의 처리를 막는다. 가장 많은 비용이 들지만 신뢰할만한 격리 수준을 제공하는 것이 가능하다.

테스트 케이스 상세

다음은 앞서 언급한 Annotation을 이용한 Transaction 관리 , XML 정의를 이용한 Transaction 관리 , TransactionProxyFactoryBean을 이용한 Transaction 관리 방법을 테스트해 보기 위해 동일한 로직으로 구성된 테스트 클래스의 일부이다.
각 테스트 클래스의 testUpdateUserWithNotExistUser 메소드에서는 UserService의 updateUserList 메소드를 호출한다. updateUserList 메소드의 첫번째 입력 인자는 신규 사용자로 두번째 입력 인자는 수정할 사용자로 인식된다. 따라서 첫번째 입력 인자를 신규 사용자 정보로 하고 두번째 입력 인자를 존재하지 않는 사용자 ID를 가진 사용자 정보로 전달하였을 경우 신규 사용자를 성공적으로 등록하고 두번째 특정 사용자 정보를 수정하려고 했을 때 해당 ID를 가진 사용자가 존재하지 않아 수정에 실패하게 된다. UserService의 updateUserList 메소드에 대해 정의된 Transaction 속성에 의해 신규로 등록한 사용자 정보가 rollback되지 않고 commit되어야 한다.
public void testUpdateUserWithNotExistUser() throws EmpException {
	UserService userService = null;
	try {
		userService = (UserService) context.getBean("UserServiceWithAnnotation");

		// 1. update user list
		UserVO newUser = new UserVO();
		newUser.setUserId("testuser");
		// 중략
		newUser.setRegDate(null);

		UserVO updateUser = userService.getUser("woos41");
		// 존재하지 않는 사용자 ID로 변경
		updateUser.setUserId("woos");
		String name = "woostest";
		updateUser.setUserName(name);

		// 신규 사용자 정보와 수정 대상 사용자 정보를 인자로 셋팅
		userService.updateUserList(newUser, updateUser);
		// 해당 메소드 수행시 에러가 발생하지 않으면 fail
		throw new EmpException("fail to get user.");
	} catch (EmpException e) {
		try {
			// 신규 사용자의 ID로 사용자 정보 조회
			userService.getUser("testuser");
		} catch (EmpException ee) {
			// 사용자 정보 수정에 실패하였으나 Annotation 정의에 따라 
			// 신규 등록된 사용자 정보는 commit되었어야 함.		
			throw new EmpException("fail to manage transaction.");
		}
	}
}

Resources

  • 다운로드
  • 샘플 테스트 코드를 포함하고 있는 anyframe-transactiontest-src.zip 파일을 다운받은 후, 테스트 환경 설정 을 참조하여 위에서 제시한 예제 코드를 실행해 볼 수 있다.
    Name
    Download
    anyframe-transactiontest-src.zip
    Download