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