2011. 12. 8. 11:06


기존 시스템은 Spring에 Hibernate로 되어있어 Spring에서 제공하는 TransactionProxyFactoryBean을 사용한

선언적 Transaction방법을 이용하고 있었다.

 <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
  <property name="driverClassName">
   <value>oracle.jdbc.driver.OracleDriver</value>
  </property>

  <property name="url">
   <value>jdbc:oracle:thin:@ip주소:포트:sid</value>
  </property>

  <property name="username">
   <value>계정명</value>
  </property>

  <property name="password">
   <value>비밀번호</value>
  </property>

 </bean>

 <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
         <property name="dataSource" ref="dataSource"/>
         <property name="mappingResources">
   <list>
    <value>경로/Test.hbm.xml</value>
   </list>
  </property>

  <property name="hibernateProperties">
      <props>
   <prop key="hibernate.dialect">org.hibernate.dialect.Oracle9Dialect</prop>
   <prop key="hbm2ddl.auto">update</prop>
   <prop key="hibernate.generate_statistics">true</prop>
   <prop key="hibernate.connection.autocommit">true</prop>
   <prop key="hibernate.show_sql">true</prop>
   <prop key="hibernate.format_sql">true</prop>
   <prop key="hibernate.connection.pool_size">25</prop>
   <prop key="hibernate.c3p0.min_size">10</prop>
   <prop key="hibernate.c3p0.max_size">25</prop>
   <prop key="hibernate.c3p0.timeout">1800</prop>
   <prop key="hibernate.c3p0.max_statements">50</prop>
   <!--
   <prop key="hibernate.cache.use_query_cache">true</prop>
   <prop key="hibernate.cache.use_second_level_cache">true</prop>
   <prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop>
   -->          
      </props>
  </property>
 </bean>

 <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
  <property name="sessionFactory" ref="sessionFactory" />
 </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"/>
   </list>
  </property>

  <property name="transactionAttributes">
   <props>
    <prop key="add*">PROPAGATION_REQUIRED</prop>
    <prop key="modify*">PROPAGATION_REQUIRED</prop>
    <prop key="remove*">PROPAGATION_REQUIRED</prop>
    <prop key="login">PROPAGATION_REQUIRED</prop>
    <prop key="findAndHistory">PROPAGATION_REQUIRED</prop>
    <prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
   </props>
  </property>
 </bean>


허나 본인이 신규 프로젝트를 하며 Spring & iBatis로 세팅을하며 위와 같이 세팅할 시 Service 빈즈에 계속하여 parent로

txProxyTemplate 를 추가해줘야되는 단점이 있었다.

뭐 해놓고 보자면 어짜피 지금 언급할 내용도 추가하긴 마찬가지지만 언제부턴가 각각 모듈별로 가지런히 정리해놓는

인터페이스 설계만을 놓고 보자면 이게 더 가독성이 좋지 않을까 싶다.

Spring AOP의 Autoproxying 기능을 사용하여 작성한 방법이다.

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

 <bean id="nameMatchAS" class="org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource">
  <property name="properties">
   <props>
    <prop key="add*">PROPAGATION_REQUIRED</prop>
    <prop key="modify*">PROPAGATION_REQUIRED</prop>
    <prop key="remove*">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 local="transactionInterceptor" />
   </list>
  </property>
  <property name="beanNames">
   <list>
    <idref bean="writingService" />
   </list>
  </property>
 </bean>

<idref bean="writingService" /> 에다가 해당 Service 빈만 잘 걸어주면 된다.

그리고 autoProxy로 빈을 생성할 경우

org.springframework.aop.framework.AopConfigException: Cannot proxy target class because CGLIB2 is not available. Add CGLIB to the class path or specify proxy interfaces.

이런식으로 에러가 발생한다. (초기)

인터페이스에서의 proxy 생성과 cglib를 이용한 proxy 생성 둘다 없으므로 뱉어낸 에러다.

이부분은 cglib2 라이브러리를 넣어주면 정상적으로 작동한다.(cglib-nodep-2.1_3.jar)

cblib site : http://cglib.sourceforge.net/

기타 설명과 더욱 상세한 설명은 아래를 참조
출처 : http://lilymate.tistory.com/222
         http://javacan.tistory.com/entry/114


** 추가

위와같이

<prop key="add*">PROPAGATION_REQUIRED</prop>

를 처리하고도 예외처리가 되지 않는 경우가 있는데 이는 checked exception의 경우 롤백되지 않는다.

(예외 발생시 RuntimeException를 상속 받은, 즉 unchecked exception은 자동 롤백처리가 된다.)

그러므로 checked exception은 반드시 수동으로 예외처리에서 롤백을 해줘야 하는데 책을 찾거나 웹을 찾아보면 대부분

AOP 단에서의 tx태그를 사용한 처리방법만 무성하다.

크게보면 이 방법도 2가지로 나뉘는데 하나는 선언적 방법, 두번째는 명시적 방법이다.

(선언적방법을 추천한다.)

<prop key="add*">PROPAGATION_REQUIRED</prop>

이부분에서 옵션을 , 로 더 줄 수 있는데 '+'와 '-'옵션(롤백 규칙)이 있다.

'+'옵션은 해당 예외가 발생하더라도  커밋을 수행하고 '-'를 붙이면 해당 예외가 발생하면 롤백을 수행한다.

<prop key="add*">PROPAGATION_REQUIRED,-Exception</prop>

두번째 방법은 해당 메소드 내에서 명시해주는 방법니다.

public void addTest() {
    try {
        // some business logic...
    } catch (Exception e) {
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    }
}

이런식으로 직접 명시해준다. (추천하지 않음 허나 경우에 따라선 어떤 일이 있을지 모르므로 가볍게 사용할 수 있을 듯 싶다)

아래의 사이트를 통해 tx태그, 어노테이션 사용 시 처리방법을 살펴보면 될 것 같다.

참고 : http://blog.naver.com/chocolleto/30087623408

Posted by silver0r