Programmatic Transaction Management

본 문서에서는 세밀한 Transaction 제어가 필요한 경우 코드 내에서 직접적으로 Transaction을 처리하는 방법에 대해 살펴보기로 하자. Spring에서는 프로그램적 Transaction 관리를 위해 다음과 같이 2가지 방법을 제공한다.

TransactionTemplate을 이용한 Transaction 관리

Configuration

TransactionTemplate을 이용하여 Transaction을 관리하기 위해서는 Transaction 서비스와 TransactionTemplate에 대한 속성 정의가 필요하다. 다음은 TransactionTemplate에 대한 속성 정의 파일(applicationContext-transaction-template.xml )의 일부로 transactionManager property에 대한 정의를 필요로 한다.
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
	<property name="transactionManager" ref="oracleTransactionManagerDataSource"/>
</bean> 
다음은 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 서비스의 속성 정의시 본 매뉴얼 >> Tech. Service >> Transaction 을 참고하도록 한다.

Transaction 관리

TransactionTemplate을 이용하여 프로그램적인 방법으로 Transaction을 관리하고자 하는 경우, Transaction Context에 의해 호출될 callback 메소드를 정의하고 이 메소드 내에 비즈니스 로직을 구현해주면 된다.
this.transactionTemplate.execute(new TransactionCallbackWithoutResult() {                
  public void doInTransactionWithoutResult(TransactionStatus status) {                    
  //... biz. logic ...       
}});

this.transactionTemplate.execute(new TransactionCallback() {                
  public Object doInTransaction(TransactionStatus status) {                    
  //... biz. logic ...       
}});
callback 메소드 doInTransactionWithoutResult()는 전달할 값이 없는 경우에 정의 가능하며, 전달해야 하는 값이 존재하는 경우에는 doInTransaction()으로 정의하도록 한다. 또한, callback 메소드 내에서 입력 인자인 TransactionStatus 객체의 setRollbackOnly() 메소드를 호출함으로써 해당 Transaction을 rollback할 수 있다.

테스트 클래스 실행

테스트 클래스 UserServiceWithProgrammaticTest 는 동일한 User 정보를 이용하여, UserService의 addUser를 두번 호출한다. 두번째 호출시에 이미 등록된 User 정보이므로 EmpException이 발생하게 된다. 따라서 catch 블럭의 TransactionStatus를 이용하여 현재 Transaction에서 발생한 변경 사항이 rollback 처리 된다. 다음은 테스트 클래스 UserServiceWithProgrammaticTest의 testAddUserUsingTransactionTemplate 메소드의 로직이다.
public void testAddUserUsingTransactionTemplate() throws Exception {
	TransactionTemplate transactionTemplate = (TransactionTemplate) context
			.getBean("transactionTemplate");
	final UserService userService = (UserService) context
			.getBean(UserService.ROLE);

	transactionTemplate.execute(new TransactionCallbackWithoutResult() {
		public void doInTransactionWithoutResult(TransactionStatus status) {

			try {
				// 1. set user information
				UserVO userVO = new UserVO();
				userVO.setUserId("woos41");
				userVO.setUserName("gang");
				userVO.setPassword("gang");
				userVO.setRole("user");
				userVO.setSsn("1234567890");
				userVO.setSlYn("Y");
				userVO.setBirthDay("19750319");
				userVO.setAge(null);
				userVO.setCellPhone("1234567890");
				userVO.setAddr("kamala road");
				userVO.setEmail("ga@samsung.com");
				userVO.setEmailYn("y");
				userVO.setImageFile("ga");
				userVO.setRegDate(null);

				// 2. 사용자 등록 요청
				userService.addUser(userVO);
				// 3. 동일한 사용자 등록 요청
				userService.addUser(userVO);
			} catch (EmpException e) {
				// 4. 현재 Transaction에서 발생한 변경 사항 rollback 처리
				status.setRollbackOnly();
			}
		}
	});

	try {
		// 5. 사용자 등록 처리 rollback 여부 확인
		userService.getUser("woos41");
		// 6. rollback이 성공적으로 이루어진 경우 해당 사용자는 미등록 상태임
		throw new Exception("fail to transaction management.");
	} catch (EmpException e) {
		System.out.println("Successful!");
	}
}

TransactionManager를 직접 이용한 Transaction 관리

Configuration

다음은 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 서비스의 속성 정의시 본 매뉴얼 >> Tech. Service >> Transaction 을 참고하도록 한다.

Transaction 관리

Transaction 서비스를 직접 얻어온 후에 다음과 같이 try~catch 구문 내에서 Transaction 서비스를 이용하여, 적절히 begin, commit, rollback을 수행한다. 이 때, TransactionDefinition와 TransactionStatus 객체를 적절히 이용하면 된다.
...
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = transactionService.getTransaction(def);
  try {
    // ... biz logic ...
    transactionService.commit(status);
  }
  catch (Exception ex) {
    transactionService.rollback(status);
    throw ex;
  }
...

테스트 클래스 실행

테스트 클래스 UserServiceWithProgrammaticTest 는 동일한 User 정보를 이용하여, UserService의 addUser를 두번 호출한다. 두번째 호출시에 이미 등록된 User 정보이므로 EmpException이 발생하게 된다. 따라서 catch 블럭의 TransactionStatus를 이용하여 현재 Transaction에서 발생한 변경 사항이 rollback 처리 된다. 다음은 테스트 클래스 UserServiceWithProgrammaticTest의 testAddUserUsingTransactionManager 메소드의 로직이다.
public void testAddUserUsingTransactionManager() throws Exception {
	PlatformTransactionManager transactionService = (PlatformTransactionManager) context
			.getBean("oracleTransactionManagerDataSource");
	UserService userService = (UserService) context
			.getBean(UserService.ROLE);

	DefaultTransactionDefinition txDefinition = new DefaultTransactionDefinition();
	// 해당 Transaction을 위한 Propagation Behavior, Isolation Level 등 정의
	txDefinition
			.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
	TransactionStatus status = transactionService
			.getTransaction(txDefinition);

	try {
		// 1. set user information
		UserVO userVO = new UserVO();
		userVO.setUserId("woos41");
		userVO.setUserName("gang");
		userVO.setPassword("gang");
		userVO.setRole("user");
		userVO.setSsn("1234567890");
		userVO.setSlYn("Y");
		userVO.setBirthDay("19750319");
		userVO.setAge(null);
		userVO.setCellPhone("1234567890");
		userVO.setAddr("kamala road");
		userVO.setEmail("ga@samsung.com");
		userVO.setEmailYn("y");
		userVO.setImageFile("ga");
		userVO.setRegDate(null);

		// 2. 사용자 등록 요청
		userService.addUser(userVO);
		// 3. 동일한 사용자 등록 요청
		userService.addUser(userVO);
		
		// 4. 정상적으로 처리된 경우 현재 Transaction에서 발생한 변경 사항 commit 처리
		transactionService.commit(status);	
	} catch (EmpException e) {
		// 5. 현재 Transaction에서 발생한 변경 사항 rollback 처리
		transactionService.rollback(status);
		throw e;
	}

	try {
		// 6. 사용자 등록 처리 rollback 여부 확인
		userService.getUser("woos41");
		// 7. rollback이 성공적으로 이루어진 경우 해당 사용자는 미등록 상태임
		throw new Exception("fail to transaction management.");
	} catch (EmpException e) {
		System.out.println("Successful!");
	}
}

Resources

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