본문 바로가기

Programming/Spring

Spring - Transaction2 (선언적 트랜잭션 처리)

선언적 트랜잭션

Transaction Template와 달리 트랜잭션 처리를 코드에서 직접적으로 수행하지 않음

설정 파일이나 어노테이션을 이용하여 트랜잭션의 법위, 롤백 규칙 등을 정의


선언적 트랜잭션 정의 방식

1. <tx:advice> 태그를 이용하여 트랜잭션 처리

2. @Transaction 어노테이션을 이용한 트랜잭션 설정

3. TransactionProxyFactoryBean 태그를 이용한 트랜잭션 처리 

(2.0 이전 빈마다 프록시를 만들어서 사용하였으나 현재는 많이 사용되지 않는다.)


트랜잭션 특성

선언적 트랜잭션은 트랜잭션 특성으로 정의된다.

트랜잭션 특성이란?  특정 메소드에 어떻게 적용되어야 하는지에 대한 정책을 기술한 것

전파방식, 격리수준, 읽기전용힌트, 타임아웃, 롤백규칙으로 정의된다.


<tx:advice> 태그를 이용한 트랜잭션 처리

<tx:advice> 태그를 이용해서 트랜잭션 속성을 정의하기 위해서는 먼저 tx 네임스페이스를 추가.

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema.beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
        p:dataSource-ref="dataSource" />

    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="save*" propagation="REQUIRED" />

            <tx:method name="*" propagation="SUPPORTS" read-only="true" />
        </tx:attributes>
    </tx:advice>
    ...
 </beans>


tx네임스페이스를 추가하여 <tx:advice>태그, <tx:attributes>태그, <tx:method>태그 이용으로 트랜잭션 속성 정의


<tx:advice> 태그

트랜잭션을 적용할 때 사용될 Advisor를 생성

id속성 - 생성 될 트랜잭션 Advisor의 식별 값 입력

transaction-manager속성 - 스프링의 PlatformTransactionManager 빈을 설정


<tx:method> 태그

트랜잭션을 설정할 메소드 및 트랜잭션 속성을 설정

<tx:attributes>태그의 자식 태그로 설정

name 속성 

- 트랜잭션이 적용될 메소드의 이름을 명시.  

- "*"을 사용한 설정이 가능 ("get*" 으로 설정한 경우 이름이 get으로 시작하는 메소드을 의미)

propagation 속성 - 트랜잭션 전파 규칙을 설정

isolation 속성 - 트랜잭션 격리 레벨을 설정

read-only 속성 - 읽기 전용 여부를 설정

no-rollback-for 속성 - 트랜잭션을 롤백하지 않을 예외 타입을 설정

rollback-for 속성 - 트랜잭션을 롤백할 예외 타입을 설정

timeout 속성 - 트랜잭션의 타임 아웃 시간을 초 단위로 설정


트랜잭션 적용

<tx:advice> 태그는 Advisor만 생성하는 것일뿐, 실제로 트랜잭션을 적용하는 것은 아니다.

트랜잭션의 적용은 AOP를 통하여 이루어짐

 <aop:config>
    <aop:advisor 

        pointcut="execution(* *...service.*(..))"

        advice-ref="txAdvice" />
 </aop:config>

- pointcut 어트리뷰트는 AspectJ 포인트 컷 표현식을 이용하여 service의 모든 메소드를 어드바이스 해야 한다고 정의

- 어떤 트랜잭션 특성이 어떤 메소드에 적용되는지는 트랜잭션 어드바이스에 정의되어 있으며, 이는 advice-ref 어트리뷰트에 txAdvice라는 이름으로 참조하고 있다는 것을 의미한다.



1. 전파방식(propagation behavior)

- 호출되는 메소드와 클라이언트에 대하여 트랜잭션의 경계를 정의

  전파방식

 의미

  MANDATORY

 호출 전에 반드시 진행 중인 트랜잭션이 존재해야 한다.
 진행 중인 트랜잭션이 존재하지 않을 경우 예외 발생

  NESTED

 이미 진행 중인 트랜잭션이 존재하면 중첩된 트랜잭션에서 실행되어야 함을 나타낸다.

 중첩된 트랜잭션은 본 트랜잭션과 독립적으로 커밋되거나 롤백될 수 있다. 만약 본 트랜잭션이 없는 상황이라면 이는 PROPAGATION_REQUIRED와 동일하게 작동한다.

 그러나 이 전파방식은 벤더 의존적이며, 지원이 안되는 경우도 많다.

  NEVER

 트랜잭션 진행 상황에서 실행 될 수 없다.

 만약 이미 진행 중인 트랜잭션이 존재하면 예외 발생

  SUPPORTED

 트랜잭션이 없는 상황에서 실행

 만약 진행 중인 트랜잭션이 있다면 해당 메소드가 반환되기 전까지 잠시 중단한다.

  REQUIRED

 트랜잭션 상황에서 실행되어야 한다.

 진행 중인 트랜잭션이 있다면 이 트랜잭션에서 실행된다.

 없는 경우에는 트랜잭션이 새로 시작된다.

  REQUIRED_NEW

 자신만의 트랜잭션 상황에서 실행되어야 한다. 

 이미 진행 중인 트랜잭션이 있으면 그 트랜잭션은 해당 메소드가 반환되기 전에 잠시 중단된다.

  SUPPORTS

 진행 중인 트랜잭션이 없더라도 실행 가능하고, 트랜잭션이 있는 경우에는 이 트랜잭션 상황에서 실행된다.


2. 격리수준(isolation level)

- 한 트랜잭션이 동시 진행 중인 다른 트랜잭션의 영향을 얼마나 받는지 정의

  격리수준

  의미

  DEFAULT

  하부 데이터 저장소의 기본 격리 수준을 이용

  이는 데이터 저장소마다 다르다.

  READ_UNCOMMITTED

  커밋되지 않은 데이터 변경사항을 읽을 수 있다.
  더티읽기, 반복할 수 없는 읽기, 팬텀읽기를 허용

  READ_COMMITTED

  다른 트랜잭션에 커밋된 변경사항만 읽을 수 있다.
  더티읽기는 방지되지만, 반복할 수 없는 읽기, 팬텀읽기는 허용

  REPEATABLE_READ

  같은 데이터 필드는 여러 번 반복해서 읽더라도 동일한 값을 읽도록 한다.

  자신이 변경한 필드는 변경된 값을 읽는다.

  더티읽기와 반복할 수 없는 읽기는 방지되지만, 팬텀읽기는 허용

  SERIALIZABLE

  완전한 ACID를 보장하는 격리 수준
  보통 해당 트랜잭션에 관련된 모든 테이블을 잠그기 때문에 성능이 가장 비효율적인 격리 수준이다.


* 더티읽기 - 한 트랜잭션에서 다른 트랜잭션에 의해 변경될 수 있지만 아직 변경되지 않은 데이터를 읽게 되는 문제

   - 데이터가 커밋되지 않고 롤백되어 버린다면, 첫 번째 트랜잭션에서 읽은 이 데이터는 유효하지 않은 데이터가 된다.

* 반복할 수 없는 읽기 - 트랜잭션이 같은 질의를 두 번 이상 수행할 때 서로 다른 데이터를 얻게되는 문제

   - 각 질의 수행 사이에 동시 진행 중인 다른 트랜잭션에서 이 데이터를 변경하는 경우 발생

* 팬텀읽기 - 반복할 수 없는 읽기와 유사하지만 차이가 있따.

   - 팸텀읽기는 어떤 트랜잭션(T1)이 둘 이상의 데이터 행을 읽은 다음, 동시 진행 중인 다른 트랜잭션(T2)이 추가 행을

     삽입할 때 발생한다. T1에서의 동일한 질의를 다시 수행하면, T1은 이전에는 없던 데이터 행까지 읽게 된다.


3. 읽기전용

- 트랜잭션이 하부의 데이터 저장소에 대해 오직 읽기 작업만 수행하고, 수정등의 작업은 수행하지 않는 경우 사용.

- 읽기전용으로 설정 할 경우 데이터 저장소에서는 트랜잭션의 읽기 전용 특성을 활용한 몇 가지의 최적화를 수행 할 여지가 생긴다. 따라서 트랜잭션을 읽기 전용으로 선언함으로써 데이터 저장소에게 최적화가 적절하다고 판단하고 최적화를 적용할 수 있는 기회를 줄 수 있다.

<tx:advice id="txAdvice" transaction-manager="transactionManager">

<tx:attributes>

<tx:method name="get*" propagation="REQUIRES" read-only="true" />

</tx:attributes>

</tx:advice>

데이터 저장소는 트랜잭션이 시작할 때 읽기 전용 최적화를 수행하므로 새로운 트랜잭션을 시작 할 수 있는 전파방식(REQUIRES, REQUIRES_NEW, NESTED)을 가진 메소드에 선언해야 의미가 있다.


4. 타임아웃

- 트랜잭션이 일정 시간이 지나버리면 롤백하도록 선언하기 위해 사용

- 새로운 트랜잭션을 시작 할 수 있는 전파방식(REQUIRES, REQUIRES_NEW, NESTED)을 가진 메소드에 선언해야 의미가 있다.


5. 롤백규칙

어떤 예외가 발생하였을 때 롤백하고, 어떤 경우에는 롤백하지 않는지 정의

기본적으로 런타임 예외(Runtime Exception)에 대해서는 롤백하고, 검사형 예외(Checked Exception)에 대해서는 롤백하지 않는다.

롤백규칙을 지정해 놓으면 검사형 예외(Checked Exception)에 대해서도 트랜잭션 롤백 처리를 할 수 있다.


rollback-for 속성 - 예외 발생 시 롤백 작업을 수행할 예외 타입을 설정

no-rollback-for 속성 - 예외가 발생하더라도 롤백을 하지 않는 예외 타입을 설정


명시할 예외 타입이 한 개 이상인 경우 각각의 예외는 콤마로 구분

예외 클래스는 완전한 이름을 입력하거나, 패키지 이름을 제외한 클래스 이름만 입력 가능

<tx:method name="get*" read-only="true" rollback-for="Exception"/>

<tx:method name="get*" read-only="true" no-rollback-for="Exception"/>

rollback-for와 no-rollback-for를 동시에 정의하면 가장 상위의 정의가 적용된다.



어노테이션 기반 트랜잭션 설정

@Transactional 어노테이션 사용

메소드나 클래스에 적용

관련 트랜잭션 속성을 설정


 import org.springframework.transaction.annotation.Propagation;
 import org.springframework.transaction.annotation.Transactional;

 public class PlaceOrderServiceAnnotationImpl implements PlaceOrderService {
    @Transactional(propagation=Propagation.REQUIRED, readOnly=true)
    public PurchaseOrderResult order(PurchaseOrderRequest orderRequest)
        throws ItemNotFoundException {

        Item item = itemDao.findById(orderRequest.getItemId());

        if(item == null) {
            throw new ItemNotFoundException(orderRequest.getItemId()):
        }

        PaymentInfo paymentInfo = new PaymentInfo(item.getPrice());
        paymentInfoDao.insert(paymentInfo);
        ...
    }
 }


@Transaction 어노테이션 속성

 속성

 설

 propagation

 트랜잭션 전파 규칙 설정, Propagation 열거형 타입에 값이 정의됨

 기본 값 - Propagation.REQUIRED

 isolation

 트랜잭션 격릴 레벨 설정

 readOnly

 읽기 전용 여부 설정

 rollbackFor

 트랜잭션을 롤백할 예외 타입 설정 ex)rollbackFor={Exception.class}

 noRollbackFor

 트랜잭션을 롤백하지 않을 예외 타입 설정 ex)noRollbackFor={Exception.class}

 timeout

 트랜잭션의 타임아웃을 초단위로 설정


@Transactional 어노테이션이 적용된 스프링 빈에 트랜잭션 적용

<tx:annotation-driven>태그를 설정

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

 <bean id="placeOrderService"
    class="kame.spring.store.domain.PlaceOrderServiceAnnotationImpl"
    ... />


<tx:annoation-driven> 태그 대신 PersistenceAnnotationBeanPostProcessor 클래스를 빈으로 등록해도 된다.

 <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />



참조 : 

http://blog.naver.com/PostView.nhn?blogId=chocolleto&logNo=30087623408&parentCategoryNo=&categoryNo=&viewDate=&isShowPopularPosts=false&from=postView

http://tedwon.com/display/dev/Spring+Transactions