Spring Transaction - 선언적으로 처리

2010. 8. 22. 20:05plming/Java - Spring

- 선언적으로 트랜잭션 처리
  빈 설정 파일에서 선언적으로 트랜잭션을 처리. 비즈니스 계층의 소스 코드를 변경하지 않고, 설정 파일의 변경만으로 트랜잭션 기능을 추가, 변경하는 것이 가능하다.
  다양한 리소스의 트랜잭션을 처리할 필요가 있거나, 처리해야 할 트랜잭션이 많은 경우와 트랜잭션 정책이 바뀌거나 개발 중에 트랜잭션 속성을 변경할 필요가 있을 때 빈 설정 파일만 변경해서 사용할 수 있다. 
이를 프로그램적으로 처리하기 위해서는 상당히 많은 시간이 필요하며, 소스 코드를 직접 수정해야 하는 단점들이 있다.

 - applicationContext-jdbc.xml

 <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
     <property name="dataSource" ref="dataSource" />
 </bean>

 <bean id="txProxyTemplate" abstract="true" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
     <property name="transactionManager">
         <ref bean="transactionManager" />
     </property>
     <property name="preInterceptors">
         <list>
             <ref bean="loggingAdvice"/>
             <ref bean="emailNotificationThrowsAdvice"/>
         </list>
     </property>
     <property name="transactionAttributes">
         <props>
             <prop key="add*">PROPAGATION_REQUIRED</prop>
             <prop key="update*">PROPAGATION_REQUIRED</prop>
             <prop key="remove*">PROPAGATION_REQUIRED</prop>
             <prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
         </props>
     </property>
 </bean>


  TransactionProxyFactoryBean은 트랜잭션을 처리하기 위하여 필요한 속성들을 정의하는 것이 가능할 뿐만 아니라 트랜잭션 외에 추가적인 Aspect를 추가하는 것도 가능하다.

위의 txProxyTemplate 빈은 abstract="true"로 설정되어 직접적으로 사용할 수 없으며,
실질적으로 트랜잭션을 처리하는 컴포넌트들에서 이를 사용한다.

 - applicationContext-user.xml

 <bean id="userServiceTarget" class="net.javajigi.user.service.UserServiceImpl">
     <property name="userDAO">
         <ref local="userDAO" />
     </property>
 </bean>

 <bean id="userService" parent="txProxyTemplate">
     <property name="target">
         <ref local="userServiceTarget" />
     </property>
 </bean>


- applicationContext-board.xml

<bean id="boardServiceTarget" class="net.javajigi.board.service.BoardServiceImpl">
    <property name="boardDAO">
        <ref local="boardDAO" />
    </property>
    <property name="boardFileDAO">
        <ref local="boardFileDAO" />
    </property>
</bean>

<bean id="boardService" parent="txProxyTemplate">
    <property name="target">
        <ref local="boardServiceTarget" />
    </property>
    <property name="transactionAttributes">
        <props>
            <prop key="add*">PROPAGATION_REQUIRED</prop>
            <prop key="update*">PROPAGATION_REQUIRED</prop>
            <prop key="remove*">PROPAGATION_REQUIRED</prop>
            <prop key="findBoardWithView">PROPAGATION_REQUIRED</prop>
            <prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
        </props>
    </property>
</bean>


findBoardWithView 메소드의 경우, readOnly 속성을 적용하지 않도록 설정하기 위해 재정의하고 있다.

※ 트랜잭션의 속성을 정의하는 방법

 PROPAGATION, ISOLATION, readOnly, -Exceptions, +Exceptions
           ①                 ②              ③                       ④

 ① 전달 행위 (필수) : 필수 값으로서 Spring 프레임워크 전달행위 중의 하나를 사용할 수 있다.
 ② 격리 레벨 (선택) : 선택 값으로 Spring 프레임워크 격리 레벨 중의 하나를 사용할 수 있다.
 ③ readOnly 유무 (선택) : 선택 값으로 실행하는 트랜잭션이 읽기 전용일 경우에 사용가능하다. 일반적으로 트랜잭션 내에서 SELECT 쿼리만을 실행하는 경우에 이 속성을 사용한다.
 ④ Rollback 규칙 (선택) : Spring 프레임워크 트랜잭션의 디폴트 설정은 RuntimeException이 발생할 경우에는 Rollback, CheckedException이 발생하는 경우에는 Commit되도록 하고 있다. 그러나 트랜잭션의 속성을 지정할 때 Rollback 규칙을 이용하여 디폴트 설정을 변경하는 것이 가능하다. [그림 5-2]의 Rollback 규칙에서 마이너스(-)로 시작하는 Exception에 대해서는 무조건 Rollback, 플러스(+)로 시작하는 Exception은 무조건 Commit되도록 규칙을 변경할 수 있다.



빈의 숫자가 수백개에 이르는 경우에는 위와 같은 정의 작업 또한 적용하기 쉽지않다.
이 경우에 Spring AOP의 Autoproxying 기능을 사용할 수 있다.

 - applicationContext-transaction.xml

 <bean id="nameMatchAS" class="org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource">
     <property name="properties">
         <props>
             <prop key="add*">PROPAGATION_REQUIRED</prop>
             <prop key="update*">PROPAGATION_REQUIRED</prop>
             <prop key="remove*">PROPAGATION_REQUIRED</prop>
             <prop key="findBoardWithView">PROPAGATION_REQUIRED</prop>
             <prop key="*">PROPAGATION_REQUIRED,readOnly</prop>

         </props>
     </property>
 </bean>

 <bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
     <property name="transactionManager">
         <ref bean="transactionManager" />
     </property>
     <property name="transactionAttributeSource">
         <ref bean="nameMatchAS" />
     </property>
 </bean>

 <bean id="autoProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
     <property name="interceptorNames">
         <list>
             <idref bean="loggingAdvice"/>
             <idref bean="emailNotificationThrowsAdvice"/>
             <idref local="transactionInterceptor" />
         </list>
     </property>
     <property name="beanNames">
         <list>
             <idref bean="boardService" />
             <idref bean="userService" />
         </list>
     </property>
 </bean>


새로운 모듈이 추가될 경우, "autoProxyCreator"에 추가된 모듈의 비즈니스 계층을 담당하고 있는 POJO빈의 ID만 추가해주면 된다.  이처럼 일괄적용하기 위해서는 애플리케이션의 메소드 이름의 명명규칙이 존재해야 가능하다.


[출처] Spring 프레임워크 워크북

'plming > Java - Spring' 카테고리의 다른 글

Spring MVC 기본  (0) 2010.08.27
모델 1 개발 vs 모델 2 개발  (0) 2010.08.26
Spring Transaction - 프로그램에서 처리  (1) 2010.08.22
Spring Transaction  (0) 2010.08.22
Spring - 빈 설정 파일 관리  (0) 2010.08.22