본문 바로가기

프로그래밍/Spring & MyBatis

[Spring Framework] @Transactional 트랜젝션 관리

출처 : http://www.mungchung.com/


Spring에서 트랜잭션을 관리하는 방법은 Spring의 특징처럼 여러가지 방법들이 많다.

간략하게 정리하자면,

 

1. JDBC의 커밋, 롤백을 이용한 트랜잭션 처리 방법으로 가장 전통적인 방식이다.

   단점은 특정 JDBC에 종속적이고 비슷한코드가 클래스마다 여기저기 반복된다.

 

2. Transaction Manager 을 이용한 방법

 

3. Transaction Template를 이용한 방법

 

4. AOP를 이용한 선언적방식의 트랜잭션 처리 방법

 

5. @Transactional 어노테이션을 이용한 방법

 

 

빈설정 파일을 통해서 트랜잭션을 선언하려면 AOP의 포인트컷, 어드바이스 같은 개념을 잘 알고 있어야하는데 개념도 쉽지 않을 뿐더러 적용하기도 번거러울것 같아서 가장 간단한 방법 같아 보이는 @Transactional 으로 트랜잭션 구현해봤다.

 

@Transactional을 이용한 한 방법은 설정이 간단하다.

1. <tx:annotation-driven> 엘리먼트를 활성화해서 트랜잭션을 손쉽게 선언할수 있도록 해준다

2. 트랜잭션을 적용할 인터페이스 or 클래스 or 메서드에 @Transactional 어노테이션을 선언해준다.

 

 

설정방법은 무지 간단하지만 실제 예제 만들고 사용하려면 생각보다 쉽지 않다.

@Transactional 으로 트랜잭션을 구현할때 주의할 점이 있으니 잘 안될 경우 아래 내용들 확인해본다.

경험상 적은것들도 있는데 실력이 부족해서 틀렸을수도 있다 -_-

 

1. 자바 1.5 이상에서만 동작

2. AOP가 프록시를 기반으로 하는 한계 때문에 public 메서드에만 어노테이션을 적용가능

    (꼼수로 회피 방법이 있는것 같기도 함)

3. 가능하면 @Controller 클래스에 @Transactional을 사용하지 말자. spring 기본 사상에 위배될 뿐만 아니라

    잘 안된다. -_- 별도의 트랜잭션처리 클래스를 만들어서 @Controller에서 그 클래스를 호출하는 방식으로 바꾼다.

4. 트랜잭션 사용한 클래스는 인터페이스를 만들어주자


그래도 롤백이 되지 않은 경우

Spring의 트랜잭션처리를 공부하려고 샘플 프로젝트 만들고 테스트 하는데 이상하게 @Transactional 어노테이션을 사용하면 rollback이 안된다. 몇 시간 동안 삽질한 끝에 결국 원인을 찾아냈다.
같은 오류로 어려움을 겪고 있다면 아래 해결방법들을 이용해보면 된다.

 


1. mysql 데이터베이스를 사용할경우 테이블 타입 확인
mysql은 테이블타입이 InnoDB일때만 트랜잭션이 동작한다고한다. 테이블 타입을 확인해보고 InnoDB로 변경해준다.

 

 

 

2. 인터페이스 코드 유무 확인
트랜잭션을 이용할 클래스들이 인터페이스를 사용하는지 확인해봐야한다.
@Transactional 어노테이션 같은경우 Spring AOP를 이용하게 되는데 이 AOP는 기본적으로 Dynamic Proxy를 이용한다.
Dynamic Proxy는 인터페이스 기반으로 동작하기 때문에 인터페이스가 없을경우 트랜잭션이 동작하지 않는다.

 

내가 테스트한 코드들이 인터페이스가 없고 클래스만 만들었었는데 이 이유 때문에 오류가 발생했던것이다.

 

인터페이스 없이 트랜잭션 동작하게 하려면  CGLib(Code Generation Library) Proxy를 이용하면 된다.
CGLib Proxy는 클래스에 대한 Proxy가 가능하기 때문에 인터페이스가 없어도 된다.
CGLib Proxy를 이용하는 방법은 다음과 같다.

 

1) maven에 cglib 추가

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.2</version>
    <type>jar</type>
    <scope>compile</scope>
</dependency>

 

 

2) servlet context에 proxy-target-class 속성 추가

 이렇게 설정해주고 transaction 테스트 하니 내 경우 잘 동작했다.

 

※ 만일 cglib 라이브러리를 추가 안하고 proxy-target-class 속성을 이용하게 되면 아래와 같은 오류메시지가 나타난다

Initialization of bean failed; nested exception is org.springframework.aop.framework.AopConfigException: Cannot proxy target class because CGLIB2 is not available. Add CGLIB to the class path or specify proxy interfaces.

 

 

3. application context 설정 파일에 transaction 관련 설정있는지 확인

DB설정 부분 application context 쪽에 할 경우, 이 DB설정은 servlet context쪽에 설정된 Bean들에는 적용이 안된다.

그래서 transaction관련 설정을 servlet context에 해줘야한다.

아래 구문이 application context에 있다면 servlet context 로 옮겨준다.

<tx:annotation-driven proxy-target-class="true"/> 

 

마지막으로, JDK Dynamic Proxy와 CGLIB Proxy에 대한 자세한 내용을 알고 싶다면 http://wiki.javajigi.net/pages/viewpage.action?pageId=1065 참고하면 된다.