2009年9月2日水曜日

Monoで他のDBも使ってみたよ!+(db4o、NHibernate編)4

8.NHibernate編

これまで、主にRDBMSを対象にしたDBアクセスについて解説を行いましたが、NHibernateはRDBMSではありません。

 NHibernateはオブジェクト関係(O/R)マッピングフレームワークと言われるもので、言い換えれば、NHibernateを経由したDBアクセスについて解説を行う、ということになります。

 Wikipediaでは、オブジェクト関係マッピングについて
データベースとオブジェクト指向プログラミング言語の間の非互換なデータを変換するプログラミング技法である。
と解説しています。もう少し簡単に言うとしたら、RDBへのアクセスをあたかもdb4oのようなODBへのアクセスのように行う方法、でしょうか。

 NHibernateではマッピング先のRDB上のテーブルへのデータ登録を次の様なコードで実現しています。
  1. Configuration cfg       = new Configuration();  
  2. cfg.Configure();  
  3. ISessionFactory factory = cfg.BuildSessionFactory();  
  4. ISession session        = factory.OpenSession();  
  5. Product product         = new Product{ Id = 1, Name = "Clamp", Price = 12.48M, Description = "Workbench clamp" };  
  6. session.Save(product);  
また、登録したデータの検索は
  1. var result = session.Get<Product>(1);  
で可能です。

 NHibernateは複数のRDBMSをサポートし、これまでの記事で使用したすべてのRDBMSで使用可能です。本稿では、NHibernateを使用した、RDBMSへのDBアクセスについて解説を行います。NHibernateについてはWikipediaなどを参照して下さい。

セットアップ

NHibernateの動作に必要なアセンブリの導入を行います。各RDBMSのセットアップについては、これまでの記事を参照して下さい。ただ、本稿での動作環境は、以前と異なる部分があるので、セットアップ上の変更点については、「おまけ」の節で解説します。

アセンブリの導入

NHibernateはSourceForgeのNHibernateプロジェクトページからダウンロード可能です。現時点(2009.09.02)の最新版は2.1.0.GAです(NHibernate 2.1.0はHibernate 3.2.6に相当)。

 「NHibernate-2.1.0.GA-bin.zip」を任意のディレクトリで展開し、次のアセンブリをGACへインストールまたは環境変数MONO_PATHに設定されているディレクトリへコピーします。

[必要なアセンブリ]
アセンブリ概要
NHibernate.dllNHibernate Service Provider
Iesi.Collections.dllrequired from NHibernate
log4net.dllrequired from NHibernate
NHibernate.ByteCode.LinFu.dlneed for proxyfactory.factory_class
LinFu.DynamicProxy.dllneed for proxyfactory.factory_class

[導入例(MONO_PATH)]
$ echo $MONO_PATH
/home/sta/lib
$ unzip NHibernate-2.1.0.GA-bin.zip -d NHibernate-2.1.0.GA-bin/
$ cd NHibernate-2.1.0.GA-bin/Required_Bins
$ cp *.dll ~/lib
$ cd ../Required_For_LazyLoading/LinFu
$ cp *.dll ~/lib

コンフィグファイル

RDBMSへの接続情報等を設定します。「NHibernate-2.1.0.GA-bin.zip」には、各RDBMS用NHibernateコンフィグファイルのテンプレートが含まれています(「展開先/Configuration_Templates」配下)。それらを参考にして、各RDBMS用のコンフィグファイルを用意します(以下は、SQLiteを対象にした設定例になります)。

 作業する上でのディレクトリ構造は、下表の通りとします。

ディレクトリファイル
~/src/cs/NHibernateExample/DriverMonoDataSqliteDriver.cs
~/src/cs/NHibernateExample/TestSQLite.cfg.xml

[NHibernateコンフィグファイル例(SQLite.cfg.xml:SQLite用)]
  1. <?xml version="1.0" encoding="utf-8" ?>  
  2. <hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">  
  3.   <session-factory name="NHibernateExample">  
  4.     <property name="connection.driver_class">NHibernateExample.Driver.MonoDataSqliteDriver, NHibernateExample</property>  
  5.     <property name="connection.connection_string">Data Source=/home/sta/data/sqlite/TestData.db</property>  
  6.     <property name="show_sql">false</property>  
  7.     <property name="dialect">NHibernate.Dialect.SQLiteDialect</property>  
  8.     <property name="query.substitutions">true=1;false=0</property>  
  9.     <property name="proxyfactory.factory_class">NHibernate.ByteCode.LinFu.ProxyFactoryFactory, NHibernate.ByteCode.LinFu</property>  
  10.     <mapping file="Product.hbm.xml" />  
  11.   </session-factory>  
  12. </hibernate-configuration>  
コンフィグファイルの読み込みは、
  1. Configuration cfg = new Configuration();  
  2. cfg.Configure();  
とした場合、実行プログラムと同じディレクトリにある「hibernate.cfg.xml」が読み込まれ、
  1. cfg.Configure("SQLite.cfg.xml");  
とした場合、同じディレクトリにある任意のファイル「SQLite.cfg.xml」が読み込まれます。

 SQLite用コンフィグファイルのテンプレートでは、SQLite用データプロバイダを使用するためのドライバとして、「NHibernate.Driver.SQLiteDriver」が設定されていますが、同ドライバで呼び出しているのは「SQLite.NET」であり、Monoで標準提供されている「Mono.Data.Sqlite」ではありません。ということで、次のような「Mono.Data.Sqlite」用のドライバを用意する必要があります。

[Mono.Data.Sqlite用ドライバ(MonoDataSqliteDriver.cs)]
  1. using System;  
  2. using NHibernate.Driver;  
  3.   
  4. namespace NHibernateExample.Driver  
  5. {  
  6.   public class MonoDataSqliteDriver : ReflectionBasedDriver  
  7.   {  
  8.     public MonoDataSqliteDriver() : base(  
  9.       "Mono.Data.Sqlite",  
  10.       "Mono.Data.Sqlite.SqliteConnection",  
  11.       "Mono.Data.Sqlite.SqliteCommand")  
  12.     {  
  13.     }  
  14.   
  15.     public override bool UseNamedPrefixInSql  
  16.     {  
  17.       get { return true; }  
  18.     }  
  19.   
  20.     public override bool UseNamedPrefixInParameter  
  21.     {  
  22.       get { return true; }  
  23.     }  
  24.   
  25.     public override string NamedPrefix  
  26.     {  
  27.       get { return "@"; }  
  28.     }  
  29.   
  30.     public override bool SupportsMultipleOpenReaders  
  31.     {  
  32.       get { return false; }  
  33.     }  
  34.   }  
  35. }  
また、NHibernateではデータプロバイダの呼び出しにReflection(System.Reflection.Assembly.Load)を使用しており、GACにインストールされているアセンブリを呼び出すために、App.config(foo.exe.config)に次の設定が必要になりますが、
  1. <?xml version="1.0" encoding="utf-8" ?>  
  2. <configuration>  
  3.   <runtime>  
  4.     <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">  
  5.       <qualifyAssembly partialName="Mono.Data.Sqlite"  
  6.         fullName="Mono.Data.Sqlite, version=2.0.0.0, publicKeyToken=0738eb9f132ed756, culture=neutral" />  
  7.     </assemblyBinding>  
  8.   </runtime>  
  9. </configuration>  
上記設定はMono 2.4上では有効になりませんでした(未対応のようです)。そこで、必要なアセンブリのリンクをMONO_PATHで設定されているディレクトリに作成して、動作確認を行いました。

$ cd ~/lib
$ ln -s /opt/mono/2.4/lib/mono/2.0/Mono.Data.Sqlite.dll .

マッピング

次のようなProductsテーブルに関連付けられる、Productクラス、NHibernateマッピングファイルを用意します。

ディレクトリファイル
~/src/cs/NHibernateExampleProduct.cs
~/src/cs/NHibernateExample/TestProduct.hbm.xml

[Productsテーブル]
  1. create table Products(  
  2.   ProductID int not null primary key,  
  3.   ProductName text not null,  
  4.   Price numeric null,  
  5.   ProductDescription text null);  

[Productクラス(Product.cs)]
  1. namespace NHibernateExample  
  2. {  
  3.   public class Product  
  4.   {  
  5.     int      _id;  
  6.     string   _name;  
  7.     decimal? _price;  
  8.     string   _description;  
  9.   
  10.     public virtual int Id  
  11.     {  
  12.       get { return _id; }  
  13.       set { _id = value; }  
  14.     }  
  15.   
  16.     public virtual string Name  
  17.     {  
  18.       get { return _name; }  
  19.       set { _name = value; }  
  20.     }  
  21.   
  22.     public virtual decimal? Price  
  23.     {  
  24.       get { return _price; }  
  25.       set { _price = value; }  
  26.     }  
  27.   
  28.     public virtual string Description  
  29.     {  
  30.       get { return _description; }  
  31.       set { _description = value; }  
  32.     }  
  33.   
  34.     public override string ToString()  
  35.     {  
  36.       return "ID:" + _id + " NAME:" + _name + " PRICE:" + _price + " DESCRIPTION:" + _description;  
  37.     }  
  38.   }  
  39. }  

[NHibernateマッピングファイル(Product.hbm.xml)]
  1. <?xml version="1.0" encoding="utf-8" ?>  
  2. <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"   
  3.   assembly="NHibernateExample"   
  4.   namespace="NHibernateExample">  
  5.   
  6.   <class name="Product" table="Products">  
  7.     <id name="Id" column="ProductID">  
  8.       <generator class="assigned" />  
  9.     </id>  
  10.     <property name="Name" column="ProductName" not-null="true" />  
  11.     <property name="Price" column="Price" />  
  12.     <property name="Description" column="ProductDescription" />  
  13.   </class>  
  14.   
  15. </hibernate-mapping>  
マッピングファイルはNHibernateコンフィグファイル内で指定し、
  1. <mapping file="Product.hbm.xml" />  
Productクラスはライブラリとしてビルドします。

$ gmcs -t:library -r:NHibernate.dll -out:./Test/NHibernateExample.dll Product.cs -recurse:./Driver/*.cs

 Monoで他のDBも使ってみたよ!+(db4o、NHibernate編)5 へ続く。

0 件のコメント:

コメントを投稿