Seasarプロジェクト ＞ Seasar.NETプロジェクト ＞ S2Container.NET ＞ トランザクション

# 目次

上へ

S2Tx.NETの機能を使って、.NETオブジェクトに対して、 Aspectでトランザクションの自動管理機能を組み込むことができます。 Aspectを適用については、こちらの注意点を確認して下さい。 トランザクションの自動管理には、System.Transactionsによるトランザクション(1.2.0-rc1 for .NET 2.0以降), ローカルトランザクション, Microsoft 分散トランザクション コーディネータ (MS DTC)の3つの種類のトランザクションをサポートしています。 組む込むことのできるトランザクション属性は次のとおりです。

- トランザクション属性
- ローカルトランザクション
- MS DTCによる分散トランザクション
- System.Transactionsによるトランザクション
- 例外発生時の動作

# トランザクション属性

上へ

S2Container.NETが標準で用意しているTx.dicon（System.Transactions用）, LocalTx.dicon（ローカルトランザクション用）, DTCTx.dicon（MS DTC用）には、 次のトランザクション属性に対応したAdviceが定義されています。 コンポーネント名がAdviceの名前です。

属性 | コンポーネント名 | 説明
--- | --- | ---
Required | LocalTx.RequiredTx （ローカルトランザクション用） | トランザクションが存在する場合はこれを共有し、 必要に応じて新しいトランザクションを作成します。
DTCTx.RequiredTx （MS DTC用）
Tx.RequiredTx （System.Transactions用）
RequiresNew | LocalTx.RequiresNewTx （ローカルトランザクション用） | 現在のコンテキストの状態とは関係なく、新しいトランザクションでコンポーネントを作成します。
DTCTx.RequiresNewTx （MS DTC用）
Tx.RequiresNewTx （System.Transactions用）
Supported | LocalTx.SupportedTx （ローカルトランザクション用） | トランザクションが存在する場合はこれを共有します。
DTCTx.SupportedTx （MS DTC用）
NotSupported | LocalTx.NotSupportedTx （ローカルトランザクション用） | 制御するトランザクションがないコンテキストでコンポーネントを作成します。
DTCTx.NotSupportedTx （MS DTC用）
Tx.NotSupportedTx （System.Transactions用）

# ローカルトランザクション

上へ

ローカルトランザクションを利用するためには、Seasar.Extension.Tx.Impl.TxDataSourceからコネクションを取得します。 コネクションのオープン、クローズはAdvice(MethodInterceptor)が管理するため、 DAO (Data Access Object)では行いません。 DAOにTxDataSourceをインジェクションしてもらうために Seasar.Extension.ADO.IDataSource型のプロパティを用意します。

コネクション、トランザクション、コマンド、パラメータのオブジェクトはdataSourceから作成します。 従業員を扱うためのDAOインターフェースとDAO実装クラスは次のようになります。

## EmployeeDao.cs

C#

    using System;
    using System.Data;
    using Seasar.Extension.ADO;

    namespace Seasar.Examples.Reference.LocalTx
    {
        // 従業員を扱うためのDAOインターフェース
        public interface IEmployeeDao
        {
            void Insert();
        }

        // 従業員を扱うためのDAO実装クラス
        public class EmployeeDaoImpl : IEmployeeDao
        {
            private IDataSource dataSource;

            // TxDataSourceをインジェクションしてもらうためのプロパティ
            public IDataSource DataSource
            {
                set { dataSource = value; }
            }

            #region IEmployeeDao メンバ

            public void Insert()
            {
                string sql = "insert into emp2 values(@empno, @ename, @deptnum)";

                using(IDbCommand cmd = dataSource.GetCommand(sql,
                          dataSource.GetConnection(), dataSource.GetTransaction()))
                {
                    cmd.Parameters.Add(dataSource.GetParameter("@empno", 99));
                    cmd.Parameters.Add(dataSource.GetParameter(
                        "@ename", "Sugimoto"));
                    cmd.Parameters.Add(dataSource.GetParameter("@deptnum", 31));
                    cmd.ExecuteNonQuery();
                    Console.WriteLine("EMPを追加しました。");
                }
            }

            #endregion
        }
    }

VB

    Imports System
    Imports System.Data
    Imports Seasar.Extension.ADO

    Namespace Seasar.Examples.Reference.LocalTx

       ' 従業員を扱うためのDAOインターフェース
       Public Interface IEmployeeDao
          Sub Insert()
       End Interface 'IEmployeeDao

       ' 従業員を扱うためのDAO実装クラス
       Public Class EmployeeDaoImpl
          Implements IEmployeeDao

          Private dataSource As IDataSource

          ' TxDataSourceをインジェクションしてもらうためのプロパティ
          Public WriteOnly Property DataSource() As IDataSource
             Set(ByVal value As IDataSource)
                dataSource = value
             End Set
          End Property

          Public Sub Insert()

             Dim sql As String = _
                 "insert into emp2 values(@empno, @ename, @deptnum)"

             Dim cmd As IDbCommand = dataSource.GetCommand(sql, _
                dataSource.GetConnection(), dataSource.GetTransaction())
             Try
                cmd.Parameters.Add(dataSource.GetParameter("@empno", 99))
                cmd.Parameters.Add(dataSource.GetParameter("@ename", "Sugimoto"))
                cmd.Parameters.Add(dataSource.GetParameter("@deptnum", 31))
                cmd.ExecuteNonQuery()
                Console.WriteLine("EMPを追加しました。")
             Finally
                cmd.Dispose()
             End Try
          End Sub
       End Class
    End Namespace

IEmployeeDaoを実行するクラスは次のようになります。

## LocalTxClient.cs

C#

    using System;
    using Seasar.Framework.Container;
    using Seasar.Framework.Container.Factory;

    namespace Seasar.Examples.Reference.LocalTx
    {
        public class LocalTxClient
        {
            private static readonly String PATH
                    = "Seasar.Examples/Reference/LocalTx/LocalTxClient.dicon";

            public LocalTxClient() {}

            public void Main()
            {
                // S2コンテナを作成します
                IS2Container container = S2ContainerFactory.Create(PATH);
                // S2コンテナを初期化します
                container.Init();

                // S2コンテナからIEmployeeDaoを実装するコンポーネントを取り出します
                IEmployeeDao dao = (IEmployeeDao) container.GetComponent(
                    typeof(IEmployeeDao));

                // 従業員を追加します
                dao.Insert();
            }
        }
    }

VB

    Imports System
    Imports Seasar.Framework.Container
    Imports Seasar.Framework.Container.Factory

    Namespace Seasar.Examples.Reference.LocalTx

       Public Class LocalTxClient

          Private Shared PATH As [String] = _
             "Seasar.Examples/Reference/LocalTx/LocalTxClient.dicon"

          Public Sub New()
          End Sub

          Public Sub Main()

             ' S2コンテナを作成します
             Dim container As IS2Container = S2ContainerFactory.Create(PATH)
             ' S2コンテナを初期化します
             container.Init()

             ' S2コンテナからIEmployeeDaoを実装するコンポーネントを取り出します
             Dim dao As IEmployeeDao = _
                CType(container.GetComponent(GetType(IEmployeeDao)), IEmployeeDao)

             ' 従業員を追加します
             dao.Insert()
          End Sub
       End Class
    End Namespace

diconファイルは次のようになります。Required属性のトランザクションを適用するために、 EmployeeDaoImplのaspectにLocalTx.RequiredTxを指定してします。

## LocalTxClient.dicon

    <?xml version="1.0" encoding="utf-8" ?>
    <!DOCTYPE components PUBLIC "-//SEASAR2.1//DTD S2Container//EN"
    "http://www.seasar.org/dtd/components21.dtd">
    <components>
    <include path="Seasar.Examples/Tx.dicon" />
        <component name="LocalTxClient"
            class="Seasar.Examples.Reference.LocalTx.LocalTxClient" />

        <component class="Seasar.Examples.Reference.LocalTx.EmployeeDaoImpl">
            <aspect>LocalTx.RequiredTx</aspect>
        </component>
    </components>

## Tx.dicon

    <?xml version="1.0" encoding="utf-8" ?>
    <!DOCTYPE components PUBLIC "-//SEASAR2.1//DTD S2Container//EN"
    "http://www.seasar.org/dtd/components21.dtd">
    <components namespace="LocalTx">
        <include path="Seasar.Examples/Ado.dicon" />

        <component name="RequiredTx"
                class="Seasar.Extension.Tx.TransactionInterceptor">
            <arg>
                <component
                    class="Seasar.Extension.Tx.Impl.LocalRequiredTxHandler" />
            </arg>
        </component>

        ...

    </components>

## Ado.dicon

    <?xml version="1.0" encoding="utf-8" ?>
    <!DOCTYPE components PUBLIC "-//SEASAR2.1//DTD S2Container//EN"
    "http://www.seasar.org/dtd/components21.dtd">
    <components namespace="Ado">

      <!-- .NET Framework Data Provider for SQL Server を使用する場合に必要です。 -->
      <component name="SqlClient" class="Seasar.Extension.ADO.DataProvider">
        <property name="ConnectionType">
            "System.Data.SqlClient.SqlConnection"
        </property>
        <property name="CommandType">
            "System.Data.SqlClient.SqlCommand"
        </property>
        <property name="ParameterType">
            "System.Data.SqlClient.SqlParameter"
        </property>
        <property name="DataAdapterType">
            "System.Data.SqlClient.SqlDataAdapter"
        </property>
      </component>
      <component name="DataSource" class="Seasar.Extension.Tx.Impl.TxDataSource">
        <property name="DataProvider">SqlClient</property>
        <property name="ConnectionString">
    "Data Source=(local);Initial Catalog=s2dotnetdemo;Integrated Security=SSPI"
        </property>
      </component>

      <component class="Seasar.Extension.Tx.Impl.TransactionContext" />
    </components>

## 実行結果

    DEBUG トランザクションを開始しました
    EMPを追加しました。
    DEBUG トランザクションをコミットしました

# MS DTCによる分散トランザクション

上へ

MS DTCによる分散トランザクションを利用するためには、MS DTCが利用できる環境でなければいけません。 ローカルトランザクションのようにTxDataSourceからコネクションをとらないといけないといった制限はありませんが、 データプロバイダに依存しないコーディングをするために、Seasar.Extension.ADO.Impl.DataSourceImpl からコネクション、コマンド、パラメータのオブジェクトを作成します。 MS DTCを利用する場合は、1回のクエリ発行毎にコネクションをクローズしても、トランザクションは維持されます。

分散トランザクションの注意点ですが、Seasar.dllをCOM+アプリケーションにインストールする必要があります。 COM+アプリケーションへのインストールは自動で行われますが、実行ユーザにその権限がない場合は、 レジストリキーへのアクセスが拒否されたという例外（System.UnauthorizedAccessException）が発生します。 このような例外が発生した場合は、権限を持ったユーザで実行するか、 あらかじめ権限を持ったユーザでCOM+アプリケーションへのインストールを手動で行ってください。

手動でSeasar.dllをCOM+アプリケーションにインストールするためには .NET サービス インストールツール（RegSvcs.exe）を使います。.NET 2.0 の場合は、 RegSvcs.exeは %windir%\Microsoft.NET\Framework\v2.0.50727\RegSvcs.exe に存在します。以下のようにRegSvcs.exeのパラメータにSeasar.dllへのフルパスを渡して、 Seasar.dllをCOM+アプリケーションにインストールすることができます。

- RegSvcs.exe Seasar.dllへのフルパス

従業員を扱うためのDAOインターフェースとDAO実装クラスは次のようになります。

## EmployeeDao.cs

C#

    using System;
    using System.Data;
    using Seasar.Extension.ADO;

    namespace Seasar.Examples.Reference.DTCTx
    {
        // 従業員を扱うためのDAOインターフェース
        public interface IEmployeeDao
        {
            void Insert();
        }

        // 従業員を扱うためのDAO実装クラス
        public class EmployeeDaoImpl : IEmployeeDao
        {
            private IDataSource dataSource;

            // DataSourceImplをインジェクションしてもらうためのプロパティ
            public IDataSource DataSource
            {
                set { dataSource = value; }
            }

            #region IEmployeeDao メンバ

            public void Insert()
            {
                using(IDbConnection cn = dataSource.GetConnection())
                {
                    cn.Open();
                    string sql =
                        "insert into emp2 values(@empno, @ename, @deptnum)";
                    using(IDbCommand cmd = dataSource.GetCommand(sql,cn))
                    {
                        cmd.Parameters.Add(dataSource.GetParameter("@empno", 99));
                        cmd.Parameters.Add(dataSource.GetParameter(
                            "@ename", "Sugimoto"));
                        cmd.Parameters.Add(dataSource.GetParameter(
                            "@deptnum", 31));
                        cmd.ExecuteNonQuery();
                        Console.WriteLine("EMPを追加しました。");
                    }
                }
            }

            #endregion
        }
    }

VB

    Imports System
    Imports System.Data
    Imports Seasar.Extension.ADO

    Namespace Seasar.Examples.Reference.DTCTx

       ' 従業員を扱うためのDAOインターフェース
       Public Interface IEmployeeDao
          Sub Insert()
       End Interface

       ' 従業員を扱うためのDAO実装クラス
       Public Class EmployeeDaoImpl
          Implements IEmployeeDao

          Private dataSource As IDataSource

          ' DataSourceImplをインジェクションしてもらうためのプロパティ
          Public WriteOnly Property DataSource() As IDataSource
             Set(ByVal value As IDataSource)
                dataSource = value
             End Set
          End Property

          Public Sub Insert()

             Dim cn As IDbConnection = dataSource.GetConnection()
             Try
                cn.Open()
                Dim sql As String = _
                    "insert into emp2 values(@empno, @ename, @deptnum)"
                Dim cmd As IDbCommand = dataSource.GetCommand(sql, cn)
                Try
                   cmd.Parameters.Add(dataSource.GetParameter("@empno", 99))
                   cmd.Parameters.Add(dataSource.GetParameter( _
                       "@ename", "Sugimoto"))
                   cmd.Parameters.Add(dataSource.GetParameter("@deptnum", 31))
                   cmd.ExecuteNonQuery()
                   Console.WriteLine("EMPを追加しました。")
                Finally
                   cmd.Dispose()
                End Try
             Finally
                cn.Dispose()
             End Try

          End Sub

       End Class

    End Namespace

## DTCTxClient.cs

IEmployeeDaoを実行するクラスは次のようになります。

C#

    using System;
    using Seasar.Framework.Container;
    using Seasar.Framework.Container.Factory;

    namespace Seasar.Examples.Reference.DTCTx
    {
        public class DTCTxClient
        {
            private static readonly String PATH
                    = "Seasar.Examples/Reference/DTCTx/DTCTxClient.dicon";

            public DTCTxClient() {}

            public void Main()
            {
                // S2コンテナを作成します
                IS2Container container = S2ContainerFactory.Create(PATH);
                // S2コンテナを初期化します
                container.Init();

                // S2コンテナからIEmployeeDaoを実装するコンポーネントを取り出します
                IEmployeeDao dao = (IEmployeeDao)
                    container.GetComponent(typeof(IEmployeeDao));

                // 従業員を追加します
                dao.Insert();
            }
        }
    }

VB

    Imports System
    Imports Seasar.Framework.Container
    Imports Seasar.Framework.Container.Factory

    Namespace Seasar.Examples.Reference.DTCTx

       Public Class DTCTxClient
          Private Shared PATH As [String] = _
             "Seasar.Examples/Reference/DTCTx/DTCTxClient.dicon"

          Public Sub New()
          End Sub

          Public Sub Main()

             ' S2コンテナを作成します
             Dim container As IS2Container = S2ContainerFactory.Create(PATH)
             ' S2コンテナを初期化します
             container.Init()

             ' S2コンテナからIEmployeeDaoを実装するコンポーネントを取り出します
             Dim dao As IEmployeeDao = _
                CType(container.GetComponent(GetType(IEmployeeDao)), IEmployeeDao)

             ' 従業員を追加します
             dao.Insert()

          End Sub

       End Class

    End Namespace

diconファイルは次のようになります。 Required属性のトランザクションを適用するために、 EmployeeDaoImplのaspectにDTCTx.RequiredTxを指定します。

## DTCTxClient.dicon

    <?xml version="1.0" encoding="utf-8" ?>
    <!DOCTYPE components PUBLIC "-//SEASAR2.1//DTD S2Container//EN"
    "http://www.seasar.org/dtd/components21.dtd">
    <components>
        <include path="Seasar.Examples/DTCTx.dicon" />

        <component name="DTCTxClient"
            class="Seasar.Examples.Reference.DTCTx.DTCTxClient" />

        <component class="Seasar.Examples.Reference.DTCTx.EmployeeDaoImpl">
            <aspect>DTCTx.RequiredTx</aspect>
        </component>

    </components>

## DTCTx.dicon

    <?xml version="1.0" encoding="utf-8" ?>
    <!DOCTYPE components PUBLIC "-//SEASAR2.1//DTD S2Container//EN"
    "http://www.seasar.org/dtd/components21.dtd">
    <components namespace="DTCTx">
        <include path="Seasar.Examples/Ado.dicon" />

        <component name="DTCTransactionStateHandler"
                class="Seasar.Extension.Tx.Impl.DTCTransactionStateHandler" />

        <component name="RequiredTx"
                class="Seasar.Extension.Tx.TransactionInterceptor">
            <arg>
                <component class="Seasar.Extension.Tx.Impl.DTCRequiredTxHandler" />
            </arg>
        </component>

        ...

    </components>

Ado.diconと実行結果は、ローカルトランザクションの場合と同じです。

# System.Transactionsによるトランザクション

上へ

ローカルトランザクションのようにTxDataSourceからコネクションをとらないといけないといった制限はありませんが、 データプロバイダに依存しないコーディングをするために、Seasar.Extension.ADO.Impl.DataSourceImpl からコネクション、コマンド、パラメータのオブジェクトを作成します。

System.Transactionsによるトランザクションを利用する場合は、 1回のクエリ発行毎にコネクションをクローズしても、トランザクションは維持されます。

従業員を扱うためのDAOインターフェースとDAO実装クラスは次のようになります。

## EmployeeDao.cs

C#

    using System;
    using System.Data;
    using Seasar.Extension.ADO;

    namespace Seasar.Examples.Reference.Tx
    {
        // 従業員を扱うためのDAOインターフェース
        public interface IEmployeeDao
        {
            void Insert();
        }

        // 従業員を扱うためのDAO実装クラス
        public class EmployeeDaoImpl : IEmployeeDao
        {
            private IDataSource dataSource;

            // DataSourceImplをインジェクションしてもらうためのプロパティ
            public IDataSource DataSource
            {
                set { dataSource = value; }
            }

            #region IEmployeeDao メンバ

            public void Insert()
            {
                using(IDbConnection cn = dataSource.GetConnection())
                {
                    cn.Open();
                    string sql =
                        "insert into emp2 values(@empno, @ename, @deptnum)";
                    using(IDbCommand cmd = dataSource.GetCommand(sql,cn))
                    {
                        cmd.Parameters.Add(dataSource.GetParameter("@empno", 99));
                        cmd.Parameters.Add(dataSource.GetParameter(
                            "@ename", "Sugimoto"));
                        cmd.Parameters.Add(dataSource.GetParameter("@deptnum", 31));
                        cmd.ExecuteNonQuery();
                        Console.WriteLine("EMPを追加しました。");
                    }
                }
            }

            #endregion
        }
    }

VB

    Imports System
    Imports System.Data
    Imports Seasar.Extension.ADO

    Namespace Seasar.Examples.Reference.Tx

       ' 従業員を扱うためのDAOインターフェース
       Public Interface IEmployeeDao
          Sub Insert()
       End Interface

       ' 従業員を扱うためのDAO実装クラス
       Public Class EmployeeDaoImpl
          Implements IEmployeeDao

          Private dataSource As IDataSource

          ' DataSourceImplをインジェクションしてもらうためのプロパティ
          Public WriteOnly Property DataSource() As IDataSource
             Set(ByVal value As IDataSource)
                dataSource = value
             End Set
          End Property

          Public Sub Insert()

             Dim cn As IDbConnection = dataSource.GetConnection()
             Try
                cn.Open()
                Dim sql As String = _
                    "insert into emp2 values(@empno, @ename, @deptnum)"
                Dim cmd As IDbCommand = dataSource.GetCommand(sql, cn)
                Try
                   cmd.Parameters.Add(dataSource.GetParameter("@empno", 99))
                   cmd.Parameters.Add(dataSource.GetParameter("@ename", "Sugimoto"))
                   cmd.Parameters.Add(dataSource.GetParameter("@deptnum", 31))
                   cmd.ExecuteNonQuery()
                   Console.WriteLine("EMPを追加しました。")
                Finally
                   cmd.Dispose()
                End Try
             Finally
                cn.Dispose()
             End Try

          End Sub

       End Class

    End Namespace

IEmployeeDaoを実行するクラスは次のようになります。

## TxClient.cs

C#

    using System;
    using Seasar.Framework.Container;
    using Seasar.Framework.Container.Factory;

    namespace Seasar.Examples.Reference.Tx
    {
        public class TxClient
        {
            private static readonly String PATH
                    = "Seasar.Examples/Reference/Tx/TxClient.dicon";

            public TxClient() {}

            public void Main()
            {
                // S2コンテナを作成します
                IS2Container container = S2ContainerFactory.Create(PATH);
                // S2コンテナを初期化します
                container.Init();

                // S2コンテナからIEmployeeDaoを実装するコンポーネントを取り出します
                IEmployeeDao dao = (IEmployeeDao)
                    container.GetComponent(typeof(IEmployeeDao));

                // 従業員を追加します
                dao.Insert();
            }
        }
    }

VB

    Imports System
    Imports Seasar.Framework.Container
    Imports Seasar.Framework.Container.Factory

    Namespace Seasar.Examples.Reference.Tx

       Public Class TxClient
          Private Shared PATH As [String] = _
             "Seasar.Examples/Reference/Tx/TxClient.dicon"

          Public Sub New()
          End Sub

          Public Sub Main()

             ' S2コンテナを作成します
             Dim container As IS2Container = S2ContainerFactory.Create(PATH)
             ' S2コンテナを初期化します
             container.Init()

             ' S2コンテナからIEmployeeDaoを実装するコンポーネントを取り出します
             Dim dao As IEmployeeDao = _
                CType(container.GetComponent(GetType(IEmployeeDao)), IEmployeeDao)

             ' 従業員を追加します
             dao.Insert()

          End Sub

       End Class

    End Namespace

diconファイルは次のようになります。Required属性のトランザクションを適用するために、 EmployeeDaoImplのaspectにTx.RequiredTxを指定します。

## TxClient.dicon

    <?xml version="1.0" encoding="utf-8" ?>
    <!DOCTYPE components PUBLIC "-//SEASAR2.1//DTD S2Container//EN"
    "http://www.seasar.org/dtd/components21.dtd">
    <components>
        <include path="Seasar.Examples/Tx.dicon" />

        <component name="TxClient"
                class="Seasar.Examples.Reference.Tx.TxClient" />

        <component class="Seasar.Examples.Reference.Tx.EmployeeDaoImpl">
            <aspect>Tx.RequiredTx</aspect>
        </component>

    </components>

## Tx.dicon

    <?xml version="1.0" encoding="utf-8" ?>
    <!DOCTYPE components PUBLIC "-//SEASAR2.1//DTD S2Container//EN"
    "http://www.seasar.org/dtd/components21.dtd">
    <components namespace="Tx">
        <include path="Seasar.Examples/Ado.dicon" />

        <component name="TransactionStateHandler"
                class="Seasar.Extension.Tx.Impl.TransactionStateHandler" />

        <component name="RequiredTx"
                class="Seasar.Extension.Tx.TransactionInterceptor">
            <arg>
                <component class="Seasar.Extension.Tx.Impl.RequiredTxHandler" />
            </arg>
        </component>

        ...

    </components>

Ado.diconと実行結果は、ローカルトランザクションの場合と同じです。

S2Container.NETには、Tx.dicon, LocalTx.dicon, DTCTx.diconのサンプルがあらかじめ用意(s2container.net/source/Seasar.Tests)されています。 必要に応じて、ユーザアプリケーションのプロジェクトにコピーしてください。

Adviceのコンポーネント名をaspectタグのボディに指定するだけなので簡単です。 簡単にトランザクション管理機能が組み込めることがわかって頂けたと思います。

# 例外発生時の動作

上へ

Tx.dicon、LocalTx.dicon、DTCTx.dionに定義されているAdviceは、 コンポーネントが例外をスローした場合はトランザクションをロールバックします。

© Copyright The Seasar Project and the others 2004-2009, all rights reserved.