はじめに
データベースにアクセスするアプリケーションの開発で、個々のテーブルに対してSQLを書く作業が繰り返しで冗長だと感じたことはありませんでしょうか。本稿では、このような問題の解決に有効な「NHibernate」というオープンソースのプロダクトについて紹介します。NHibernateは、SQLを書かなくともテーブルのデータとクラスをシームレスに変換させるO/Rマッピング(Object/Relational Mapping)を実現できるため、コーディングの量を軽減することが可能になります。
対象読者
.NETにて開発を行っている方を対象としています。
必要な環境
サンプルはVisual Studio .NET 2003で作成され、.NET Framework 1.1、NHibernate 0.91で動作確認をしています。
NHibernateの概要と入手方法
NHibernateはO/Rマッピングを行うオープンソースのライブラリです。NHibernateは、軽快な動作と使い勝手の良さから人気が上がっているJavaの「Hibernate」を.NETへ移植したものです。NHibernateは、別稿でご紹介したiBATIS.NETと異なり、SQLを書かなくともデータの取得や更新を行うことができます。またHQL(Hibernate Query Language)と呼ばれるSQLに似た独自のオブジェクトクエリ言語によって、集計関数やサブクエリをサポートするなどと、柔軟性が高いことが特徴です。
NHibernateを入手するにはWebサイトの「Download NHibernate」のリンクからダウンロードしてください。なお執筆時点での最新バージョンは0.91です。
NHibernateの設定
Visual Studioから使用する場合、ソリューションエクスプローラの[参照設定]を右クリックして、[参照の追加]から「NHibernate.dll」を選択します。NHibernateは、
といった他のライブラリを使用しますので、これらのファイルを「NHibernate.dll」と同じフォルダに配置するようにしてください。
サンプルアプリケーションについて
サンプルは、書籍マスタのメンテナンスを行うWindowsアプリケーションです。[デバッグ]メニューの[開始]にて画面が起動します。
このサンプルは、MDB(Microsoft Accessのデータベース形式)にアクセスし、データの追加・更新・参照・削除を行います。なお「NHibernate.dll(nhibernate-0.9.1.0.zip)」が同梱されているため、改めてNHibernateを入手する必要はありません。
このサンプルに登場する主要なクラス・テーブル・設定ファイルは下図の通りです。
サンプルのテーブルレイアウト
書籍マスタ(BOOK)のレイアウトは以下の通りです。
列名 | 型 | 桁数 | 説明 |
ISBN | 文字列型 | 10 | 図書コード(主キー) |
TITLE | 文字列型 | 50 | 書籍名 |
SALE_DATE | 日付時刻型 | 8 | 発売日 |
PRICE | 数値型 | 8 | 価格 |
AUTHOR_ID | 数値型 | 8 | 著者コード |
著者(AUTHOR)マスタのレイアウトは以下の通りです。
列名 | 型 | 桁数 | 説明 |
ID | 数値型 | 8 | 著者コード(主キー) |
NAME | テキスト型 | 50 | 著者名 |
TEL | テキスト型 | 15 | 電話番号 |
REG_DATE | 日付時刻型 | 8 | 登録日 |
書籍マスタのAUTHOR_ID
カラムには、著者マスタの「ID」が設定されています。
「NHibernate」のマッピング準備
NHibernateを動作させるためには、以下の手順で実装を行います。
- マッピング対象クラスの作成
- マッピングファイル(「*.hbm.xml」)の作成
- アプリケーション構成ファイルの記述
Book
クラスとAuthor
クラスを作成します。マッピング対象クラス「Book.cs」の作成
まず「BOOK」テーブルのデータを格納するBook
クラスを作成します。Book
クラスは「BOOK」テーブルと同じ属性を持つシンプルなクラスです。
public class Book { private string _Isbn; private string _Title; private DateTime _SaleDate; private int _Price; private Author _Author; public string Isbn { get{ return _Isbn; } set{_Isbn = value; } } //(以下同様にget/set設定) }
「BOOK」テーブルから読み込まれたデータがBook
クラスにセットされ、Book
クラスのプロパティの値が「BOOK」テーブルに反映されます。(Book
クラスと同様にAuthor
クラスも作成します。)
「Book.hbm.xml」にマッピング定義を記述
「Book.hbm.xml(クラス名+.hbm.xml)」というファイルを作成し、クラスとテーブルの関係(マッピング)を定義します。
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.0"> <class name="NHibernateSample.Domain.Books.Book, NHibernateSample" table="BOOK"> <id name="Isbn"> <generator class="assigned" /> </id> <property name="Title" /> <property name="Price" /> <property name="SaleDate" column="SALE_DATE" /> <many-to-one name="Author" column="AUTHOR_ID" /> </class> </hibernate-mapping>
対象クラスとテーブルの紐づけ
「<class name="
クラス名,
アセンブリ名" table="
テーブル名" />
」という形式でマッピングするクラスとテーブルを定義します。このサンプルでは、NHibernateSample.Domain.Books.Book
クラスと「BOOK」テーブルを紐づけています。
プロパティとカラムのマッピング
「<property name="
プロパティ名" column="
カラム名" />
」という形式で、クラスのプロパティとテーブルのカラムのマッピングを定義します。このサンプルでは、『「BOOK」テーブルのSALE_DATE
カラム』と『book
クラスのSaleDate
プロパティ』を紐づけています。なおTitle
やPrice
のようにプロパティ名とカラム名が同じ場合、カラム名は省略できます。
主キー列の定義
主キー列Isbn
の定義を行います。設定方法は「property」と同じです。ただし、主キー値の生成方法を「<generator class="
IDジェネレーター名" />
」という子要素にて指定する必要があります。サンプルアプリケーションでは、プログラムから値を設定するため「assigned
(アプリケーションでIDを生成する方式)」としています。なお、他のIDジェネレータの例としては、
- 「
increment
(long
,short
,int
型の数値をインクリメンタルに生成)」 - 「
identity
(SQLServerやMySQLのidentity
列の使用)」 - 「
sequence
(OracleやPostgreSQLのsequence
の利用)」 - 「
hilo
(hi/loアルゴリズムによるID生成)」 - 「
uuid.hex
(128ビットUUIDアルゴリズムによるID生成)」
などがありますので状況に応じて使い分けてください。
関連の設定
Book
クラスのプロパティがAuthor
クラスであるため、これらの関連を設定します。サンプルのようにトランザクションデータからマスタを参照するような関連は「N対1の関係」と呼び、「<many-to-one name="
プロパティ名" column="
カラム名" />
」という形式で定義します。この設定によりBook
クラスの読み込み時に、対応するAuthor
クラスも読み込まれるようになります。
なおNHibernateは「1対Nの関係(プロパティにコレクションを持つような場合)」や「1対1の関係(テーブル分割など同じキーを持つ場合)」といった関連もサポートしています。
マッピングファイルの設定の詳細については、NHibernate documentation Chapter 3もしくはHibernateの日本語訳 第5章が参考になるでしょう。
アプリケーション構成ファイルにデータベース情報を記述
アプリケーション構成ファイル「App.config」(ASP.NETの場合は「Web.config」)に、データベースの接続先に関する設定を行います。
<configSections> <section name="nhibernate" type="System.Configuration.NameValueSectionHandler, System, Version=1.0.5000.0,Culture=neutral, PublicKeyToken=b77a5c561934e089" /> </configSections> <nhibernate> <add key="hibernate.show_sql" value="true" /> <add key="hibernate.connection.provider" value="NHibernate.Connection.DriverConnectionProvider" /> <add key="hibernate.connection.driver_class" value="NHibernate.Driver.OleDbDriver" /> <add key="hibernate.dialect" value="NHibernate.Dialect.MsSql7Dialect" /> <add key="hibernate.connection.connection_string" value="Provider=Microsoft.Jet.OLEDB.4.0;User ID=Admin; Data Source=./NHibernateSample.mdb" /> </nhibernate>
NHibernateは執筆時点で下表のデータベースに対応していますので、接続先に応じて「driver_class
(使用するDriver
クラスを設定)」「dialect
(使用するDialect
を設定)」「connection_string
(データベースの接続文字列を設定)」を設定します。
データベース | 使用Driver | 使用Dialect | connection_string例 |
DB2 UDB | DB2Driver | DB2Dialect | DSN=SampleDb;UID=admin;PWD=pass |
Firebird | FirebirdDriver | FirebirdDialect | Database=C:\DB\SampleDb.GDB; User=admin;Password=pass;Dialect=3; Server=localhost |
Microsoft Access | OleDbDriver | MsSql7Dialect | Provider=Microsoft.Jet.OLEDB.4.0; User ID=admin; Data Source=./NHibernateSample.mdb |
MySQL | MySqlDataDriver | MySQLDialect | Server=server;Database=SampleDb; User ID=admin;Password=pass |
Oracle | OracleClientDriver(OracleDataClientDriver) | OracleDialect(Oracle9Dialect) | Data Source=SampleDb;User ID=admin;Password=pass; |
PostgreSQL | NpgsqlDriver | PostgreSQLDialect | Server=localhost;Database=SampleDb; User ID=admin;Password=pass; Encoding=UNICODE |
SQL Server(MSDE) | SqlClientDriver | MsSql7Dialect(MsSql2000Dialect) | Server=localhost;database=SampleDb; Password=pass;User ID=admin |
SQLite | SQLiteDriver | SQLiteDialect | Data Source=%APPDATA%\Aample\Data.sqlite.db; New=False;UTF8Encoding=True; Version=3 |
接続するデータベースの詳細設定については、NHibernate FAQを参考にすると良いでしょう。
「NHibernate」でデータを操作する
ここまでの手順でO/Rマッピングの準備は完了です。それでは、テーブルのデータを操作してみましょう。
NHibernateによるデータベース接続
まず、データベースに接続するため、Configuration
クラスとISessionFactory
インターフェースを使用して、アセンブリの登録とISession
の取得を行っています。
//NHibernateの接続設定 Configuration cfg = new Configuration(); //アセンブリの登録 cfg.AddAssembly("NHibernateSample"); ISessionFactory factory = cfg.BuildSessionFactory(); //セッションの取得 ISession session = factory.OpenSession();
NHibernateが提供する主要クラスを下表に整理しておきます。
クラス・インタフェース名 | 役割 |
NHibernate.Cfg.Configuration | マッピングファイルの読み込みとISessionFactoryの生成を行うクラス。 |
NHibernate.ISessionFactory | ISessionの生成を行うインターフェース。 |
NHibernate.ISession | データベース操作に関する中心的なインターフェース。 |
NHibernate.ITransaction | トランザクションを管理する。ISession.BeginTransaction()によって生成されるインターフェース。 |
データの挿入(INSERT)
ISession#BeginTransaction
を使用してトランザクションを開始した後、Book
インスタンスを生成し値を設定します。
//トランザクションの開始 ITransaction transction = session.BeginTransaction(); //オブジェクトに値をセット Book book = new Book(); book.Isbn = "4987654321"; book.Title = "NHibernate入門"; book.Price = 4500; book.SaleDate = new DateTime(2005,8,1); book.Author.Id = 3; session.Save(book); //書き込み(トランザクションをコミット) transction.Commit(); session.Close();
テーブルにデータを挿入(INSERT)するにはISession#Save
を使用します。トランザクションをコミット(ITransaction#Commit
)し、セッションを閉じることで一連の処理は完了となります。
データの取得・更新・削除(SELECT・UPDATE・DELETE)
続けてデータの取得・更新・削除を行います。
1件のデータを読み出す(SELECT)にはISession#Load
を使用します。Load
メソッドの引数には、読み出すクラスの型「typeof(Book)
」と検索するキーの値を指定します。
//ISBNが「4900003428」のBookデータを読み込み Book book = (Book)session.Load(typeof(Book), "4900003428"); //タイトルを変更 book.Title = "C#入門(改訂3版)"; //データを更新 session.Update(book);
更新(UPDATE)するには、更新したいプロパティに値を設定した後でISession#Update
を使用します。
session.Delete(book)
削除(DELETE)するには、book
クラスにキー項目を設定した後でISession#Delete
を使用します。
「NHibernate」で複数のデータを取得する
実際の開発案件では、データを1件表示するだけではなく、複数件表示する場合も多いのではないでしょうか。ここでは複数件のデータをコレクションに格納する2つの方法(「Criteria」と「HQL」)を紹介します。
Criteriaによるデータの取得
ISession#CreateCriteria
を使用することで複数件のデータを読み出すことができます。引数には、読み出すクラスの型を指定します。
//BOOKテーブルの全件取得 IList books = session.CreateCriteria(typeof(Book)).List(); //結果を出力 foreach (Book book in books) { Console.WriteLine(book.Title); }
上の例では、「BOOK」テーブルの全レコードが読み出されます。もし条件を付加したい場合にはExpression
クラスを使用します。
//価格(Price)が3000円以上(Greater than or Equal)のデータを抽出する場合 IList books = session.CreateCriteria(typeof(Book)). Add(Expression.Ge("Price",3000)).List(); //価格(Price)が2000円未満(Less Than)のデータを抽出する場合 IList books = session.CreateCriteria(typeof(Book)). Add(Expression.Lt("Price",2000)).List();
この例では3000円以上や2000円未満というような条件を付加しましたが、Expression
クラスには「Eq
」「In
」「IsNull
」「Like
」といったメソッドもありますので、必要に応じて条件を変更すると良いでしょう。
HQLによるデータの取得
Criteriaは便利ですが、複雑な条件でデータを抽出したい場合には、HQLを使用したほうが良いでしょう。HQLは「Hibernate Query Language」と呼ばれるSQLに似たHibernate独自のオブジェクトクエリー言語です。HQLにてデータを読み出すにはISession#Find
を使用します。
//タイトルの入門という文字が含まれるデータを抽出 string hql = "from Book b where b.Title like '%入門%' order by b.Title"; IList books = session.Find(hql);
この例ではFind
メソッドの引数にHQL形式でLIKE
句を記述して検索を行っています。HQLの詳細についてはHibernateの日本語訳 第11章が参考になるでしょう。
Tips(マッピングファイルのインテリセンス)
NHibernateではマッピングファイルの編集作業が多く発生します。開発効率を上げるために、Visual Studioでマッピングファイル「*.hbm.xml」のインテリセンス(補完機能)を効かせることができます。
- XMLスキーマの設定ファイル「nhibernate-mapping-2.0.xsd」を「<VisualStudioのインストールフォルダ>\Common7\Packages\schemas\xml」フォルダにコピーする。
- 補完させたいファイル名の拡張子を「*.XML」にする。
- XMLファイルの先頭に
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.0">
と記述します。
この設定によって、[CTRL]+[スペース]キーで要素や属性を表示できるようになります。
まとめ
NHibernateの特長を整理します。
- マッピングファイル「*.hbm.xml」にクラスとテーブルの関係を定義します。
ISession
の「Save
」「Load
」「Update
」「Delete
」メソッドによって、データベースのCRUD(Create、Read、Update、Delete)操作が可能です。ISession
の「CreateCriteria
」「Find
」メソッドによって、複数データの抽出が可能です。- アプリケーション構成ファイルの「
driver_class
」「dialect
」「connection_string
」の設定によって、データベースの接続先を変更できるため、特定のデータベースに依存しにくくなります。 - SQLを記述しないためコーディング量の軽減が期待できます。
執筆時点でのNHibernateの最新バージョンは0.91ですが、頻繁にバージョンアップが繰り返されているため、正式版が公開される日も近いのではないかと予想されます。興味をもたれた読者はNHibernateを先取りして試してみてはいかがでしょうか。
参考資料
- NHibernate(英語)
- Hibernateリファレンス・ドキュメント
- @IT 『O/Rマッピングの役割とメリット』 山本大 著、2004年4月
- オブジェクトの広場 『現場で使えるHibernate』 更谷暢哉 著、2005年7月