글쓴이 보관물: 낭창

Spring 에서 Transaction 설정 시 Service 레벨에서 적용 안되는 문제

요즘 뒤늦게 spring을 쓴다고 삽질에 여념이 없다=_=
어제는 Transaction 설정을 추가했는데, 이럴수가 적용이 안되는 것이다;;;
STS에서 transaction 표시도 잘 되고, 설정은 몇 번이나 검토해도 틀린게 없는데 말이다.

maven을 위해 pom.xml에 다음과 같이 추가해 주고

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.5.4</version>
</dependency>

다음과 같이 spring 에 data source, transaction manager 빈을 추가하고,
service 클래스에서 transaction 이 사용하도록 설정을 했다.

<!– data source –>
<bean id=”dataSource” class=”org.apache.commons.dbcp.BasicDataSource”>
<property name=”driverClassName” value=”net.sf.log4jdbc.DriverSpy” />
<property name=”url” value=”jdbc:log4jdbc:postgresql://192.168.0.12:5432/db” />
<property name=”username” value=”user” />
<property name=”password” value=”passwd” />
<property name=”initialSize” value=”3″ />
<property name=”maxActive” value=”10″ />
<property name=”maxWait” value=”3000″ />
</bean>

<!– transaction manager –>
<bean id=”transactionManager”
class=”org.springframework.jdbc.datasource.DataSourceTransactionManager”>
<property name=”dataSource” ref=”dataSource” />
</bean>

<!–  transaction setting –>
<tx:advice id=”txAdvice” transaction-manager=”transactionManager”>
<tx:attributes>
<tx:method name=”get*” read-only=”true” />
<tx:method name=”list*” read-only=”true” />
<tx:method name=”find*” read-only=”true” />
<tx:method name=”*” />
</tx:attributes>
</tx:advice>

<aop:config>
<aop:advisor advice-ref=”txAdvice”
                                     
pointcut=”execution(* com.acroem.hadpan.service.*Service.*(..))” />
</aop:config>

그리고, MyBatis와 연동해서 쓰는지라 다음과 같이 MyBatis 설정도 추가했다.

<bean id=”sqlSessionFactory” class=”org.mybatis.spring.SqlSessionFactoryBean”>
<property name=”dataSource” ref=”dataSource” />
<property name=”typeAliasesPackage” value=”z.y.x.bean” />
</bean>

<bean id=”sqlSession” class=”org.mybatis.spring.SqlSessionTemplate”>
<constructor-arg ref=”sqlSessionFactory” />
</bean>

<bean class=”org.mybatis.spring.mapper.MapperScannerConfigurer”>
<property name=”basePackage” value=”z.y.x.dao” />
</bean>

그러나 당최가 transaction이 동작을 하지 않는다=_= service 쪽의 매소드에 일부러 성공하는 SQL과 실패하는 SQL을
연속으로 실행 했는데, rollback 되지 않고 첫 번째 결과가 무조건 commit 이 되는 것이다;;
이래저래 설정을 바꿔보고, 이것 저것 실험을 해봐서 얻은 결론은 DAO 레벨에서는 transaction이 작동하는데,
service 레벨에서는 안한다는것.
삽질 끝에 service 클래스에서 @Service 어노테이션을 제거하고, 설정 파일에 bean 설정으로 추가 했더니..
동작한다! 먼가 해결의 실마리가 보이는 듯하지만 도대체가 이유를 알 수 없는 상황…-_-

다시 이래저래 설정을 바꿔가면서 실험해보고, 관련 글들을 찾아서 읽어 봤는데…
결론은 component scan의 과용과 spring context 설정에 대한 무지로 인한 설정 오류… 랄까..-_-a

STS에서 Spring MVC Template 프로젝트를 사용하여 프로젝트를 생성했는데, 이렇게 하면
기본적으로 계층 구조를 갖는 두 개의 context가 생성된다.
root context
 |- servlet context

그리고, root context의 설정 파일은 /WEB-INF/spring/root-context.xml 에,
servlet context 의 설정파일은 /WEB-INF/spring/appServlet/servlet-context.xml 에 저장된다.

참고로, root context의 빈은 servlet context 에서 참조 가능하나, 그 반대는 안되고,
root context와 servlet context에 동일한 빈이 있으면, root context의 빈은 무시될 수 있다고 한다.

여기서 servlet context 설정 파일에 <context:component-scan basePackage=”z.y.x” /> 를 넣은 것이 화근.
위의 data source, transaction, mybatis 관련 설정은 모두 root context의 설정에 추가 되어 있으며,

root context 설정 파일에도
<context:component-scan basePackage=”z.y.x” /> 가 추가되어있다.

여기서 문제는 AOP 설정은 다른 context에는 영향을 미칠 수 없다는 것과
root context와 servlet context에 동일한 빈이 있으면, root context의 빈은 무시될 수 있다는 것.

servlet context에서  <context:component-scan basePackage=”z.y.x” /> 을 해버렸기 때문에,

@Component, @Control, @Service, @Repository 가 붙은 모든 빈은 root context에서 무시될 가능성이 높다.
거기다 transaction 경계를 설정 하는 AOP 설정이 root context에 있으니…
root context 설정에서 component scan 했다고 하더라도 모조리 무시되니 root context 내에서는 AOP 설정이 안되고,
AOP 설정이 servlet context에는 적용되지 않으니, 결국 어디에도 transaction 설정이 되지 않는 것이지=_=

DAO 같은 경우에는 root context에 MyBatis 설정이 있으니, component scan에는 걸리지 않고
root context의 빈으로 등록되어 transaction이 잘 적용됐던 것이지. 덴장 OTL
망할놈의 STS는 context 간의 관계를 알리가 없으니 설정에 있으면 transaction 표시를 해줬던 것이고;

머.. 암튼 root context 설정에서는 요렇게,

<context:component-scan basePackage=”z.y.x.service” />

servlet context 설정에서는 요렇게,

<context:component-scan basePackage=”z.y.x.controller” />

해주는 것으로 모든 문제 해결!