둘러보기 생략.
 

쿼리서비스를 사용하면서 트랜잭션으로 묶는 방법이 없을까요?

쿼리서비스를 이용하여 5개 테이블에 insert 나 update를 한다면
이것을 한 트랜잭션으로 묶는 방법이 없을까요?

선언적인 방법(xml configulation)이나 명시적인 방법(programming) 을 이용한 방법들이 있다면
알고 싶습니다.

명시적 트랜젝션 선언 방법

명시적인 방법으로 트랜젝션을 선언하기 위해는

아래와 같은 방법으로 프로그래밍 하시면 됩니다.

DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = txManager.getTransaction(def);
  try {
  //
  }
  catch (Exception ex) {
    txManager.rollback(status);
    throw ex;
  }
txManager.commit(status);

또 다른 방법은 TransactionTemplate이용할 수 있습니다.

Spring Bean설정 파일에

<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">

        <property value="transactionManager" ref="txManager"/>
<bean>

위와 같이 TransactionTemplate을 등록하고

DI나 DL을 이용해 transactionTemplate을 얻어온 후 아래와 같은 코드를 추가하시면 됩니다.

this.transactionTemplate.execute(new TransactionCallbackWithoutResult() {                
  public void doInTransactionWithoutResult(TransactionStatus status) {                    
  //biz. logic        
}});

선언적인 방법으로 AOP를 활용하실 수 있습니다.

대략 이런 PointCut이 있다고 가정할 때

<aop:config>
<!-- pointcut -->
<aop:pointcut id="handlerMethod"
expression="execution(public * org.codelabor..*ExceptionHandler.*(..))"/>
<aop:pointcut id="actionMethod"
expression="execution(public * org.codelabor..*Action.*(..))"/>
<aop:pointcut id="serviceMethod"
expression="execution(public * org.codelabor..*Service.*(..))" />
<aop:pointcut id="managerMethod"
expression="execution(public * org.codelabor..*Manager.*(..))" />
<aop:pointcut id="daoMethod"
expression="execution(public * org.codelabor..*DAO.*(..))" />
</aop:config>

service, manager, dao 가 통상적으로 tx 의 시작점이 되므로
아래와 같이 AOP를 걸어줄 수 있습니다.

<!--
[PROPAGATION]
REQUIRED: 이미 tx가 존재할 경우, 해당 tx에 참여 / tx가 없을 경우, 새 tx를 생성하고 실행
SUPPORTS: 이미 tx가 존재할 경우, 해당 tx에 참여 / tx가 없을 경우, 그냥 실행
MANDATORY: 이미 tx가 존재할 경우, 해당 tx에 참여 / tx가 없을 경우, Exception 발생
REQUIRES_NEW: 이미 tx가 존재할 경우, 해당 tx suspend 시키고 새 tx를 생성 / tx가 없을 경우, 새 tx를 생성
NOT_SUPPORTED: 이미 tx가 존재할 경우, 해당 tx suspend 시키고 tx없이 실행 / tx가 없을 경우, 그냥 실행 NEVER: 이미 tx가 존재할 경우, Exception 발생 / tx가 없을 경우, tx없이 실행
NESTED: 이미 tx가 존재할 경우, 해당 tx에 참여 / tx가 없을 경우, nested tx 실행

[ISOLATION]
DEFAULT: datastore에 의존
READ_UNCOMMITTED: Dirty reads, Non-repeatable reads, Phantom reads 발생
READ_COMMITTED: Dirty reads 방지, Non-repeatable reads, Phantom reads 발생
REPEATABLE_READ: Non-repeatable Read 방지, Phantom reads 발생
SERIALIZABLE: Phantom Read 방지
-->

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

<!-- service -->
<tx:advice id="serviceTxAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="*"
rollback-for="org.codelabor.system.exceptions.RollbackCommonException"
no-rollback-for="org.codelabor.system.exceptions.NoRollbackCommonException"
propagation="REQUIRED" timeout="600" />
<tx:method name="select*" rollback-for="RollbackCommonException"
propagation="SUPPORTS" read-only="true"
timeout="20" />
<tx:method name="find*" rollback-for="RollbackCommonException"
propagation="SUPPORTS" read-only="true"
timeout="20" />
</tx:attributes>
</tx:advice>

<!-- manager -->
<tx:advice id="managerTxAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="*"
rollback-for="org.codelabor.system.exceptions.RollbackCommonException"
no-rollback-for="org.codelabor.system.exceptions.NoRollbackCommonException"
propagation="REQUIRED" timeout="600" />
<tx:method name="select*" rollback-for="RollbackCommonException"
propagation="SUPPORTS" read-only="true"
timeout="20" />
<tx:method name="find*" rollback-for="RollbackCommonException"
propagation="SUPPORTS" read-only="true"
timeout="20" />
</tx:attributes>
</tx:advice>

<!-- dao -->
<tx:advice id="daoTxAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="*"
rollback-for="org.codelabor.system.exceptions.RollbackCommonException"
no-rollback-for="org.codelabor.system.exceptions.NoRollbackCommonException"
propagation="REQUIRED" timeout="600" />
<tx:method name="select*" rollback-for="RollbackCommonException"
propagation="SUPPORTS" read-only="true"
timeout="20" />
<tx:method name="find*" rollback-for="RollbackCommonException"
propagation="SUPPORTS" read-only="true"
timeout="20" />
</tx:attributes>
</tx:advice>

<!-- tx advice -->
<aop:config>
<aop:advisor advice-ref="serviceTxAdvice" pointcut-ref="serviceMethod" />
<aop:advisor advice-ref="managerTxAdvice" pointcut-ref="managerMethod" />
<aop:advisor advice-ref="daoTxAdvice" pointcut-ref="daoMethod" />
</aop:config>

참고로 annotation 보다는 xml에 기재하는 것이
프로젝트 종료 후, 유지보수자가 tx 속성 변경이나 pointcut 추가하기 유리합니다.