はじめてのS2Dao.NET

S2Dao.NETとは

S2Dao.NETマッピング情報をXMLに記述しないO/Rマッピングフレームワークです。 S2Container.NET上で動作します。

マッピングのためにXMLガリガリ書くのが面倒なので嬉しいところ。というわけで、Oracleに接続して単純なCRUDを行うサンプルを書いてみました。
先ずは対象となるテーブルから。

create table Greeting(
    id  integer,
    message varchar (128)
);

-- IDを主キーとする
alter table Greeting add constraint Greeting_P primary key (id);

-- シーケンスを作成する
create sequence GreetingID;

主キーとメッセージだけの単純なテーブルです。これをSQL*Plusなどで実行してください。

次にテーブルのマッピング先となるクラスです。

using System;
using Seasar.Dao.Attrs;

// Entity
[Table("Greeting")]
public class Greeting {
    private int id;
    private string message;

    [ID ("sequence", "GreetingID")]
    public int ID {
        get { return id; }
        set { id = value; }
    }

    public string Message {
        get { return message; }
        set { message = value; }
    }

    public override string ToString () {
        return String.Format ("{0}:{1}", ID, Message);
    }
}

Table/IDという属性がついている以外は普通のクラスですね。Table属性はデータベースのテーブル名を指定します。クラス名とテーブル名が同じ場合は不要だった気がしますが、自信が無かったため付けてしまいました。(^^; ID属性ではシーケンスを使うことと、そのときに使用するシーケンスオブジェクト名を指定しています。今回はS2Dao.NETに自動的にIDを付けさせますが、手動の場合はID属性は不要です。

さて、クラスとテーブルが出来ましたので次は両者のマッピングですが、DAO(Data Access Object)を作成します。作成と言っても実は、インタフェースを定義するだけで、実装はS2Dao.NETが勝手にやってくれます。AOP万歳。

で、そのDAOのインタフェースですが、

using System;
using System.Collections;
using Seasar.Dao.Attrs;

[Bean (typeof (Greeting))]
public interface IGreetingDao {
    int InsertGreeting (Greeting greeting);
    int UpdateGreeting (Greeting greeting);
    int DeleteGreeting (Greeting greeting);

    IList GetAllList ();

    [Query ("id=/*id*/")]
    Greeting GetGreetingByID (int id);
}

こんな感じ。Bean属性で対象となるクラスを指定します。Insertクラス名/Updateクラス名/Deleteクラス名などのメソッド名はルールなので合わせてください。また、Query属性で指定した内容はwhere節に埋め込まれます。コメントとなっている変数にメソッドの引数が展開されます。

さて、準備は出来ましたのでDIしてみましょう。

構成ファイルは以下のようになります。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <configSections>
        <section name="seasar"
            type="Seasar.Framework.Xml.S2SectionHandler, Seasar" />
    </configSections>

    <appSettings>
        <!-- oracleex.diconで使用 -->
        <add key="ConnectionString" value="Data Source=mydb;User Id=user;Password=pass;" />
    </appSettings>

    <seasar>
        <assemblys>
            <assembly>Seasar.Dao</assembly>
            <assembly>System.Data.OracleClient, version=2.0.0.0, 
                Culture=neutral, PublicKeyToken=b77a5c561934e089</assembly>
        </assemblys>
    </seasar>
</configuration>

assemblyとしてSeasar.DaoとSystem.Data.OracleClientを追加します。また、appSettingsで接続文字列を設定してみました。

次に、app.diconファイルです。

<?xml version="1.0" encoding="utf-8" ?> 
<!DOCTYPE components PUBLIC "-//SEASAR2.1//DTD S2Container//EN"
"http://www.seasar.org/dtd/components21.dtd">
<components>
    <!-- S2Dao.NETでOracleに接続 -->
    <include path="OracleEx.dicon" />

    <component class="IGreetingDao" name="dao">
        <aspect>OracleEx.DaoInterceptor</aspect>
    </component>

</components>

aspectで指定されているOracleEx.DaoInterceptorがミソで、コイツがIGreetingDaoインタフェースの実装を自動生成してくれます。

Oracleを使うにはもう少し設定が必要になりますが、OracleEx.diconの中にまとめてしまい、他からインクルードするだけになっています。OracleEx.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="OracleEx">
    <!-- データプロバイダ -->
    <component name="Oracle" class="Seasar.Extension.ADO.DataProvider">
        <property name="ConnectionType">"System.Data.OracleClient.OracleConnection"</property>
        <property name="CommandType">"System.Data.OracleClient.OracleCommand"</property>
        <property name="ParameterType">"System.Data.OracleClient.OracleParameter"</property>
        <property name="DataAdapterType">"System.Data.OracleClient.OracleDataAdapter"</property>
    </component>

    <!-- データソース -->
    <component name="SqlDataSource" class="Seasar.Extension.Tx.Impl.TxDataSource">
    <property name="DataProvider">Oracle</property>
        <!-- 接続文字列は構成ファイルから取得 -->
        <property name="ConnectionString"> appSettings['ConnectionString']</property>
    </component>

     <!-- DaoInterceptor -->
    <component class="Seasar.Extension.ADO.Impl.BasicDataReaderFactory" />
    <component class="Seasar.Extension.ADO.Impl.BasicCommandFactory" />
    <component class="Seasar.Dao.Impl.DaoMetaDataFactoryImpl" />
    <component name="DaoInterceptor" class="Seasar.Dao.Interceptors.S2DaoInterceptor"/>

    <!-- ローカルトランザクション用のインターセプター -->
    <component name="LocalRequiredTx" class="Seasar.Extension.Tx.TransactionInterceptor">
        <arg><component class="Seasar.Extension.Tx.Impl.LocalRequiredTxHandler" /></arg>
        <property name="TransactionStateHandler">TransactionContext</property>
    </component>

    <!-- 分離レベルの指定 -->
    <component name="TransactionContext" class="Seasar.Extension.Tx.Impl.TransactionContext">
        <property name="IsolationLevel">System.Data.IsolationLevel.ReadCommitted</property>
    </component>
 
</components>

接続文字列を構成ファイルに出したので、あまり見ることはないでしょう。

準備も整ったので、これらを使ってOracleにアクセスしてみましょう。

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

public class App {
    public static void TestSelect (IGreetingDao dao) {
        IList list = dao.GetAllList ();
        if (list.Count > 0) 
            foreach (Greeting g in dao.GetAllList ()) 
                Console.WriteLine (g);
        else 
            Console.WriteLine ("Greeting is empty.");
    }

    public static void TestInsert (IGreetingDao dao) {
        Console.WriteLine ("Insert");
        Greeting entity = new Greeting ();
        entity.Message = "Hello";
        dao.InsertGreeting (entity);
        entity.Message = "Goodby";
        dao.InsertGreeting (entity);
    }

    public static void TestUpdate (IGreetingDao dao) {
        Console.WriteLine ("Update");
        foreach (Greeting g in dao.GetAllList ()) {
            g.Message += ", World";
            dao.UpdateGreeting (g);
        }
    }

    public static void TestDelete (IGreetingDao dao) {
        Console.WriteLine ("Delete");
        foreach (Greeting g in dao.GetAllList ()) 
            dao.DeleteGreeting (g);
    }

    public static void Main () {
        SingletonS2ContainerFactory.Init ();
        IS2Container container = SingletonS2ContainerFactory.Container;

        IGreetingDao dao = container.GetComponent (typeof(IGreetingDao)) 
            as IGreetingDao;

        TestSelect (dao);
        TestInsert (dao);
        TestSelect (dao);
        TestUpdate (dao);
        TestSelect (dao);
        TestDelete (dao);
        TestSelect (dao);
    }
}

/*
Greeting is empty.
Insert
5:Hello
6:Goodby
Update
5:Hello, World
6:Goodby, World
Delete
Greeting is empty.
 */

SQL無しでデータベースアクセスが出来るようになりましたね。