본문 바로가기
Programming/>> Spring

[Spring] Spring Transaction 설정

by 니키ᕕ( ᐛ )ᕗ 2016. 3. 2.

DB를 다룰려면 트랜잭션이 매우 중요하다. 개념적으로는 중요성을 이해하고 있지만 실제 업무에서 트랜잭션을 관리해본적이 없다... 

그래서 Transaction 설정방법과 propagation에 대해서만 작성함...

 

우선 context에 transactionManager빈을 추가한다.

 

DataSourceTransactionManager는 Connection의 트랜잭션 API를 이용해서 트랜잭션을 관리해주는 트랜잭션 매니저이고 JDBC API를 이용하여 트랜잭션을 관리하는 데이터 액세스 기술인 JDBC와 Mybatis에 적용가능하다.

 

 

1. Annotation을 이용한 트랜잭션 

 

<tx:annotation-driven transaction-manager="txManager"/>

 

context에 요 한줄을 추가한다.

 

@Transactional
public interface MemberService {
	
	public List<Map<Integer, String>> getInterests();
	public User selectUser(String userid);
	@Transactional
	public Integer insertUser(User tbUser);
}
	
@Service
@Transactional
public class MemberServiceImpl implements MemberService {

	@Autowired
	MemberDao dao;
	
	public List<Map<Integer, String>> getInterests() {
		return dao.getInterests();
	}

	public User selectUser(String userid) {
		return dao.getUser(userid);
	}
	
	@Transactional
	public Integer insertUser(User user) {
		System.out.println("---- insertUser ----");
		user.setUserno(dao.insertUser(user));
		return dao.insertUserInterest(user);
	}

}

 

사용은 간단하다. 트랜잭션을 적용할 클래스, 인터페이스, 메소드에 @Transaction 어노테이션을 선언한다. 

트랜잭션의 적용 우선 순위는 클래스의 메소드 > 클래스 > 인터페이스의 메소드 > 인터페이스 순이다.

 

 

2. <tx:advice> 태그를 이용한 트랜잭션

이 방법은 AOP를 사용하므로 일단 AOP부터 정리하자면...

 AOP(Aspect Oriented Programming)

- 기능을 핵심 비지니스 로직과 공통 모듈로 구분하여 핵심 로직에 영향을 미치지 않고 로직 사이에 공통 모듈을 효과적으로 잘 끼워넣도록 하는 개발 방법이다. 보안, 로깅 등의 공통 모듈을 만든 후에 코드 밖에서 이 모듈을 비지니스 로직에 삽입하는 것이고 코드 밖에서 설정되는 것이 핵심이다.

 

 AOP 요소

- Advice : 어떤 기능을 어디에 적용할 것인지를 정의함. jointpoint에 삽입되어 동작되는 코드로 before(실행 전)/after return(성공적 실행 후)/ throws(예외 발생)/around(실행하기 전, 후) 4개의 시점 지정 가능.

- Joinpoint : advice를 적용할 수 있는 곳 혹은 적용할수 있는 지점(메소드 호출, 예외발생, 필드값수정) 스프링에선 메소드만 가능.

  - Pointcut : 어디에 공통 모듈 기능을 삽입할 것인지. 어떤 클래스의 어떤 조인포인트를 사용할 것인지 결정. pointcut의 표현식 참조.

  - Target : 어드바이스가 적용되는 객체.

  - Weaving : 타겟 객체에 적용되어 새로운 프록시 객체를 생성하는 절차.

  - Aspect(Advisor) : Pointcut과 Advice를 하나로 묶은 것

 

 

- context.xml 파일의 namespace에 tx, aop 추가

 

    <tx:advice id="txAdvice" transaction-manager="txManager">
    	<tx:attributes>
    		<tx:method name="*"/>
    	</tx:attributes>
    </tx:advice>

- <tx:advice> 태그에 transaction-manager 속성을 트랜잭션을 적용할 transactionManager 빈으로 설정한다. 여기서 설정된 id는 Advisor를 생성할 때 쓰인다.

- <tx:method>는 트랜잭션을 적용할 메소드 및 속성을 설정하는 것으로 name속성은 어노테이션 방법에서 어떤 메소드 or 클래스에 어노테이션을 달것이냐와 같은 것으로 볼 수 있다. 세부 속성은 어노테이션방식도 같은 것을 사용하기 때문에 밑에서 추가하겠다.

 

    <aop:config>
		<aop:pointcut id="txPointcut" expression="execution(* spring.sample.service.*.*(..))" />    
		<aop:advisor pointcut-ref="txPointcut" advice-ref="txAdvice"/>
    </aop:config>

- Pointcut을 정의하여 txPointcut이라 명명했다. 포인트컷은 service, controller, dao 어디든 설정 가능하다. 난 비즈니스 로직인 서비스에 적용했다.

- 트랜잭션 어드바이스와 포인트컷을 참조하여 Aspect를 생성함.

 

 

 

- 트랜잭션 설정 속성은 다음이 있다. 참고 - http://wiki.gurubee.net/pages/viewpage.action?pageId=26741432

속성 설 명

isolation

트랜잭션 격리수준은 동시에 여러 트랜잭션이 진행될 때에 트랜잭션의 작업 결과를 여타 트랜잭션에게 어떻게 노출할 것인지를 결정하는 기준이며 스프링은 다음 다섯 가지 격리수준 속성을 지원. 별도로 정의하지 않으면 DB의 Isolation Level을 따름.

propagation

트랜잭션 전파규칙을 정의. 동시에 여러 트랜잭션이 진행될 때에 트랜잭션의 작업 결과를 여타 트랜잭션에게 어떻게 노출할 것인지를 결정하는 기준

readOnly
read-only

해당 Transaction을 읽기 전용 모드로 처리 (Default = false). 성능을 최적화하기 위해 사용할 수도 있고 특정 트랜잭션 작업 안에서 쓰기 작업이 일어나는 것을 의도적으로 방지하기 위해 사용하기도 함. 트랜잭션을 준비하면서 읽기 전용 속성이 트랜잭션 매니저에게 전달되고 그에 따라 트랜잭션 매니저가 적절한 작업을 수행함. 일부 트랜잭션 매니저의 경우 읽기전용 속성을 무시하고 쓰기 작업을 허용할 수도 있기 때문에 주일반적으로 읽기 전용 트랜잭션이 시작된 이후 INSERT, UPDATE, DELETE 같은 쓰기 작업이 진행되면 예외가 발생한다. 

rollbackFor

rollback-for

rollbackForClassName

정의된 Exception에 대해서는 rollback을 수행함.

noRollbackFor
no-Rollback-for
noRollbackForClassName

정의된 Exception에 대해서는 rollback을 수행하지 않음.

timeout

지정한 시간 내에 해당 메소드 수행이 완료되지 않은 경우 rollback 수행. 값은 초 단위로 지정하며 트랜잭션 제한시간을 직접 지정하는 경우 이 기능을 지원하지 못하는 일부 트랜잭션 매니저는 예외를 발생시킬 수 있음.
-1일 경우 no timeout (Default = -1)

-- isolation

 DEFAULT

 사용하는 데이터 액세스 기술 또는 DB 드라이버의 디폴트 설정을 따름. 보통 드라이버의 격리수준은 DB의 격리수준을 따르는게 일반적이고 대부분의 DB는 READ_COMMITTED를 기본 격리수준으로 갖음. 일부 DB는 디폴트 값이 다른 경우도 있으므로 DEFAULT를 사용할 경우에는 드라이버와 DB의 문서를 참고해서 디폴트 격리수준 확인이 필요.

 READ_UNCOMMITTED

 가장 낮은 격리수준. 하나의 트랜잭션이 커밋되기 전에 그 변화가 다른 트랜잭션에 그대로 노출되는 문제 가있지만 가장 빠르기 때문에 데이터의 일관성이 조금 떨어지더라도 성능을 극대화할 때 의도적으로 사용하기도 함.

 READ_COMMITTED

 실제로 가장 많이 사용되는 격리수준. Spring에서는 DEFAULT로 설정해둬도 DB의 기본 격리수준을 따라서 READ_COMMITTED로 동작하는 경우가 대부분이므로 명시적으로 설정하지 않기도 함. READ_UNCOMMITTED와 달리 다른 트랜잭션이 커밋하지 않은 정보는 읽을 수 없고 대신 하나의 트랜잭션이 읽은 로우를 다른 트랜잭션이 수정할 수 있고 이 때문에 처음 트랜잭션이 같은 로우를 읽을 경우 다른 내용이 발견될 수 있음.

 REPEATABLE_READ

 하나의 트랜잭션이 읽은 로우를 다른 트랜잭션이 수정하는 것을 막지만 새로운 로우를 추가하는 것은 제한하지 않음. 따라서 SELECT로 조건에 맞는 로우를 전부 가져오는 경우 트랜잭션이 끝나기 전에 추가된 로우가 발견될 수 있음.

 SERIALIZABLE

 가장 강력한 트랜잭션 격리수준. 이름 그대로 트랜잭션을 순차적으로 진행시켜 주기 때문에 여러 트랜잭션이 동시에 같은 테이블의 정보를 액세스하지 못함. 가장 안전한 격리수준이지만 가장 성능이 떨어지기 때문에 극단적인 안전한 작업이 필요한 경우가 아니라면 자주 사용되지 않음.

 

-- propagation

 REQUIRED

 디폴트 속성으로 모든 트랜잭션 매니저가 지원하며, 대개 이속성이면 충분. 미리 시작된 트랜잭션이 있으면 참여하고 없으면 새로 시작. 자연스럽고 간단한 트랜잭션 전파 방식이지만 사용해보면 매우 강력하고 유용하며 하나의 트랜잭션이 시작된 후에 다른 트랜잭션 경계가 설정된 메소드를 호출하면 자연스럽게 같은 트랜잭션으로 묶인다.

 SUPPORTS

 이미 시작된 트랜잭션이 있으면 참여하고 그렇지 않으면 트랜잭션 없이 진행. 트랜잭션이 없긴 하지만 해당 경계 안에서 Connection이나 하이버네이트 Session 등을 공유할 수 있음.

 MANDATORY

 REQUIRED와 비슷하게 이미 시작된 트랜잭션이 있으면 참여하는 반면에 트랜잭션이 시작된 것이 없으면 새로 시작하는 대신 예외를 발생시킨다. 혼자서는 독립적으로 트랜잭션을 진행하면 안 되는 경우에 사용.

 REQUIRES_NEW

 항상 새로운 트랜잭션을 시작. 이미 진행 중인 트랜잭션이 있으면 트랜잭션을 잠시 보류함.

 NOT_SUPPORTED

 트랜잭션을 사용하지 않게 함. 이미 진행 중인 트랜잭션이 있으면 보류시킴.

 NEVER

 트랜잭션을 사용하지 않도록 강제함. 이미 진행 중인 트랜잭션도 존재하면 안되며 있다면 예외를 발생시킴.

 NESTED

 이미 진행중인 트랜잭션이 있으면 중첩 트랜잭션을 시작하고. 중첩 트랜잭션은 트랜잭션 안에 다시 트랜잭션을 만듦. 하지만 독립적인 트랜잭션을 만드는 REQUIRES_NEW와는 다름

propagation에 대한 도식화된 설명 - http://www.nextree.co.kr/p3180/

 

 

참고

신관영님 블로그 - http://springsource.tistory.com/

1. 개요

2. 추상화와 동기화

3. 트랜잭션 추상화 클래스의 종류와 사용법

4. 트랜잭션 경계 설정 전략 - 개요

5. 코드에 의한 트랜잭션 경계 설정

6. aop 와 tx 네임스페이스를 사용한 설정

7. @Transactional 을 사용한 설정

8. 마치며

댓글