반응형

Spring4 + myBatis 환경에서 트랜잭션 설정하기.

트랜잭션 위주로 정리해 봅니다. mybatis 는 뭐 정보가 많으니 패스하겠습니다.


1. 기본 설정.

transaction 범위와 관련하여, dispatch-servlet 에서와  applicaitonContext.xml 에서의  component-scan 설정을 수정해 줘야 합니다.

mvc-config.xml

<context:component-scan base-package="com.slowstarter" use-default-filters="false">

<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />

</context:component-scan>


applicationContext.xml

<context:component-scan base-package="com. slowstarter">

<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />

</context:component-scan>


poll(dbcp2, c3po), mybatis 등 필요한 dependency 설정들 추가하기. 

<dependency>

<groupId>org.mybatis</groupId>

<artifactId>mybatis</artifactId>

<version>3.4.0</version>

</dependency>


<dependency>

<groupId>org.aspectj</groupId>

<artifactId>aspectjweaver</artifactId>

<version>1.8.1</version>

</dependency>


2. aop 를 통한 설정.


<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

<property name="dataSource" ref="pushDataSource" />

</bean>


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


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

<tx:attributes>

<tx:method name="createTest"

    isolation="REPEATABLE_READ"

    propagation="REQUIRED"

    rollback-for"RuntimeException" />

</tx:attributes>

</tx:advice>


<aop:config>

<aop:pointcut id="servicePublicMethod"

                expression="execution(* com.joochang.service.*Service.*(..))" />

<aop:adviser id="transactionAdvisor"

               pointcut-ref="servicePubicMethod" advice-ref="txAdvice" />

</aop:config>


- 스프링AOP 대신 AspectJ-AOP를 사용하는 방법도 있다. AspectJ는 프록시 대신 클래스 바이트코드를 직접 변경해서 부가기능을 추가하기 때문에 타깃 오브젝트의 자기 호출 중에도 트랜잭션 부가기능이 잘 적용된다. 굳이 자신의 메소드를 호출하는 경우에도 트랜잭션 속성이 정확히 적용되기를 원한다면 AspectJ를 사용하는편이 좋다. <tx:annotation-driven mode="aspectj" /> 처럼 설정하면 된다. (Spring AOP 는 기본적으로 JDK프록시이고, AspectJ는 클래스기반 프록시)


3. annotation 을 통한 설정. 

 MySql 은 기본 isolation 이 repeatable_read  인데, spring  의 default 가 알아서 맞춰주는지 알아볼 필요도 있고 명확하게 해당 타입을 지정해 주는게 더 와 닿는 것 같습니다.


@Transactional(propagation = Propagarion.REQUIRED,

                  isolation = Isolation.REPEATABLE_READ,

                  rollbackFor = RuntimeException.class)

public MyTestResult createTest() throws RuntimeException {

...

}


 선언적 트랜잭션에서는 런타입 예외가 발생하면 롤백합니다. 반면에 예외가 전혀 발생하지 않거나 checked-exception이 발생하면 commit 합니다.  스프링에서 data-access-exception은 Runtime-Exception으로 전환돼서 던져지므로 Runtime-Exception만 롤백대상이 됩니다. 하지만 롤백대상으로 삼아야 한다면 rollbackFor 를 사용해서 등록합니다. 반대로 기본적인 롤백대상인 Runtime-Exception을 트랜잭션 커밋 대상으로 해 주려면 noRollbackFor 를 사용하면 됩니다.


 그리고 rollback 시 createTest() 에서 만약 try catch 쓸 경우 statement 안에서 예외를 잡더라도 throw new RuntimeException  하지 않으면 롤백되지 않습니다. 또한 finally 로 뭔가 동작을 하길 바래서 사용한다면 ㅠㅠ rollback 이 되지 않습니다.


4. 기타 사항. 

 스프링의 Proxy 는 2가지 타입이 있습니다.  JDK Reflection API 를 사용한 JDK다이내믹프록시 와 CBLIB(Code Generator Library) 인데요. JDK프록시는 인터페이스를 구현한 클래스에 적용이되고, CBLIB은 spring 3.2부터 기본적용이 되어있어서 인터페이스를 사용하지 않은 클래스에 대해 적용이 됩니다.

 스프링AOP는 기본적으로 JDK Reflection API를 사용하여 Spring에 의해 생성 된 다이내믹 프록시 기법을 이용해 동작합니다. 다이내믹 프록시를 적용하려면 인터페이스가 있어야 하지만 스프링 내부의 ProxyFactoryBean 클래스나 CGLIB 라이브러리가 제공해주는 클래스 레벨의 프록시도 사용할 수 있습니다. AspectJ를 사용해 트랜잭션을 적용할 때는 앞에서 설명한 것처럼 @Transactional을 클래스레벨과 클래스 메소드 레벨에 부여해야 한다는 점을 주의해야합니다. AspectJ 모드를 사용하면서 인터페이스에만 @Transactional을 부여하면 트랜잭션이 적용되지 않습니다. 그래서 클래스프록시 모드에서는 Service 코드에 인터페이스를 Implements 해야한다는니 이런 거 필요없습니다.


명확하게 CGLIB을 사용하려면 proxy-target-class=true 옵션을 아래의 설정에 추가 해야만 합니다.

<tx:annotation-driven proxy-target-class="true">

또는 <aop:config proxy-targer-class="true"> </aop:config>

또는 <aop:aspectj-autoproxy proxy-target-class="true"> //  aspectj는 기본이 클래스모드

또는 @EnableAspectJAutoProxy(proxyTargetClass=true)

또는 spring.aop.proxy-target-class=true // application.properties 




반응형

+ Recent posts