본문 바로가기

IT 살이/04. 기술 - 프로그래밍

개발 프레임워크 만들기 대장정 35 - Spring.NET 트랜잭션 관리

 

정리가 힘들었던 포스트였다. AOP에 대한 부분을 연수 전에 작성한 것이라 리마인드를 위해서 그것까지 다시 읽어야했고 읽다 보니 그곳에서도 수정해야 할 부분도 있었다. 어떻게 정리해야 하나 고민이 많았던 부분인데 쌈박하지는 못한 것 같다. 토요일부터 시작해서 오늘에야 끝난다.

 

이 포스트에서는 Spring이 어떻게 트랜잭션 기능을 제공하는지를 이해할 것이다. 그리고 트랜잭션 기능을 이용하기 위해서 어떻게 설정하는지도 알아본다. “트랜잭션 관리자”, “트랜잭션용 AOP 프락시”, “트랜잭션 어드바이저”, “어드바이스 즉 인터셉터”, “포인트컷”같은 용어를 이해할 필요가 있을 것이다. “AOP 프락시”, “어드바이저”, “어드바이스”, “포인트컷”등은 Spring의 트랜잭션용 용어가 아니라 AOP 용어이다. 지난 포스트를 참조할 수 있다 ( AOP 프로그래밍 개념 I, AOP 프로그램 개념 II )

 

  • 트랜잭션 관리자

 

앞 포스트에서 말한 대로 Spring에서는 트랜잭션을 구현하는 코드를 제공하지는 않는다. 다만 기존의 트랜잭션 관리자들에서 시스템 환경에 적절한 것을 사용할 수 있도록 strategy 패턴을 이용해서 추상화했을 분이다. Spring에서는 다음과 같은 전략 인터페이스를 정의하고 있다.

public interface IPlatformTransactionManager

{

    ITransactionStatus GetTransaction(ITransactionDefinition definition);

    void Commit(ITransactionStatus transactionStatus);

    void Rollback(ITransactionStatus transactionStatus);

}

이 전략 인터페이스를 구체적으로 트랜잭션 전략의 구현체로는 다음과 같은 것을 제공하고 있다.

AdoPlatformTransactionManager

ADO.NET기반의 로컬 트랜잭션 제공

ServiceDomainPlatformTransactionManager

Enterprise Services기반의 분산 트랜잭션 제공

TxScopePlatformTransactionManager

System.Transactions 사용한 로컬 / 분산 트랜잭션 관리자

HibernatePlatformTransactionManager

NHiberate기반 또는 ADO.NET&NHibernate를 함께 사용해서 로컬 트랜잭션 제공

 

이 트랜잭션 처리 방법들은 각각의 특성이 있다. Spring.NET 문서 17.2절을 보면 잘 요약되어 있다. : 각 트랜잭션 관리자와 관련해서 로컬 트랜잭션, 글로벌 트랜잭션의 지원 여부 및 관리자의 특성등을 살펴볼 필요가 있겠다.  TxScopePlatformTransactionManager가 가장 유연할 것으로 보인다.

여튼 이런 트랜잭션 관리자는 외부에서 제공( configuration 또는 프로그램적으로 제공)되는 정보를 이용해서 적절한 트랜잭션을 생성, 관리하는 역할을 할 것이다.

트랜잭션을 개념적으로 설명할때는 어떤 “영역(바운더리) 또는 공간”으로 표현한다.  “하나의 트랜잭션 공간안에서 실행되는 코드로부터 영향을 받는 리소스( 데이터베이스 데이터)는 모두가 커밋되거나 또는 모두가 롤백된다”는 식으로 표현한다. 상당히 개념적인 표현이다. 트랜잭션에 참여하는 객체들에는 트랜잭션 성격을 정의하는 하나의 객체들이 달라붙어 있다. 객체와 트랜잭션 객체가 쌍으로 존재한다는 의미이다. 이 트랜잭션 객체의 속성이 같은 객체들은 동일한 트랜잭션 공간에 존재한다고 볼 수 있다. 만약 그 공간이 제공하는 트랜잭션 옵션들이 마음에 안들면 객체는 다른 트랜잭션 옵션을 갖는 객체를 만들어서 새로운 공간을 만들 수도 있다.

여튼 트랜잭션 공간을 생성하기위해서는 트랜잭션을 성격을 결정할 수 있는 몇 가지 옵션들을 정의해줘야 한다는 것인데, 다음과 같은 옵션들이 있다.

Isolation값

Propagation값

Timeout 값

Read-only 여부

트랜잭션은 데이터의 ACID (Atomicity, Consistency, Isolation, Durability) 개념을 보장할 수 있어야 한다. 이런 개념들을 보장하기 위해서 이와 같은 값들의 설정이 필요하다. 이런 값을 어떻게 설정하느냐에 따라서 그 트랜잭션 공간이 보장할 수 있는 성격이 조금씩 변경될 수 있다는 것이다.

이것들을 설명할 기력이 없다. 구글링해보자. “acid, transaction”로 검색하면 안 나올라나? “acid”만으로 검색하면 “산성비”에 대한 결과가 나올 것도 같은데…^^.

여튼 트랜잭션 공간을 생성하기 위해서는 이런 값을 지정해줄 필요가 있는데, 앞의 인터페이스의 메소드 GetTransaction()의 인자로 넘겨주는 객체 ITransactionDefinition을 통해서 그것이 가능하다. 이런 트랜잭션 옵션을 지정하는 것을 “트랜잭션을 정의”하는 것으로 표현하고 있다.

ITransactionDefinition 객체를 GetTransaction()을 호출하면 하나의 트랜잭션 공간이 생긴다고 보면 된다. 이 메소드로부터 반환받는 객체 ITransactionStatus를 통해서 트랜잭션 작업을 수행할 수 있다. 달봉이가 얼른 생각할때는 이 반환되는 객체의 이름을 왜 ITransaction이라고 짓지 왜 끝에 Status를 붙여놨을까 했다. 달봉이도 모른다. 이름만으로 판단해보면 트랜잭션의 상태에 대한 정보도 가지고 있는듯한데, 매뉴얼을 보면 이 녀석이 하는 일은 실제로 트랜잭션에 대한 상태를 제공한다. 그리고 이 녀석을 통해서 트랜잭션을 실행시킬 수도 있단다. 생각해보면 이해도 갈 수 있는 부분이다. Spring 입장에서는 트랜잭션을 처리하는 로직을 구현하고 있는 것이 아니다. 그냥 필요한 옵션들을 받아서 트랜잭션을 직접 생성하고 트랜잭션 처리를 로우 레벨에서 구현한 것은 각각의 트랜잭션 전략 객체들이다. Spring은 녀석들에게 줄 것 주고 “트랜잭션 시작하세요, 트랜잭션 현재 상태는 어때요? 좋아요? 그럼 커밋하세요” 라는 명령을 내리거나 상태를 조회하는 일만 하면 될 것 같다. Spring을 설계한 사람들이 잘 알아서 했겠는가? 우선은 그들의 설계를 이해하는 차원에서 공부하자.

그러나 사실 Spring 프레임워크를 사용하면 앞의 인터페이스의 메소드를 개발자가 직접 호출할 일은 거의 없을 것이다. 이런 API보다는 트랜잭션 관리자의 개념적인 의미에 집중하도록 하자.

기업에서 업무를 구현하는 개발자들을 위한 최종 개발 프레임워크라면 이런 트랜잭션 처리 API에 접근할 필요가 없도록 지원해줘야 한다. 물론 최종 트랜잭션에서 정의(ITransactionDefinition)한 것이 적합하지 않은 특별한 업무가 발생하는 경우 프로그램적으로 새로운 트랜잭션을 생성해야 한다면 직접 개발자가 이런 API를 호출할 수도 있겠다.

트랜잭션 관리자는 내부적으로 트랜잭션을 생성, 관리한다고 했다. 그러나 구체적으로 어떤 관리자를 사용해서 트랜잭션을 관리할 지는 설정을 해 줘야 한다.

 

  • 사용할 트랜잭션 관리자 설정

 

<objects xmlns='http://www.springframework.net'

xmlns:db="http://www.springframework.net/database">

  <db:provider id="DbProvider"

               provider="SqlServer-1.1"

               connectionString="Data Source=(local);Database=Spring;...">

  </db:provider>

  <object id="TransactionManager"

          type="Spring.Data.AdoPlatformTransactionManager, Spring.Data">

    <property name="DbProvider" ref="DbProvider"/>

  </object>

  . . . other object definitions . . .

</objects>

 

<object/>를 이용해서 일반 객체를 설정하듯 하면 된다. 이 설정은 AdoPlatformTransactionManager를 사용하겠다는 것이고 DbProvider 속성을 이용해서 데이터베이스 연결 설정을 함께 하고 있다.

참고로 앞의 설정에서 트랜잭션 관리자(AdoPlatformTransactionManager)를 정의할때 DbProvider 속성이 있는 것에 주목해보자.
데이터베이스 연결을 트랜잭션 관리자가 관리하고 있다는 이 사실은 중요한 부분이다.
트랜잭션 관리 기술중에서 TransactionScope을 사용하는 가장 큰 이유를 알고 있을 것이다. 그런가?
하나의 트랜잭션 내부에서 접근하는 데이터베이스 서버의 수가 한대인 경우 로컬 트랜잭션으로 작업을 하다가 만약 2대 이상의 데이터베이스 작업을 하게 되면 자동으로 전역 트랜잭션( 분산 트랜잭션)으로 자동 전환( promotion)된다는 이점이 있다.
로컬 트랜잭션으로 작업을 하는 것이 당연 성능 측면에서 유리하다.
Enterprise Services의 MS-DTC를 이용하게 되면 모든 트랜잭션을 전역으로 수행한다. 그리고 ADO.NET은 로컬 트랜잭션만한 지원한다.
따라서 트랜잭션 관리에는 TransactionScope을 주로 이용하게 되는데 이 녀석의 단점이 있다. MS-SQL 서버로만 구성된 분산 환경에서라면 이 기능을 사용하는데 문제가 없다. 그러나 오라클 데이터베이스가 포함된 분산 환경에서는 전역 트랜잭션으로의 자동 전환( promotion)이 일어나지 않는다.
이곳에서 말하고 싶은 이것이 아니다. 더 중요한 단점이 있다. 데이터베이스 서버가 한대인 경우에도 전역 트랜잭션으로 자동전환되는 경우가 있다. 하나의 트랜잭션에서 첫번째 연결이 열리는 순간까지는 로컬 트랜잭션으로 작업을 하다가, 동일한 데이터베이스로의 연결이 두번째 열리는 경우 전역 트랜잭션으로 자동전환되어 버린다. 설령 데이터베이스 연결 문자열이 동일하다 해도, 두번째 데이터베이스 연결이 열리는 순간 트랜잭션 관리자는 분산 환경으로 판단한다는 것이다.
그렇다면 하나의 트랜잭션 안에서 하나의 데이터베이스에 작업을 할때는 동일한 데이터베이스 연결을 사용하면 되는 것이다. 흔히들 DAO단에서 연결 정보를 관리하는 구조에서는 이 단점을 피해갈 수 없다. 해서 Spring.NET에서는 트랜잭션 관리자에게 연결 정보를 주고 DAO( DAC, DSL이라고도 흔히 부른다)단에서 데이터베이스에 연결할때는 이 녀석을 사용하도록 하고 있다.
위의 트랜잭션 관리자(AdoPlatformTransactionManager) 설정에 DbProvider 속성이 있다는 것이 이런 이유에서 합리적이라고 할 수 있겠다.
 

  • 트랜잭션용 AOP 프락시

 

AOP 프락시에 대해서는 이전 포스트에서 설명했다. Spring의 트랜잭션용 프락시가 어떤 위치에 있는지 개념적으로 다시 한번 더 보자. Spring.NET 레퍼런스 문서 17.5.1에 나와 있는 그림이다.

그림을 보면 클라이언트 코드는 직접 타겟 객체를 참조하지 않고 트랜잭션용 AOP 프락시를 참조한다. 트랜잭션용 AOP 프락시는 클라이언트의 호출을 받으면 바로 타겟 객체의 메소드를 호출하지 않는다. 트랜잭션용 어드바이저를 호출한다. 이 녀석은 트랜잭션 작업을 수행하는데, 트랜잭션이 생성되어 있지 않다면 새로운 트랜잭션을 생성하거나 또는 기존의 트랜잭션 공간에 참여하는 작업을 하게 되는 것이다. 커스텀 어드바이저가 또 있다면 그 녀석들의 작업을 차례로 수행하고 나서 마지막으로 타겟 객체의 메소드를 호출하게 된다.

타겟 메소드가 리턴을 하게 되면 그 결과는 다시 호출순과 반대순으로 차례로 전달된다. 이때 트랜잭션용 어드바이저는 해당 호출에 대해 예외가 발생하지 않고 호출이 성공을 하게 되면 해당 트랜잭션을 커밋하게 된다.

이런 트랜잭션용 AOP 프락시를 생성하는 방법을 Spring.NET에서는 여러가지를 제공한다.

 

  • Spring의 트랜잭션용 프락시 객체 생성 방법

 

Spring.NET에서 제공하는 트랜잭션용 프락시 객체를 생성하는 방법에는 몇 가지가 있지만 이곳에서는 기업용 애플리케이션에 적용하기 적합한 2가지만 언급하겠다.  다른 방법에 대해서는 Spring.NET 레퍼런스 문서를 참고하길 바란다.

.ProxyFactoryObject  :

이 녀석을 사용해서 타겟 객체에 대한 프락시 객체를 얻는 방법은 이전의 포스트에서 설명한 적 있다. 이 녀석을 이용하기 위해서는 타겟 객체에 대한 참조 그리고 트랜잭션용 어드바이스( 인터셉터 )에 대한 참조를  넘겨줘야 한다.

그러나 이 녀석보다는 기업용 애플리케이션에 적합한 것은 다음 녀석이다.

.DefaultAdvisorAutoProxyCreator :

이 녀석은 TransactionAttribute와 함께 사용해서 트랜잭션용 프락시를 생성하고 싶은 메소드를 손쉽게 결정할 수 있다. 트랜잭션 커밋을 하기 위해서 [AutoComplete]같은 어트리뷰를 사용해 본적이 있을 것이다. 비슷하게 [Transaction]어트리뷰트를 사용해서 해당 메소드를 호출할때는 트랜잭션용 프락시가 자동으로 생성될 수 있도록 하는 방법이다.

public class UserMgmtDSL : ...

{

    [Transaction()]

    public void Save( ... )

    {

        //필요한 작업을 한다.

    }

}                              

이 녀석에게는 타겟 객체에 대한 참조나 트랜잭션용 어드바이스에 대한 참조를 명시적으로 건네주지 않아도 된다.  이게 무슨 말인지 ProxyFactoryObject를 사용하는 설정과 DefaultAdvisorAutoProxyCreator를 사용하기 위한 설정을 비교해 보도록 하자.

 

  • ProxyFactoryObject를 사용하기 위한 설정

 

<!--프락시 생성자-->

<object id="proxyCreator" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop">

  <property name="Target" ref="bslObject"/>

  <property name="ProxyInterfaces">

    <value>Spring.Data.IBSLObject</value>

  </property>

  <property name="InterceptorNames">

    <value>transactionInterceptor</value>

  </property>

</object>

 

<!-- 트랜잭션용 어드바이스-->

<object id="transactionInterceptor" type="Spring.Transaction.Interceptor.TransactionInterceptor, Spring.Data">

  <property name="TransactionManager" ref="transactionManager"/>

  <!-- note do not have converter from string to this property type registered -->

  <property name="TransactionAttributeSource" ref="methodMapTransactionAttributeSource"/>

</object>

<!-- 포인트컷 및 트랜잭션 옵션-->

<object name="methodMapTransactionAttributeSource"

type="Spring.Transaction.Interceptor.MethodMapTransactionAttributeSource, Spring.Data">

  <property name="MethodMap">

    <dictionary>

      <entry key="Spring.Data.BSLObject.SaveTwoTestObjects, Spring.Data.Integration.Tests"

      value="PROPAGATION_REQUIRED"/>

      <entry key="Spring.Data.BSLObject.DeleteTwoTestObjects, Spring.Data.Integration.Tests"

      value="PROPAGATION_REQUIRED"/>

    </dictionary>

  </property>

</object>

 

<!--   BSL, DAO 객체 정의   -->

<object id="bslObject" type="Spring.Data.BSLObject, Spring.Data.Integration.Tests">

  <property name="DaoObject" ref="daoObject"/>

</object>

 

프락시 생성자 ProxyFactoryObject 를 설정하는 부분에서는 어떤 타겟 객체에 대해서 프락시를 생성해야 할지를 “Target” 속성을 통해서 설정해줘야 한다. 그리고 “InterceptorNames” 속성을 통해서 사용할 트랜잭션용 어드바이스도 설정해줘야 한다.

위 설정에서는 “bslObject”라는 이름으로 정의된 BSL단 객체에 대해서 프락시를 생성하겠다는 것을 보여주고 있다. 기업용 애플리케이션에서는 수많은 BSL단 객체들이 나올텐데 이것들을 프락시 생성자들에 모두 등록하는 것은 아니올시다이다. 그리안해도 BSL단의 객체들은 모두 위 설정의 아래처럼 configuration에 정의해야 한다. 맨 아래처럼 해야 하는 것은 Spring.NET의 IoC 기능이나 AOP 기능을 위해서는 어쩔 수 없는 것이라 해도 트랜잭션을 위해서 또 한번 객체마다 트랜잭션용 프락시에 등록작업을 해야 한다는 것은 중복작업이라는 기분을 피할 수 없다.

 

  • DefaultAdvisorAutoProxyCreator용 설정

 

Spring.NET은 TransactionAttribute를 인식할 수 있는 기능을 지원한다고 했으니 configuration에 정의된 객체중에서 [Transaction()] 어트리뷰트가 표시된 메소드들이 호출될때는 스스로 인식해서 트랜잭션용 AOP 프락시를 생성해서 반환해주는 기능이 있으면 좋을 것이다.

Spring.NET은 DefaultAdvisorAutoProxyCreator와 TransactionAttribute를 사용해서 그런 시나리오를 지원해준다는 것이다. 즉 DefaultAdvisorAutoProxyCreator를 사용하면 BSL단의 객체를 이 녀석에게 등록할 필요가 없다는 것이다. configuration에 정의된 모든 객체들중에서 TransactionAttribute가 표시된 객체들에 대해서 자동으로 트랜잭션용 AOP 프락시를 생성한다는 것이다. 또한 “InterceptorNames” 속성도 “transactionInterceptor”라고 설정하면 Spring.NET에서 기본적으로 제공되는 트랜잭션용 어드바이스를 사용하게 된다.

따라서 굳이 트랜잭션용 프락시나 어드바이스 설정을 해 주지 않아도 된다는 것이다. 그런 기본적인 구현을 사용하겠다는 표시를 해주면 된다. 다음 설정을 보자.

<!--기본 트랜잭션용 AOP 프락시 생성자 및 어드바이스 사용 -->

<tx:attribute-driven transaction-manager="transactionManager"/>

 

<!--   BSL, DAO 객체 정의   -->

<object id="bslObject"

        type="Spring.Data.BSLObject, Spring.Data.Integration.Tests">

  <property name="DaoObject" ref="daoObject"/>

</object>

<object id="daoObject" type="Spring.Data.DaoObject, Spring.Data.Integration.Tests">

  <property name="AdoTemplate" ref="adoTemplate"/>

</object>

트랜잭션용 AOP 프락시를 생성하는데 DefaultAdvisorAutorProxyCreator를 사용하고 기본적인 트랜잭션 어드바이스를 사용하겠다는 표시로 <tx:attribute-driven>을 추가하고 있다. that’s all !

이런 방식으로 트랜잭션용 프락시와 어드바이스 설정을 하기 위해서는 한 가지가 더 필요하다. 트랜잭션용 네임스페이스를 파싱할 수 있는 파서가 등록되어야 한다.

 

  • 트랜잭션용 네임스페이스 파서 등록

 

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

  <configSections>

    <sectionGroup name="spring">

      <section name="parsers" type="Spring.Context.Support.NamespaceParsersSectionHandler, Spring.Core" />

      <!-- other spring config sections like context, typeAliases, etc not shown for brevity -->

    </sectionGroup>

  </configSections>

  <spring>

    <parsers>

      <!-- -->

      <parser type="Spring.Data.Config.DatabaseNamespaceParser, Spring.Data" />

      <parser type="Spring.Transaction.Config.TxNamespaceParser, Spring.Data" />

      <parser type="Spring.Aop.Config.AopNamespaceParser, Spring.Aop" />

    </parsers>

  </spring>

  </configSections>

 

  • 트랜잭션용 최종 configuration 모습

 

달봉이는 기업용 애플리케이션에서 Spring.NET을 이용해서 비즈니스 서비스 레이어의 트랜잭션 처리를 해야 한다면 DefaultAdvisorAutoProxyCreator를 사용할 것이다. 트랜잭션 처리를 해야 하는 객체가 대규모로 있는 상황에서 반복되는 설정을 가장 피할 수 있는 방법으로 가장 적합한 녀석으로 판단된다.

이제 트랜잭션이 필요한 데이터베이스 프로그래밍을 할때 필요한 전체 설정들을 통합해보도록 하자. 기업 애플리케이션의 설정은 다음과 유사한 모습이 될 것이다.

 

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

  <configSections>

    <sectionGroup name="spring">

      <section name="parsers" type="Spring.Context.Support.NamespaceParsersSectionHandler, Spring.Core" />

      <!-- other spring config sections like context, typeAliases, etc not shown for brevity -->

    </sectionGroup>

  </configSections>

  <spring>

    <parsers>

      <!-- -->

      <parser type="Spring.Data.Config.DatabaseNamespaceParser, Spring.Data" />

      <parser type="Spring.Transaction.Config.TxNamespaceParser, Spring.Data" />

      <parser type="Spring.Aop.Config.AopNamespaceParser, Spring.Aop" />

    </parsers>

  </spring>

  </configSections>

 

  <objects xmlns="http://www.springframework.net"

           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

           xmlns:tx="http://www.springframework.net/tx"

           xmlns:db="http://www.springframework.net/database" >

    <!-- 데이터베이스 연결 정보-->

    <db:provider id="DbProvider"

                  provider="SqlServer-1.1"

                  connectionString="Data Source=(local);Database=Spring;User ID=springqa;Password=springqa;Trusted_Connection=False"/>

    <!-- 트랜잭션 관리자-->

    <object id="transactionManager"

            type="Spring.Data.AdoPlatformTransactionManager, Spring.Data">

      <property name="DbProvider" ref="DbProvider"/>

    </object>

 

    <!--데이터베이스 접근을 위한 AdoTemplate 정의  -->

    <object id="adoTemplate" type="Spring.Data.AdoTemplate, Spring.Data">

      <property name="DbProvider" ref="DbProvider"/>

    </object>

 

    <!--기본 트랜잭션용 AOP 프락시 생성자 및 어드바이스 사용 -->

    <tx:attribute-driven transaction-manager="transactionManager"/>

 

    <!--   BSL, DAO 객체 정의   -->

    <object id="bslObject"

            type="Spring.Data.BSLObject, Spring.Data.Integration.Tests">

      <property name="DaoObject" ref="daoObject"/>

    </object>

    <object id="daoObject" type="Spring.Data.DaoObject, Spring.Data.Integration.Tests">

      <property name="AdoTemplate" ref="adoTemplate"/>

    </object>

  </objects>

</configuration>

 

이 설정에는 어떤 메소드에 트랜잭션이 적용될 것인가가 나와 있지 않다. BSL단 객체의 메소드에 어트리뷰트( TransactionAttribute)를 표시하고 있다. 앞에서 말한대로 Spring은 configuration에 정의된 모든 객체들중에서 어트리뷰트( Transaction() )이 표시된 객체들을 찾는다. 그런 다음 TrasanctionAttribute에 설정된 옵션값들( IsolationLevel, Propagation값 등)로 세팅된 AOP 트랜잭션 인터셉터를 생성한다.

이제 앞에서 보여준 그림을 다시 한번 보도록 하자. “타갯 객체의 메소드 호출자가 트랜잭션용 프락시를 통해서 메소드를 호출하게 되면 이제 트랜잭션 관리자는 트랜잭션 인터셉터에 설정된 옵션들을 이용해서 트랜잭션 객체를 생성하고 트랜잭션 관리를 시작하게 된다.” 이제 이 말이 이해가 되어야 하는데….

만약 개발자가 TransactionAttribute를 사용할때 아무 트랜잭션 옵션도 제공하지 않는다면 즉 [Transaction()]만 사용한다면 기본 옵션값으로 트랜잭션이 설정된다. 다음은 Spring.NET에서 제공하는 트랜잭션 옵션들의 기본값들이다.

 

propagation : TransactionPropagation.Required

isolation : IsolationLevel.ReadCommitted

read-only : false 즉 read/write

timeout : 이 값은 사용되는 트랜잭션 관리자에 따라서 기본값이 달라진다.

 

  • 트랜잭션 기본 옵션 변경

 

만약 이 기본값을 시스템 전체적으로 바꾸고 싶다면 어떻게 해야 하나. <tx:advice> 요소를 사용해서 변경할 수 있다.

<!-- the transactional advice (i.e. what 'happens'; see the <aop:advisor/> object below) -->

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

  <!-- the transactional semantics... -->

  <tx:attributes>

    <!-- all methods starting with 'get' are read-only -->

    <tx:method name="Get*" read-only="true"/>

    <!-- other methods use the default transaction settings (see below) -->

    <tx:method name="*" propagation="Required" isolation="ReadUncommitted" timeout="60" read-only="false"/>

  </tx:attributes>

</tx:advice>

 

이 설정은 메소드명이 “Get”으로 시작하는 메소드는 읽기 전용의 트랜잭션을 사용하고 그 외는 기본 트랜잭션 옵션을 설정된 트랜잭션을 이용하라는 내용이다. 이렇게 설정하면 읽기 전용의 트랜잭션을 사용하기 위해서 개발자는 메소드명을 지을때 “Get”으로 시작하는 이름을 사용하면 된다. 그 외의 모든 메소드( name=”*” )는 설정에서 앞의 설정에서 주어진 트랜잭션 컨텍스에서 실행된다.

이 설정은 전체 시스템의 기본값을 변경한다. 만약 필요하다면 메소드별로 개발자는 TransactionAttribute의 속성을 변경해서 이 값을 변경할 수 있다.

 

트랜잭션을 위한 Spring.NET 설정에 대한 이야기를 이것으로 마무리해야 겠다. Spring.NET 메뉴얼을 보면 트랜잭션과 관련해서 다른 얘기도 많이 있지만 달봉이는 기업용 애플리케이션에서 사용하기에 적절하다고 판단되는 설정에 대한 이야기를 위주로 했다.

 

  • 다음 작업

 

이제 남은 것은 트랜잭션을 사용할 BSL 및 DAO 객체를 코딩하는 것이다.

 

휴~~