Nhibernate-quick-guide

提供:Dev Guides
移動先:案内検索

NHibernate-概要

この章では、NHibernateとは何か、実装できるすべてのプラットフォーム、その利点、およびそれに関連する他の側面について説明します。

NHibernateとは何ですか?

NHibernateは、.NETフレームワーク用の成熟したオープンソースのオブジェクトリレーショナルマッパーです。 何千もの成功したプロジェクトで積極的に開発され、完全に機能し、使用されています。 ADO.NET の上に構築されており、現在のバージョンは* NHibernate 4.0.4。*です。

  • NHibernateはオープンソースの.NETオブジェクトリレーショナルマッパーであり、 GNU Lesser General Public License の下で配布されます。
  • 人気のあるJavaオブジェクトリレーショナルマッパーであるHibernateに基づいており、非常に成熟したアクティブなコードベースを備えています。
  • オブジェクト指向のドメインモデルを従来のリレーショナルデータベースにマッピングするためのフレームワークを提供します。
  • NHibernateは Tom Barrett によって開始され、このプロジェクトは2003年2月から開始されました。
  • それは大きなプロジェクトであり、多くの機能を提供します。
  • * NuGetパッケージ*が用意されており、プロジェクトへの追加が非常に簡単になります。

NHibernateを使用する理由

さて、問題はなぜオブジェクトリレーショナルマッパーが必要なのかということです。それは、オブジェクトの世界とリレーショナルの世界との間に断絶があるからです。

  • オブジェクトの世界では、すべてが*オブジェクト*に基づいています。オブジェクトとは、データを持つものと呼ばれます。
  • リレーショナルワールドはすべてセットベースであり、オブジェクトワールドとは異なるテーブルと行を扱っています。
  • オブジェクトの世界では、*単方向の関連付け*があります。 顧客が注文へのポインタを持っている場合、それは必ずしも注文が顧客へのポインタを持っていることを意味するわけではありません。
  • リレーショナルの世界では、すべての関連付けは*双方向*であり、外部キーによって実行できます。
  • すべてのアソシエーションは本質的に双方向であるため、オブジェクトリレーショナルマッピングを扱うときは、この切断も処理する必要があります。
  • オブジェクトの世界では、単方向のポインターで作業していますが、リレーショナルの世界では、本質的に双方向の外部キーがあります。
  • オブジェクトの世界には継承という概念があり、車両にはさまざまなサブクラスを含めることができるため、車は車両の一種、ボートは車両の一種、スポーツカーは車の一種です。継承関係。
  • リレーショナルの世界には、この継承の概念がありません。

マッピング

それでは、これらすべての*互いに素な関係*をどのようにマッピングしますか?このマッピングの概念は、オブジェクトリレーショナルマッパーに由来します。 次の図に示すように、主に3つのことを理解する必要があります。

マッピング

  • アプリケーションでは、クラス定義が必要です。これは通常、C#コードと、Employeeクラス、Customerクラス、Orderクラスなどのクラスを表す.NETコードです。
  • 一番下に、データベーススキーマがあります。これは、顧客テーブルと従業員テーブルの外観を指定するリレーショナルデータベースの*データ定義言語*です。
  • これらの間に、C#のオブジェクトワールドから行と列、および外部キー関係の観点からデータベースワールドに変換する方法をオブジェクトリレーショナルマッパーに伝えるマッピングメタデータがあります。
  • このマッピングメタデータはさまざまな方法で表すことができ、NHibernateアプリケーションで最も一般的なこのさまざまな方法をいくつか見ていきます。
  • XMLファイルである* HBM(Hibernate Mapping)*ファイルで表されます。

サポートされるデータベース

NHibernateは、多種多様なデータベースをサポートしています。 そこにある既存のリレーショナルデータベースはすべて、NHibernateにアクセスできます。

  • SQLサーバーは、サポートされている主要なデータベースです。これは、ほとんどの開発者が開発中に使用しているもので、おそらく最も一般的なものです。
  • また、* Oracleと非常にうまく機能します*。
  • DB2、Firebird、MySQL、PostgreSQL、SQL Liteもサポートしています
  • また、* ODBCおよびOLEDBドライバー*もあります。

NHibernate-アーキテクチャ

現在、多くのシステムはレイヤードアーキテクチャで設計されていますが、NHibernateにもそれがあり、その設計で完全に機能します。

階層化アーキテクチャ

階層化アーキテクチャは、システムをいくつかのグループに分割します。各グループには特定の問題領域に対応するコードが含まれ、これらのグループはレイヤーと呼ばれます。 エンタープライズレベルのアプリケーションのほとんどは、3つの層で構成される*高レベルアプリケーションアーキテクチャ*を使用します-

  • プレゼンテーション層
  • ビジネス層
  • 永続化レイヤー

レイヤードアーキテクチャ

たとえば、プレゼンテーションレイヤーとも呼ばれるユーザーインターフェイスレイヤーには、Webページを構築し、ユーザー入力を処理するためのすべてのアプリケーションコードが含まれる場合があります。

階層化アプローチの主な利点の1つは、多くの場合、他のレイヤーを大幅に中断することなく1つのレイヤーに変更を加えることができることです。

プレゼンテーション層

  • これは最上位のレイヤーであり、ユーザーインターフェイス、ページ、ダイアログ、または画面の描画、ユーザー入力の収集、およびナビゲーションの制御を担当するコードが含まれています。

ビジネス層

  • ビジネス層は、ユーザーが問題ドメインの一部として理解するビジネスルールまたはシステム要件を実装する責任があります。
  • また、永続層で定義されたモデルを再利用します。

永続層

  • 永続層は、アプリケーションデータの保存と取得を担当するクラスとコンポーネントで構成されます。
  • この層は、モデルクラスとデータベース間のマッピングも定義します。 NHibernateは主にこの層で使用されます。

データベース

  • データベースは.NETアプリケーションの外部に存在します。
  • これは、システム状態の実際の永続的な表現です。
  • SQLデータベースを使用する場合、データベースにはリレーショナルスキーマとストアドプロシージャが含まれます。

ヘルパー/ユーティリティクラス

  • すべてのアプリケーションには、UIウィジェット、メッセージングクラス、例外クラス、ロギングユーティリティなど、他のレイヤーをサポートするヘルパークラスまたはユーティリティクラスのセットがあります。
  • これらの要素は、レイヤードアーキテクチャの層間依存性の規則に従っていないため、レイヤーとは見なされません。

NHibernateアーキテクチャ

  • NHibernateアプリケーションの高レベルのビューであり、単純なNHibernateアーキテクチャも確認できます。

.Net Environment

*アプリケーションコードは、永続化操作にNHibernate* ISession *および *IQuery* APIを使用し、理想的にはNHibernate *ITransaction* APIを使用して、データベーストランザクションのみを管理する必要があります。

NHibernate - ORM

NHibernateを実際に使い始める前に、NHibernateが構築されている基盤を理解する必要があります。 NHibernateは、オブジェクトリレーショナルマッピングまたはORMの概念に基づいた永続化テクノロジです。

ORMとは何ですか?

オブジェクトリレーショナルマッピング(ORM)は、オブジェクト指向プログラミング言語の互換性のない型システム間でデータを変換するための*プログラミングテクニック*です。 つまり、アプリケーションのビジネスオブジェクトをリレーショナルデータベーステーブルにマッピングする概念であり、アプリケーションのオブジェクトモデルを介してデータに簡単にアクセスし、更新することができます。

  • リレーショナルデータベースはデータを格納するための優れた手段を提供することを既にご存じのとおり、オブジェクト指向プログラミングは複雑なアプリケーションを構築するための優れたアプローチです。
  • NHibernateとORMは一般に、重要なビジネスロジック、ドメインモデル、何らかのデータベースを備えたアプリケーションに最も関連しています。
  • ORMを使用すると、オブジェクトをリレーショナルデータに簡単に変換したり、元に戻したりできる変換レイヤーを簡単に作成できます。
  • 頭字語ORMはオブジェクトロールモデリングを意味することもあり、この用語はオブジェクト/リレーショナルマッピングが関連する前に発明されました。
  • データベースモデリングで使用される情報分析の方法について説明します。

ORMを選ぶ理由

ORMは、オブジェクト指向言語で見つかったオブジェクトの世界を、リレーショナルデータベースで見つかったリレーショナルテーブルの行にマッピングできる*フレームワーク*です。

この概念を理解するために、次の図を見てみましょう。

従業員

  • 上の図では、個々の従業員に関連付けられた各データの列を含むEmployeeというテーブルが右側にあることがわかります。
  • 各従業員を一意に識別するIDの列があります。
  • 従業員の名前の列、参加日の別の列、最後に従業員の年齢の列。
  • テーブルに新しい従業員を格納するコードを作成したい場合、それはそれほど簡単ではありません。
  • 上の図では、Id、名前、加入日、年齢のフィールドを持つ従業員オブジェクトがあることもわかります。
  • ORMがなければ、このオブジェクトを、従業員データを従業員テーブルに挿入するいくつかの異なるSQLステートメントに変換する必要があります。
  • したがって、上記のシナリオを実行するSQLを作成するコードを作成するのはそれほど難しくありませんが、少し面倒で、間違いを犯しやすいです。
  • NHibernateのようなORMを使用して、特定のクラスをリレーショナルテーブルにマップする方法を宣言し、ORMまたはNHibernateが従業員テーブルのクエリデータに挿入、更新、削除するSQLを作成するという厄介な仕事を処理できるようにします。
  • これにより、コードをオブジェクトの使用に集中させ、それらのオブジェクトをリレーショナルテーブルに自動的に変換できます。
  • ORMが実際に行うことは、オブジェクトをテーブルに手動でマップする必要がなくなることです。

NHibernate-環境設定

NHibernateでの作業を開始するには、Visual StudioとNHibernateパッケージが必要です。

Visual Studioのインストール

Microsoftは SQL Server を含むVisual Studioの*無料版*を提供しており、https://www.visualstudio.com/en-us/downloads/download-visual-studio-vs.aspxからダウンロードできます[ https://www.visualstudio.com]インストールの手順は次のとおりです。

  • ステップ1 *-ダウンロードが完了したら、インストーラーを実行すると、次のダイアログボックスが表示されます。

Visual Studio

  • ステップ2 *-[インストール]ボタンをクリックすると、インストールプロセスが開始されます。

インストールプロセス

  • ステップ3 *-インストールプロセスが正常に完了すると、次のダイアログボックスが表示されます。

インストールプロセス完了

  • ステップ4 *-このダイアログボックスを閉じ、必要に応じてコンピューターを再起動します。
  • ステップ5 *-[スタート]メニューからVisual Studioを開き、次のダイアログを開きます。 準備には初めて時間がかかります。

Visual Studioの最初の使用

  • ステップ6 *-これがすべて完了すると、Visual Studioのメインウィンドウが表示されます。

Visual Studioのウィンドウ

NHibernateパッケージのインストール

NHibernateは、.NETフレームワーク用の成熟したオープンソースのオブジェクトリレーショナルマッパーです。 数千の成功したプロジェクトで積極的に開発され、完全に機能し、使用されています。 次の方法でNHibernateパッケージをインストールできます。

直接ダウンロード

NuGetを使用してインストールする

  • NHibernateをインストールする別の方法は、NuGetを使用してNHibernateパッケージをインストールすることです。これは、NHibernateをプロジェクトに組み込む最も簡単な方法です。
  • すべてのNHibernate依存関係をダウンロードし、必要なすべてのアセンブリへの参照を作成します。
  • NHibernateをインストールするには、パッケージマネージャーコンソールで次のコマンドを実行します。
install-package NHibernate

パッケージマネージャーコンソール

これで、アプリケーションを開始する準備ができました。

NHibernate-はじめに

この章では、NHibernateを使用して簡単な例を開始する方法を見ていきます。 *シンプルなコンソールアプリケーション*を構築します。 コンソールアプリケーションを作成するには、作成に必要なすべての機能を含むVisual Studio 2015を使用し、NHibernateパッケージを使用してアプリケーションをテストします。

以下は、Visual Studioで利用可能なプロジェクトテンプレートを使用してプロジェクトを作成する手順です。

  • ステップ1 *-Visual Studioを開き、[ファイル]→[新規]→[プロジェクト]メニューオプションをクリックします。
  • ステップ2 *-新しいプロジェクトダイアログが開きます。

プロジェクトダイアログ

  • ステップ3 *-左ペインから、テンプレート→Visual C#→Windowsを選択します。
  • ステップ4 *-中央のペインで、[コンソールアプリケーション]を選択します。
  • ステップ5 *-[Name]フィールドにプロジェクト名「NHibernateDemoApp」を入力し、[OK]をクリックして続行します。
  • ステップ6 *-Visual Studioでプロジェクトを作成すると、ソリューションエクスプローラーウィンドウに多数のファイルが表示されます。

プロジェクトダイアログ

簡単なコンソールアプリケーションプロジェクトを作成したことをご存知のとおり、NHibernateパッケージをコンソールプロジェクトに含める必要があります。

[ツール]メニューに移動し、[NuGetパッケージマネージャー]→[パッケージマネージャーコンソール]を選択すると、[パッケージマネージャーコンソール]ウィンドウが開きます。

マネージャーコンソールウィンドウ

上記の Package Manager Console ウィンドウに表示されるコマンドを指定してEnterキーを押すと、NHibernateのすべての依存関係がダウンロードされ、必要なすべてのアセンブリへの参照が作成されます。 インストールが完了すると、次の図に示すようなメッセージが表示されます。

NHibernate Demo App

NHibernateが追加されたので、実装を開始できます。 したがって、まず、 Student という非常に単純な table をマッピングすることから始めます。この表には、IDという名前の整数主キーとFirstNameおよびLastName列があります。

新しいアイテムを追加

この生徒を表すクラスが必要です。そのため、ソリューションエクスプローラーでプロジェクトを右クリックし、[追加]→[クラス]を選択して[新しいアイテムの追加]ダイアログボックスを開いて、Studentという新しいクラスを作成します。

新しいアイテムNHibernateを追加

名前フィールドに Student.cs と入力し、[追加]ボタンをクリックします。 このStudentクラスでは、IDという整数のプライマリキーが必要です。また、次のStudentクラスの完全な実装に示すように、この文字列、 FirstName および LastName フィールドを作成する必要があります。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace NHibernateDemoApp {

   class Student {
      public virtual int ID { get; set; }
      public virtual string LastName { get; set; }
      public virtual string FirstMidName { get; set; }
   }
}

NHibernateアプリケーションでモデルを扱う場合、すべてのフィールドを仮想化するのが最も簡単です。 これが、使用する単純なNHibernateモデルであり、これをバックエンドデータベースにマッピングします。

次に、ProgramクラスのMainメソッドに移動して、新しいNHibernate構成オブジェクトを作成しましょう。

最初に提供する必要があるのは、接続文字列*です。 これはデータベース固有の接続文字列であり、接続文字列を見つける最も簡単な方法は、 SQL Serverオブジェクトエクスプローラー*でデータベースを右クリックし、[プロパティ]を選択することです。

SQL Serverオブジェクトエクスプローラー

[プロパティ]ウィンドウが開き、下にスクロールすると、[プロパティ]ウィンドウに[接続文字列]フィールドが表示されます。

プロパティウィンドウ

接続文字列をコピーして、コードで指定します。 以下は、NHibernateの設定が必要なMainメソッドの実装です。

using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;

using System;
using System.Linq;
using System.Reflection;

namespace NHibernateDemoApp {

   class Program {

      static void Main(string[] args) {
         var cfg = new Configuration();

         String Data Source = asia13797\\sqlexpress;
         String Initial Catalog = NHibernateDemoDB;
         String Integrated Security = True;
         String Connect Timeout = 15;
         String Encrypt = False;
         String TrustServerCertificate = False;
         String ApplicationIntent = ReadWrite;
         String MultiSubnetFailover = False;

         cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source +
            Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
            TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";


            x.Driver<SqlClientDriver>();
            x.Dialect<MsSql2008Dialect>();
         });

         cfg.AddAssembly(Assembly.GetExecutingAssembly());

         var sefact = cfg.BuildSessionFactory();

         using (var session = sefact.OpenSession()) {

            using (var tx = session.BeginTransaction()) {
              //perform database logic
               tx.Commit();
            }

            Console.ReadLine();
         }
      }
   }
}

接続文字列の後に、 SQLClientDriver であるドライバーを提供する必要があります。次に、SQL Serverのバージョンである方言を提供する必要があり、MS SQL 2008を使用します。

NHibernateは、データベースへの接続方法を認識できるようになりました。 他に必要なことは、マップするモデルのリストを提供することです。

これを行うには、アセンブリを追加します。 Assembly.GetExecutingAssembly を指定すると、プログラムがマッピングファイルを検索します。 マッピングファイルは、C#クラスからデータベーステーブルに移動する方法をNHibernateに指示します。

SessionFactoryは、NHibernateの初期化に必要なすべてのメタデータをコンパイルします。 SessionFactoryを使用して、データベース接続にほぼ類似したセッションを構築できます。 したがって、適切な方法は、usingブロックで使用することです。 var sessionsessionFactory.OpenSession に等しいと言えます。これをトランザクション内で実行したいと思います。

セッションが開いたら、セッションに新しいトランザクションを開始するように指示し、ここでいくつかのロジックを実行できます。 したがって、いくつかのデータベースロジックを実行し、最後にそのトランザクションをコミットします。

NHibernate-基本的なORM

この章では、いくつかの「基本的なマッピング」について説明します。最後の章から、データベーステーブルとC#クラス定義があることを知っています。 ここで、C#からデータベースへの変換とその逆の変換方法を説明するマッピングが必要です。

ソリューションエクスプローラーでプロジェクトを右クリックし、[追加]→[新しいアイテム]を選択して、新しいXMLファイルを追加しましょう。

新しいXMLファイル

名前フィールドに Student.hbm.xml を入力します。 NHibernateDemoApp になる既定のアセンブリを指定し、既定の名前空間も指定する必要があります。 これにより、このファイルで作成する他の多くの型定義が短縮されます。

以下は、XMLファイルでの実装です-

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

<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2"
   assembly = "NHibernateDemoApp" namespace = "NHibernateDemoApp">

   <class name = "Student">
      <id name = "ID">
        <generator class = "native"/>
      </id>

      <property name = "LastName"/>
      <property name = "FirstMidName"/>
   </class>

</hibernate-mapping>

次にクラスを定義する必要があるもの。このクラスは* Studentクラス*になります。 次に、NHibernateにIDの名前(ID)を伝える必要があります。また、NHibernateにIDの生成方法を伝える必要があるため、ジェネレーターはネイティブタイプになります。

ネイティブタイプジェネレーターとは、SQL Serverのようなデータベースでは、ID列であるIDタイプを使用することを意味します。

次に行う必要があるのは、プロパティの名前を指定することです。 そのため、FirstNameとLastNameにさらに2つのプロパティを追加します。

現在、これらのマッピングファイルをアセンブリから読み取っています。 したがって、これを行うための好ましい方法は、これらの* HBMファイル*をアセンブリに焼き付けることです。 これを行うには、プロパティを設定するだけです。

ソリューションエクスプローラーでプロジェクトを右クリックして[プロパティ]を選択すると、デフォルトでコンテンツが選択されている[ビルドアクション]フィールド*が表示されます。

ビルドアクションフィールド

ドロップダウンリストから埋め込みリソースを選択します。

埋め込みリソース

したがって、これは実際に NHibernateDemoApp アセンブリ内にそのXMLファイルを埋め込みます。

NHibernate-基本的なCRUD操作

この章では、基本的な* CRUD操作*について説明します。 ドメインのStudentクラスを正常に実装したため、システムを起動する準備ができたので、マッピングファイルも定義し、NHibernateを構成しました。 これで、いくつかのクエリを使用してCRUD操作を実行できます。

データを作成する

ご覧のとおり、 NHibernateDemoDB データベースのStudentテーブルにはデータがありません。

NHibernate DemoDB

そのため、データを追加するには、以下に示すように Add/Create 操作を実行する必要があります。

using (var session = sefact.OpenSession()) {

   using (var tx = session.BeginTransaction()) {

      var student1 = new Student {
         ID = 1,
         FirstMidName = "Allan",
         LastName = "Bommer"
      };

      var student2 = new Student {
         ID = 2,
         FirstMidName = "Jerry",
         LastName = "Lewis"
      };

      session.Save(student1);
      session.Save(student2);
      tx.Commit();
   }

   Console.ReadLine();
}

ご覧のとおり、2人の生徒を作成し、 OpenSession のSave()メソッドを呼び出してから、 BeginTransaction のCommit()を呼び出します。 Program.cs ファイルの完全な実装を次に示します。

using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;

using System;
using System.Linq;
using System.Reflection;

namespace NHibernateDemoApp {

   class Program {

      static void Main(string[] args) {
         var cfg = new Configuration();

         String Data Source = asia13797\\sqlexpress;
         String Initial Catalog = NHibernateDemoDB;
         String Integrated Security = True;
         String Connect Timeout = 15;
         String Encrypt = False;
         String TrustServerCertificate = False;
         String ApplicationIntent = ReadWrite;
         String MultiSubnetFailover = False;

         cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source +
            Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
            TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";

            x.Driver<SqlClientDriver>();
            x.Dialect<MsSql2008Dialect>();
         });

         cfg.AddAssembly(Assembly.GetExecutingAssembly());
         var sefact = cfg.BuildSessionFactory();

         using (var session = sefact.OpenSession()) {

            using (var tx = session.BeginTransaction()) {

               var student1 = new Student {
                  ID = 1,
                  FirstMidName = "Allan",
                  LastName = "Bommer"
               };

               var student2 = new Student {
                  ID = 2,
                  FirstMidName = "Jerry",
                  LastName = "Lewis"
               };

               session.Save(student1);
               session.Save(student2);
               tx.Commit();
            }

            Console.ReadLine();
         }
      }
   }
}

次に、このアプリケーションを実行してから、SQL Serverオブジェクトエクスプローラーに移動し、データベースを更新します。 上記の2人の生徒が、NHibernateDemoDBデータベースのStudentテーブルに追加されたことがわかります。

SQL Serverオブジェクト

学生テーブルからデータを読み取る

学生テーブルに2つのレコードがあることがわかります。 テーブルからこれらのレコードを読み取るには、次のコードに示すように、OpenSessionの* CreateCriteria()*を呼び出す必要があります。

using (var session = sefact.OpenSession()) {

   using (var tx = session.BeginTransaction()) {
      var students = session.CreateCriteria<Student>().List<Student>();

      foreach (var student in students) {
         Console.WriteLine("{0} \t{1} \t{2}",
            student.ID,student.FirstMidName, student.LastName);
      }

      tx.Commit();
   }

   Console.ReadLine();
}

したがって、レコードのリストが必要な場合は、Studentタイプのリストを単に言うことができます。

すべての生徒で foreach を使用して、コンソールにID、 FirstMidName および LastName を印刷すると言います。 ここで、このアプリケーションを再度実行してみましょう。コンソールウィンドウに次の出力が表示されます。

1 Allan Bommer
2 Jerry Lewis

次のコードを使用して、OpenSessionの* Get()*メソッドでIDを指定することにより、任意のレコードを取得することもできます。

using (var session = sefact.OpenSession()) {

   using (var tx = session.BeginTransaction()) {
      var students = session.CreateCriteria<Student>().List<Student>();

      foreach (var student in students) {
         Console.WriteLine("{0} \t{1} \t{2}", student.ID,
            student.FirstMidName, student.LastName);
      }

      var stdnt = session.Get<Student>(1);
      Console.WriteLine("Retrieved by ID");
      Console.WriteLine("{0} \t{1} \t{2}", stdnt.ID,
         stdnt.FirstMidName, stdnt.LastName);
      tx.Commit();
   }

   Console.ReadLine();
}

これで、アプリケーションを実行すると、次の出力が表示されます。

1 Allan Bommer
2 Jerry Lewis
Retrieved by ID
1 Allan Bommer

レコードを更新

テーブル内のレコードを更新するには、最初にその特定のレコードをフェッチしてから、次のコードに示すようにOpenSessionの* Update()*メソッドを呼び出してそのレコードを更新する必要があります。

using (var session = sefact.OpenSession()) {

   using (var tx = session.BeginTransaction()) {
      var students = session.CreateCriteria<Student>().List<Student>();

      foreach (var student in students) {
         Console.WriteLine("{0} \t{1} \t{2}", student.ID,
            student.FirstMidName, student.LastName);
      }

      var stdnt = session.Get<Student>(1);
      Console.WriteLine("Retrieved by ID");
      Console.WriteLine("{0} \t{1} \t{2}", stdnt.ID, stdnt.FirstMidName, stdnt.LastName);

      Console.WriteLine("Update the last name of ID = {0}", stdnt.ID);
      stdnt.LastName = "Donald";
      session.Update(stdnt);
      Console.WriteLine("\nFetch the complete list again\n");

      foreach (var student in students) {
         Console.WriteLine("{0} \t{1} \t{2}", student.ID,
            student.FirstMidName, student.LastName);
      }

      tx.Commit();
   }

   Console.ReadLine();
}

これで、アプリケーションを実行すると、次の出力が表示されます。

1 Allan Bommer
2 Jerry Lewis
Retrieved by ID
1 Allan Bommer
Update the last name of ID = 1
Fetch the complete list again
1 Allan Donald
2 Jerry Lewis

ご覧のとおり、IDが1のLastNameがBommerからDonaldに更新されます。

レコードを削除

テーブルからレコードを削除するには、次のコードに示すように、まず特定のレコードをフェッチしてから、OpenSessionの* Delete()*メソッドを呼び出してそのレコードを削除する必要があります。

using (var session = sefact.OpenSession()) {

   using (var tx = session.BeginTransaction()) {
      var students = session.CreateCriteria<Student>().List<Student>();

      foreach (var student in students) {
         Console.WriteLine("{0} \t{1} \t{2}", student.ID,
            student.FirstMidName, student.LastName);
      }

      var stdnt = session.Get<Student>(1);
      Console.WriteLine("Retrieved by ID");
      Console.WriteLine("{0} \t{1} \t{2}", stdnt.ID, stdnt.FirstMidName, stdnt.LastName);

      Console.WriteLine("Delete the record which has ID = {0}", stdnt.ID);
      session.Delete(stdnt);
      Console.WriteLine("\nFetch the complete list again\n");

      foreach (var student in students) {
         Console.WriteLine("{0} \t{1} \t{2}", student.ID, student.FirstMidName,
            student.LastName);
      }

      tx.Commit();
   }

   Console.ReadLine();
}

これで、アプリケーションを実行すると、次の出力が表示されます。

1 Allan Donald
2 Jerry Lewis
Retrieved by ID
1 Allan Bommer
Delete the record which has ID = 1
Fetch the complete list again
2 Jerry Lewis

ご覧のとおり、IDが1のレコードはデータベースで使用できなくなっています。 SQL Serverオブジェクトエクスプローラーでデータベースを確認することもできます。

オブジェクトエクスプローラ

NHibernate-プロファイラー

この章では、データベースのすべてのレコードが*取得、更新、作成、削除*され、これらのクエリがどのように実行されるのかを理解します。

これらすべてを理解するために、コンソールにSQLを記録するオプションを設定に追加するだけです。 これは、SQLクエリをログに記録する簡単なステートメントです-

x.LogSqlInConsole = true;

これで、NHibernateDemoDBデータベースのstudentテーブルに2つのレコードができました。 次のコードに示すように、データベースからすべてのレコードを取得しましょう。

using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;

using System;
using System.Linq;
using System.Reflection;

namespace NHibernateDemoApp {

   class Program {

      static void Main(string[] args) {
         var cfg = new Configuration();

         String Data Source = asia13797\\sqlexpress;
         String Initial Catalog = NHibernateDemoDB;
         String Integrated Security = True;
         String Connect Timeout = 15;
         String Encrypt = False;
         String TrustServerCertificate = False;
         String ApplicationIntent = ReadWrite;
         String MultiSubnetFailover = False;

         cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source +
            Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
            TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";

            x.Driver<SqlClientDriver>();
            x.Dialect<MsSql2008Dialect>();
            x.LogSqlInConsole = true;
         });

         cfg.AddAssembly(Assembly.GetExecutingAssembly());
         var sefact = cfg.BuildSessionFactory();

         using (var session = sefact.OpenSession()) {

            using (var tx = session.BeginTransaction()) {
               Console.WriteLine("\nFetch the complete list again\n");
               var students = session.CreateCriteria<Student>().List<Student>();

               foreach (var student in students) {
                  Console.WriteLine("{0} \t{1} \t{2}", student.ID, student.FirstMidName,
                     student.LastName);
               }

               tx.Commit();
            }

            Console.ReadLine();
         }
      }
   }
}

だから先に進み、このアプリケーションを再度実行してみましょう。次の出力が表示されます-

NHibernate: SELECT this_.ID as ID0_0_, this_.LastName as LastName0_0_,
   this_.FirstMidName as FirstMid3_0_0_ FROM Student this_

Fetch the complete list again

3 Allan Bommer
4 Jerry Lewis

ご覧のとおり、データベースに送信される* select句*は、実際にはID、FirstMidNameおよびLastNameを取得する句に似ています。 したがって、大量のレコードをサーバーに戻してサーバー側で処理するのではなく、これらすべてをデータベースに送信してそこで処理しています。

NHibernate Profiler

これらの結果を見る別の方法は、NHibernate Profilerを使用することです。 NHibernate Profilerは商用ツールですが、NHibernateアプリケーションでの作業には非常に便利です。 NuGetからNHibernate Profilerをアプリケーションに簡単にインストールできます。

NuGetパッケージマネージャー→パッケージマネージャーコンソールを選択して、[ツール]メニューからNuGetマネージャーコンソールに移動します。 パッケージマネージャーコンソールウィンドウが開きます。 次のコマンドを入力して、Enterキーを押します。

PM> install-package NHibernateProfiler

NHibernate Profilerに必要なすべてのバイナリがインストールされます。正常にインストールされると、次のメッセージが表示されます。

NHibernate Profiler

NHibernate Profilerがインストールされると、NHibernate Profilerが起動することもわかります。 それを使用するにはライセンスが必要になりますが、デモのために、NHibernate Profilerの30日間の試用版を使用できます。

これで、NHibernate ProfilerはWebアプリケーションで動作するように最適化され、ソリューションエクスプローラーに* App_Startフォルダー*が追加されていることがわかります。 これらすべてをシンプルに保つには、App_Startフォルダーを削除します。また、プログラムクラスのMainメソッドの開始時に1つのステートメントが追加されることを確認します。

App_Start.NHibernateProfilerBootstrapper.PreStart();

このステートメントも削除し、次のコードに示すように、単純な呼び出し NHibernateProfiler.Initialize を追加するだけです。

using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;

using System;
using System.Linq;
using System.Reflection;

namespace NHibernateDemoApp {

   class Program {

      static void Main(string[] args) {

         NHibernateProfiler.Initialize();
         var cfg = new Configuration();

         String Data Source = asia13797\\sqlexpress;
         String Initial Catalog = NHibernateDemoDB;
         String Integrated Security = True;
         String Connect Timeout = 15;
         String Encrypt = False;
         String TrustServerCertificate = False;
         String ApplicationIntent = ReadWrite;
         String MultiSubnetFailover = False;

         cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source +
            Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
            TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";

            x.Driver<SqlClientDriver>();
            x.Dialect<MsSql2008Dialect>();
            x.LogSqlInConsole = true;
         });

         cfg.AddAssembly(Assembly.GetExecutingAssembly());
         var sefact = cfg.BuildSessionFactory();

         using (var session = sefact.OpenSession()) {

            using (var tx = session.BeginTransaction()){
               var students = session.CreateCriteria<Student>().List<Student>();
               Console.WriteLine("\nFetch the complete list again\n");

               foreach (var student in students) {
                  Console.WriteLine("{0} \t{1} \t{2}", student.ID, student.FirstMidName,
                     student.LastName);
               }

               tx.Commit();
            }

            Console.ReadLine();
         }
      }

   }
}

アプリケーションを実行すると、NHibernate Profilerアプリケーションにデータが送信されます。

NHibernate Profiler Application

ここに表示されているように、トランザクションが開始されたこと、SQLがデータベースに対して実行していることを素敵な形式で表示する素晴らしい表示があります。

したがって、これは、NHibernateアプリケーションの内部で何が起こっているかを正確に判断するのに非常に役立ちます。 アプリケーションが一定レベルの複雑さになると、信じられないほど便利になります。SQLプロファイラのようなものが必要ですが、NHibernateの知識が必要です。

IntelliSenseをマッピングファイルに追加する

この章では、NHibernateマッピングファイル*(。hbm.xmlファイル)*に *IntelliSense を追加します。 現在、IntelliSenseを使用できないドメインStudentクラスのマッピング中に確認したように。 * XMLスキーマ*を利用できると非常に便利です。 したがって、この章では、これらのNHibernate XMLファイル用にVisual StudioでIntelliSenseを追加する方法を理解します。

マッピングファイルを開くと、メインメニューにXMLメニューオプションが表示されます。

XMLメニュー

[XML]→[スキーマ]メニューオプションを選択すると、[XMLスキーマ]ダイアログボックスが表示されます。

XMLスキーマ

ダイアログボックスの右上にある[追加…​]ボタンを選択すると、ファイルダイアログが開きます。 プロジェクトのSolutionフォルダーにある* packagesフォルダー*に移動すると、プロジェクトに含まれるさまざまなパッケージが表示されます。

パッケージフォルダー

  • NHibernate.4。**フォルダー*をダブルクリックすると、NHibernateの構成とマッピングを定義する2つのスキーマ( .xsd)ファイルまたはXMLスキーマ定義ファイルが表示されます。

Nhibernate Configuration

これらの2つのスキーマファイルを選択し、[開く]ボタンをクリックします。

2つのスキーマファイル

NHibernateスキーマが[XMLスキーマ]ダイアログに追加されていることがわかります。 OKボタンをクリックしてください。 それでは、新しいプロパティタグを開始しましょう。ここで、完全なIntelliSenseが用意されています。

IntelliSense

IntelliSenseが利用可能になり、オブジェクトリレーショナルマッピング中の時間を大幅に節約できます。

NHibernate-データ型マッピング

この章では、マッピングデータ型について説明します。 エンティティのマッピングは簡単で、エンティティクラスは常に <class>、<subclass>、および<joined-subclass> マッピング要素を使用してデータベーステーブルにマッピングされます。 値型にはさらに何かが必要です。マッピング型が必要な場所です。

NHibernateは、さまざまなデータタイプをマップできます。 以下は、サポートされている最も一般的なデータ型のリストです。

Mapping type .NET type System.Data.DbType
Int16 System.Int16 DbType.Int16
Int32 System.Int32 DbType.Int32
Int64 System.Int64 DbType.Int64
Single System.Single DbType.Single
Double System.Double DbType.Double
Decimal System.Decimal DbType.Decimal
String System.String DbType.String
AnsiString System.String DbType.AnsiString
Byte System.Byte DbType.Byte
Char System.Char DbType.StringFixedLength—one character
AnsiChar System.Char DbType.AnsiStringFixedLength—one character
Boolean System.Boolean DbType.Boolean
Guid System.Guid DbType.Guid
PersistentEnum System.Enum(an enumeration) DbType for the underlying value
TrueFalse System.Boolean DbType.AnsiStringFixedLength—either 'T' or 'F'
YesNo System.Boolean DbType.AnsiStringFixedLength—either 'Y' or 'N'
DateTime DateTime DbType.DateTime—ignores milliseconds
Ticks System.DateTime DbType.Int64
TimeSpan System.TimeSpan DbType.Int64
Timestamp System.DateTime DbType.DateTime—as specific as the database supports
Binary System.Byte[] DbType.Binary
BinaryBlob System.Byte[] DbType.Binary
StringClob System.String DbType.String
Serializable Any System.Object marked with SerializableAttribute DbType.Binary
CultureInfo System.Globalization.CultureInfo DbType.String—five characters for culture
Type System.Type DbType.String holding the Assembly Qualified Name

上記の表は、下記のポインターを詳細に説明しています。

  • 単純な数値型から文字列まで、すべて varchars、chars などを使用してさまざまな方法でマッピングできます。 文字列blob、およびデータベースがサポートするすべてのさまざまなタイプ。
  • また、ゼロと1を使用するフィールド、true、false、またはTとFを含む文字フィールドの両方に*ブール値*をマップすることもできます。
  • データベース内のバックエンド、ブール値にマップする方法を定義するさまざまな方法があります。
  • タイムゾーンオフセット、夏時間などを含めて、 DateTime のマッピングを処理できます。
  • *列挙*をマップすることもできます。これらを文字列または基になる数値にマップできます。

データベースとStudentクラスの両方で同じプロパティ名を使用する簡単な例を見てみましょう。

では、学生クラスでFirstMidNameをFirstNameに変更します。FirstMidName列は変更しませんが、NHibernateにこの変換を実行することを知らせる方法を確認します。 以下は、更新された学生クラスです。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace NHibernateDemoApp {

   class Student {
      public virtual int ID { get; set; }
      public virtual string LastName { get; set; }
      public virtual string FirstName { get; set; }
   }
}

NHibernateマッピングファイルの実装を次に示します。

<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemoApp"
   namespace = "NHibernateDemoApp">

   <class name = "Student">

      <id name = "ID">
         <generator class = "native"/>
      </id>

      <property name = "LastName"/>
      <property name = "FirstName" column = "FirstMidName" type = "String"/>
   </class>

</hibernate-mapping>

この例では、FirstNameフィールドが.NET文字列で、FirstMidName列が SQL nvarchar であると想定しています。 NHibernateにこの変換の実行方法を伝えるには、名前を FirstName に、列を FirstMidName に設定し、この特定の変換に適したマッピングタイプをStringに指定します。

以下は Program.cs ファイルの実装です。

using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;

using System;
using System.Linq;
using System.Reflection;

namespace NHibernateDemoApp {

   class Program {

      static void Main(string[] args) {

         NHibernateProfiler.Initialize();
         var cfg = new Configuration();

         String Data Source = asia13797\\sqlexpress;
         String Initial Catalog = NHibernateDemoDB;
         String Integrated Security = True;
         String Connect Timeout = 15;
         String Encrypt = False;
         String TrustServerCertificate = False;
         String ApplicationIntent = ReadWrite;
         String MultiSubnetFailover = False;

         cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source +
            Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
            TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";

            x.Driver<SqlClientDriver>();
            x.Dialect<MsSql2008Dialect>();
            x.LogSqlInConsole = true;
         });

         cfg.AddAssembly(Assembly.GetExecutingAssembly());
         var sefact = cfg.BuildSessionFactory();

         using (var session = sefact.OpenSession()) {

            using (var tx = session.BeginTransaction()) {
               var students = session.CreateCriteria<Student>().List<Student>();
               Console.WriteLine("\nFetch the complete list again\n");

               foreach (var student in students) {
                  Console.WriteLine("{0} \t{1} \t{2}", student.ID, student.FirstName,
                     student.LastName);
               }

               tx.Commit();
            }

            Console.ReadLine();
         }
      }
   }
}

これで、アプリケーションを実行すると、次の出力が表示されます。

NHibernate: SELECT this_.ID as ID0_0_, this_.LastName as LastName0_0_,
   this_.FirstMidName as FirstMid3_0_0_ FROM Student this_

Fetch the complete list again
3 Allan Bommer
4 Jerry Lewis

ご覧のとおり、異なるプロパティ名がデータベースの列名にマッピングされています。

*enum* タイプのStudentクラスに別のプロパティを追加する別の例を見てみましょう。 これがStudentクラスの実装です。
using System;
using System.Collections.Generic;
using System.Linq; using System.Text;
using System.Threading.Tasks;

namespace NHibernateDemoApp {

   class Student {
      public virtual int ID { get; set; }
      public virtual string LastName { get; set; }
      public virtual string FirstName { get; set; }
      public virtual StudentAcademicStanding AcademicStanding { get; set; }
   }

   public enum StudentAcademicStanding {
      Excellent,
      Good,
      Fair,
      Poor,
      Terrible
   }
}

ご覧のとおり、列挙には、Excellent、Good、Fair、Poor、Terribleなどのさまざまな値が含まれている可能性があります。

マッピングファイルにジャンプすると、これらの各プロパティが、新しく追加された AcademicStanding プロパティを含むマッピングファイルにリストされていることがわかります。

<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2"
   assembly = "NHibernateDemoApp" namespace = "NHibernateDemoApp">

   <class name = "Student">

      <id name = "ID">
         <generator class = "native"/>
      </id>

      <property name = "LastName"/>
      <property name = "FirstName" column = "FirstMidName" type = "String"/>
      <property name = "AcademicStanding"/>
   </class>

</hibernate-mapping>

データベースも変更する必要があるため、SQL Serverオブジェクトエクスプローラーに移動し、データベースを右クリックして[新しいクエリ]オプションを選択します。

新しいクエリ

クエリエディターを開き、次のクエリを指定します。

DROP TABLE [dbo].[Student]

CREATE TABLE [dbo].[Student] (
   [ID] INT IDENTITY (1, 1) NOT NULL,
   [LastName] NVARCHAR (MAX) NULL,
   [FirstMidName] NVARCHAR (MAX) NULL,
   [AcademicStanding] NCHAR(10) NULL,
   CONSTRAINT [PK_dbo.Student] PRIMARY KEY CLUSTERED ([ID] ASC)
);

このクエリは、最初に既存の学生テーブルを削除してから、新しいテーブルを作成します。

新しいテーブルの作成

上記のように、実行アイコンをクリックします。 クエリが正常に実行されると、メッセージが表示されます。

データベースとテーブルのドロップダウンを展開し、Studentテーブルを右クリックして、View Designerを選択します。

テーブルのドロップダウン

これで、新しく作成されたテーブルが表示されます。これには、新しいプロパティAcademicStandingもあります。

アカデミックスタンディング

次の Program.cs ファイルに示すように、2つのレコードを追加しましょう。

using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;

using System;
using System.Linq;
using System.Reflection;

namespace NHibernateDemoApp {

   class Program {

      static void Main(string[] args) {

         NHibernateProfiler.Initialize();
         var cfg = new Configuration();

         String Data Source = asia13797\\sqlexpress;
         String Initial Catalog = NHibernateDemoDB;
         String Integrated Security = True;
         String Connect Timeout = 15;
         String Encrypt = False;
         String TrustServerCertificate = False;
         String ApplicationIntent = ReadWrite;
         String MultiSubnetFailover = False;

         cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source +
            Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
            TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";

            x.Driver<SqlClientDriver>();
            x.Dialect<MsSql2008Dialect>();
         });

         cfg.AddAssembly(Assembly.GetExecutingAssembly());
         var sefact = cfg.BuildSessionFactory();

         using (var session = sefact.OpenSession()) {
            using (var tx = session.BeginTransaction()) {

               var student1 = new Student {
                  ID = 1,
                  FirstName = "Allan",
                  LastName = "Bommer",
                  AcademicStanding = StudentAcademicStanding.Excellent
               };

               var student2 = new Student {
                  ID = 2,
                  FirstName = "Jerry",
                  LastName = "Lewis",
                  AcademicStanding = StudentAcademicStanding.Good
               };

               session.Save(student1);
               session.Save(student2);
               var students = session.CreateCriteria<Student>().List<Student>();
               Console.WriteLine("\nFetch the complete list again\n");

               foreach (var student in students) {
                  Console.WriteLine("{0} \t{1} \t{2} \t{3}", student.ID,
                     student.FirstName, student.LastName, student.AcademicStanding);
               }

               tx.Commit();
            }

            Console.ReadLine();
         }
      }
   }
}

アプリケーションを実行してみましょう。コンソールウィンドウに次の出力が表示されます。

Fetch the complete list again

1 Allan Bommer Excellent
2 Jerry Lewis Good

では、Student Tableを右クリックして、データベースを見てみましょう。

データベース

[データの表示]を選択すると、次のスクリーンショットに示すように、学生テーブルに2つのレコードが表示されます。

データの表示

2つのレコードが追加され、アランにはAcademicStanding 0があり、ジェリーにはAcademicStanding 1があることがわかります。 これは、.Netでは最初の列挙値のデフォルトが0であるためです。これは、 StudentAcademicStanding を見ると優れています。 一方、Student.csファイルではGoodが2番目であるため、値は1です。

NHibernate-構成

この章では、NHibernateの構成について説明します。 NHibernateを構成できるさまざまな方法があります。 2つの主なグループに分かれています

  • XMLベースの構成
  • コードベースの構成

コードベースの構成

コードベースの構成はNHibernateに組み込まれています。 NHibernate 3の周辺で導入されたもので、これまでコードベースの構成を使用していました。

String Data Source = asia13797\\sqlexpress;
String Initial Catalog = NHibernateDemoDB;
String Integrated Security = True;
String Connect Timeout = 15;
String Encrypt = False;
String TrustServerCertificate = False;
String ApplicationIntent = ReadWrite;
String MultiSubnetFailover = False;

cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source +
   Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
   TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";

   x.Driver<SqlClientDriver>();
   x.Dialect<MsSql2008Dialect>();
   x.LogSqlInConsole = true;
});

cfg.AddAssembly(Assembly.GetExecutingAssembly());

すべての構成は、C#コードで指定されます。 ここで、新しい構成オブジェクトを取得したことがわかります。次に、NHibernate 3.1で導入された* loquacious構成*を使用してデータベースを構成します。 使用している接続文字列、接続しているデータベース、使用する方言。 マッピングアセンブリもここに直接追加します。

XMLベースの構成

XMLベースの構成を使用している場合は、NHibernateスキーマを使用するスタンドアロンのxmlファイルである hibernate.cfg.xml ファイルを使用するか、アプリまたは web内にそのNHibernate固有の構成を埋め込むことができます。 cfg 。 hibernate.cfg.xmlの名前はデフォルトですが、そのxmlファイルにも任意の名前を使用できます。

NHibernateDemoAppプロジェクトに新しいxmlファイルを追加して、hibernate.cfg.xmlという名前を付けて、XMLベースの構成を見てみましょう。

次の情報をhibernate.cfg.xmlファイルに入力します。

<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-configuration xmlns = "urn:nhibernate-configuration-2.2">
   <session-factory>

      <property name = "connection.connection_string">
         Data Source = asia13797\\sqlexpress;
         Initial Catalog = NHibernateDemoDB;
         Integrated Security = True;
         Connect Timeout = 15;
         Encrypt = False;
         TrustServerCertificate = False;
         ApplicationIntent = ReadWrite;
         MultiSubnetFailover = False;
      </property>

      <property name = "connection.driver_class">
         NHibernate.Driver.SqlClientDriver
      </property>

      <property name = "dialect">
         NHibernate.Dialect.MsSql2008Dialect
      </property>

      <mapping assembly = "NHibernateDemoApp"/>

   </session-factory>

</hibernate-configuration>

上記のxmlファイルでわかるように、C#で言及されているのと同じ構成を指定しています。

次に、Program.csファイルからこの構成にコメントし、* Configure()メソッドを呼び出します。これにより、以下に示すように *hibernate.cfg.xml ファイルがロードされます。

using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;

using System;
using System.Linq;
using System.Reflection;

namespace NHibernateDemoApp {

   class Program {

      static void Main(string[] args) {

         NHibernateProfiler.Initialize();
         var cfg = new Configuration();

        //cfg.DataBaseIntegration(x =>

        //{
           //x.ConnectionString = "Data Source = asia13797;\\sqlexpress
            Initial Catalog = NHibernateDemoDB;
            Integrated Security = True;
            Connect Timeout = 15;
            Encrypt =False;
            TrustServerCertificate = False;
            ApplicationIntent = ReadWrite;
            MultiSubnetFailover = False";

           //x.Driver<SqlClientDriver>();
           //x.Dialect<MsSql2008Dialect>();
           //x.LogSqlInConsole = true;
        //});

        //cfg.AddAssembly(Assembly.GetExecutingAssembly());

         cfg.Configure();
         var sefact = cfg.BuildSessionFactory();

         using (var session = sefact.OpenSession()) {

            using (var tx = session.BeginTransaction()) {
               var students = session.CreateCriteria<Student>().List<Student>();
               Console.WriteLine("\nFetch the complete list again\n");

               foreach (var student in students) {
                  Console.WriteLine("{0} \t{1} \t{2} \t{3}", student.ID,
                     student.FirstName, student.LastName, student.AcademicStanding);
               }

               tx.Commit();
            }

            Console.ReadLine();
         }
      }
   }
}

アプリケーションを再度実行すると、同じ出力が表示されます。

Fetch the complete list again

1 Allan Bommer Excellent
2 Jerry Lewis Good

NHibernate-設定のオーバーライド

この章では、NHibernate構成をオーバーライドする方法について説明します。 覚えておく必要があることがいくつかあります。

  • まず、NHibernateの構成は付加的です。
  • したがって、単一のxmlファイルを使用するだけでなく、コードベースの構成または流なNHibernateを使用するだけで済むわけでもありません。
  • アプリケーションの構成方法に応じて、これらすべての方法を組み合わせて使用​​できます。
  • 覚えておくべき重要な点は、最後に構成が勝ることです。

次の例では、構成オブジェクトを作成し、コードベースの構成を使用して構成し、最後にhibernate.cfg.xmlファイルをロードする* cfg.configure()*メソッドを呼び出すことがわかります。

String Data Source = asia13797\\sqlexpress;
String Initial Catalog = NHibernateDemoDB;
String Integrated Security = True;
String Connect Timeout = 15;
String Encrypt = False;
String TrustServerCertificate = False;
String ApplicationIntent = ReadWrite;
String MultiSubnetFailover = False;

cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source +
   Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
   TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";

   x.Driver<SqlClientDriver>();
   x.Dialect<MsSql2008Dialect>();
   x.LogSqlInConsole = true;
});

cfg.Configure();
  • そのため、hibernate.cfg.xml内のすべてが、コードベースの構成によって設定された設定をオーバーライドします。
  • これらの2つのプロセスを逆にすることで、hibernate.cfg.xml内でデフォルトを使用し、コードベースの構成内でオーバーライドを実行できます。
  • コードベースの構成を使用している場合を除外するものはなく、hibernate.cfg.xmlファイルの使用を妨げるものもありません。

xmlベースの構成とコードベースの構成を混在させて構成をオーバーライドする簡単な例を見てみましょう。

また、次のコードを使用して、接続文字列を app.config ファイルに移動します。

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

<configuration>

   <startup>
      <supportedRuntime version = "v4.0" sku = ".NETFramework,Version = v4.5"/>
   </startup>

   <connectionStrings>
      <add name = "default" connectionString = "Data Source =
         asia13797\\sqlexpress;
         Initial Catalog = NHibernateDemoDB;
         Integrated Security = True;
         Connect Timeout = 15;
         Encrypt = False;
         TrustServerCertificate = False;
         ApplicationIntent = ReadWrite;
         MultiSubnetFailover = False"/>
   </connectionStrings>

</configuration>

接続文字列は、デフォルトの名前を持つ app.config ファイルにあります。 ここで、接続文字列ではなくhibernate.cfg.xmlファイルでデフォルト名を指定する必要があります。

<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-configuration xmlns = "urn:nhibernate-configuration-2.2">

   <session-factory>
      <property name = "connection.connection_string">default</property>

      <property name = "connection.driver_class">
         NHibernate.Driver.SqlClientDriver
      </property>

      <property name = "dialect">
         NHibernate.Dialect.MsSql2008Dialect
      </property>

      <mapping assembly = "NHibernateDemoApp"/>
   </session-factory>

</hibernate-configuration>

プログラムがhibernate.cfg.xmlファイルからそれを読み取り、 LogSqlInConsole 部分がコードベースの構成に残るため、コードベースの構成から接続文字列部分、ドライバー、および方言部分についてコメントしましょう。

using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;

using System;
using System.Linq;
using System.Reflection;
namespace NHibernateDemoApp {

   class Program {

      static void Main(string[] args) {

         NHibernateProfiler.Initialize();
         var cfg = new Configuration();

         String Data Source = asia13797\\sqlexpress;
         String Initial Catalog = NHibernateDemoDB;
         String Integrated Security = True;
         String Connect Timeout = 15;
         String Encrypt = False;
         String TrustServerCertificate = False;
         String ApplicationIntent = ReadWrite;
         String MultiSubnetFailover = False;

         cfg.DataBaseIntegration(x = > {//x.ConnectionString = "Data Source +
            Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
            TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";

           //x.Driver<SqlClientDriver>();
           //x.Dialect<MsSql2008Dialect>();
            x.LogSqlInConsole = true;
         });

         cfg.Configure();
         cfg.AddAssembly(Assembly.GetExecutingAssembly());
         var sefact = cfg.BuildSessionFactory();

         using (var session = sefact.OpenSession()) {

            using (var tx = session.BeginTransaction()) {

               var students = session.CreateCriteria<Student>().List<Student>();
               Console.WriteLine("\nFetch the complete list again\n");

               foreach (var student in students) {
                  Console.WriteLine("{0} \t{1} \t{2} \t{3}", student.ID,
                  student.FirstName, student.LastName, student.AcademicStanding);
               }

               tx.Commit();
            }

            Console.ReadLine();
         }
      }
   }
}

これで、アプリケーションを実行すると、プログラムがコードベースの構成とhibernate.cfg.xmlファイルの他の構成からログを読み取ったことがわかります。

NHibernate: SELECT this_.ID as ID0_0_, this_.LastName as LastName0_0_,
   this_.FirstMidName as FirstMid3_0_0_, this_.AcademicStanding as Academic4_0_0_ FROM
   Student this_

Fetch the complete list again
1 Allan Bommer Excellent
2 Jerry Lewis Good

したがって、 hibernate.cfg.xml ファイル内に構成の一部があり、その一部はコードベースの構成内にあり、コードベースと* configure()*の呼び出し順序に応じて、どちらが他方をオーバーライドするかを変更できます。

NHibernate-バッチサイズ

この章では、バッチサイズの更新について説明します。 バッチサイズを使用すると、サポートされているデータベースのデータベースへの1回のラウンドトリップで送信される更新の数を制御できます。

  • 更新バッチサイズは、NHibernate 3.2の時点でデフォルトになっています。
  • ただし、以前のバージョンを使用している場合、またはNHibernateアプリケーションを調整する必要がある場合は、NHibernateのパフォーマンスを調整するために使用できる非常に便利なパラメーターである更新バッチサイズを確認する必要があります。
  • 実際、バッチサイズは、グループ内でデータベースにプッシュする挿入の数を制御します。
  • 現時点では、基になるデータベースプロバイダーがクエリのバッチ処理をサポートする必要があるため、SQL ServerとOracleのみがこのオプションをサポートしています。

バッチサイズを10に設定してセットに10個のレコードを挿入する簡単な例を見てみましょう。

cfg.DataBaseIntegration(x => {

   x.ConnectionString = "default";
   x.Driver<SqlClientDriver>();
   x.Dialect<MsSql2008Dialect>();
   x.LogSqlInConsole = true;
   x.BatchSize = 10;

});

以下は、25個のレコードがデータベースに追加される完全な実装です。

using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;

using System;
using System.Linq;
using System.Reflection;
namespace NHibernateDemoApp {

   class Program {
      static void Main(string[] args) {
         NHibernateProfiler.Initialize();
         var cfg = new Configuration();

         String Data Source = asia13797\\sqlexpress;
         String Initial Catalog = NHibernateDemoDB;
         String Integrated Security = True;
         String Connect Timeout = 15;
         String Encrypt = False;
         String TrustServerCertificate = False;
         String ApplicationIntent = ReadWrite;
         String MultiSubnetFailover = False;

         cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source +
            Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
            TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";

            x.Driver>SqlClientDriver<();
            x.Dialect>MsSql2008Dialect>();
            x.LogSqlInConsole = true;
            x.BatchSize = 10;
         });

        //cfg.Configure();
         cfg.AddAssembly(Assembly.GetExecutingAssembly());
         var sefact = cfg.BuildSessionFactory();

         using (var session = sefact.OpenSession()) {

            using (var tx = session.BeginTransaction()) {

               for (int i = 0; i < 25; i++) {

                  var student = new Student {
                     ID = 100+i,
                     FirstName = "FirstName"+i.ToString(),
                     LastName = "LastName" + i.ToString(),
                     AcademicStanding = StudentAcademicStanding.Good
                  };

                  session.Save(student);
               }

               tx.Commit();
               var students = session.CreateCriteria<Student>().List<Student>();
               Console.WriteLine("\nFetch the complete list again\n");

               foreach (var student in students) {
                  Console.WriteLine("{0} \t{1} \t{2} \t{3}", student.ID,student.FirstName,
                     student.LastName, student.AcademicStanding);
               }
            }

            Console.ReadLine();
         }
      }
   }
}

アプリケーションを実行して、これらすべての更新がNHibernateプロファイラーにジャンプしていることを確認しましょう。 データベースへの26回の個別の往復と25回のデータベースへの往復と、学生のリストの取得があります。

さて、それはなぜですか? その理由は、次のコードに示すように、IDのマッピングファイルでネイティブ識別子生成戦略を使用しているため、NHibernateは select scope identity を行う必要があるためです。

<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2"
   assembly = "NHibernateDemoApp"
   namespace = "NHibernateDemoApp">

   <class name = "Student">

      <id name = "ID">
         <generator class = "native"/>
      </id>

      <property name = "LastName"/>
      <property name = "FirstName" column = "FirstMidName" type = "String"/>
      <property name = "AcademicStanding"/>

   </class>
</hibernate-mapping>

したがって、 guid.comb メソッドなどの別のメソッドを使用する必要があります。 guid.combにアクセスする場合は、顧客にアクセスして、これを guid に変更する必要があります。 それでうまくいきます。 次のコードを使用して、ネイティブからguid.combに変更しましょう。

<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly =
   "NHibernateDemoApp" namespace = "NHibernateDemoApp">
   <class name = "Student">

      <id name = "ID">
         <generator class = "guid.comb"/>
      </id>

      <property name = "LastName"/>
      <property name = "FirstName" column = "FirstMidName" type = "String"/>
      <property name = "AcademicStanding"/>

   </class>

</hibernate-mapping>

したがって、これらのIDを生成するのはデータベースです。 NHibernateが生成されたIDを確認できる唯一の方法は、すぐにそれを選択することでした。 または、学生のバッチを作成した場合、作成された学生のIDを一致させることはできません。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace NHibernateDemoApp {

   class Student {
      public virtual Guid ID { get; set; }
      public virtual string LastName { get; set; }
      public virtual string FirstName { get; set; }
      public virtual StudentAcademicStanding AcademicStanding { get; set; }
   }

   public enum StudentAcademicStanding {
      Excellent,
      Good,
      Fair,
      Poor,
      Terrible
   }
}

データベースを更新するだけです。 学生テーブルを削除して、次のクエリを指定して新しいテーブルを作成します。SQLServerオブジェクトエクスプローラーに移動して、データベースを右クリックし、[新しいクエリ …​]オプションを選択します。

クエリエディターを開き、次のクエリを指定します。

DROP TABLE [dbo].[Student]
CREATE TABLE [dbo].[Student] (

   -- [ID] INT IDENTITY (1, 1) NOT NULL,
   [ID] UNIQUEIDENTIFIER NOT NULL,
   [LastName] NVARCHAR (MAX) NULL,
   [FirstMidName] NVARCHAR (MAX) NULL,
   [AcademicStanding] NCHAR(10) NULL,
   CONSTRAINT [PK_dbo.Student] PRIMARY KEY CLUSTERED ([ID] ASC)
);

このクエリは、最初に既存の学生テーブルを削除してから、新しいテーブルを作成します。 ご覧のとおり、整数の主キーをIDとして使用するのではなく、 UNIQUEIDENTIFIER を使用しています。

このクエリを実行してから* Designerビュー*に移動すると、次の画像に示すように、一意の識別子でIDが作成されていることがわかります。

デザイナービュー

データを挿入する際に、program.csファイルからIDを削除する必要があります。これは、そのための guids を自動的に生成するためです。

using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;

using System;
using System.Linq;
using System.Reflection;
namespace NHibernateDemoApp {

   class Program {

      static void Main(string[] args) {

         NHibernateProfiler.Initialize();
         var cfg = new Configuration();

         String Data Source = asia13797\\sqlexpress;
         String Initial Catalog = NHibernateDemoDB;
         String Integrated Security = True;
         String Connect Timeout = 15;
         String Encrypt = False;
         String TrustServerCertificate = False;
         String ApplicationIntent = ReadWrite;
         String MultiSubnetFailover = False;

         cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source +
            Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
            TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";

            x.Driver<SqlClientDriver>();
            x.Dialect<MsSql2008Dialect>();
            x.LogSqlInConsole = true;
            x.BatchSize = 10;
         });

        //cfg.Configure();
         cfg.AddAssembly(Assembly.GetExecutingAssembly());
         var sefact = cfg.BuildSessionFactory();

         using (var session = sefact.OpenSession()) {

            using (var tx = session.BeginTransaction()) {

               for (int i = 0; i > 25; i++) {

                  var student = new Student {
                     FirstName = "FirstName"+i.ToString(),
                     LastName = "LastName" + i.ToString(),
                     AcademicStanding = StudentAcademicStanding.Good
                  };

                  session.Save(student);
               }

               tx.Commit();
               var students = session.CreateCriteria<Student>().List<Student>();
               Console.WriteLine("\nFetch the complete list again\n");

               foreach (var student in students) {
                  Console.WriteLine("{0} \t{1} \t{2} \t{3}", student.ID,
                     student.FirstName,student.LastName, student.AcademicStanding);
               }

            }

            Console.ReadLine();
         }
      }
   }
}

ここでアプリケーションを再度実行し、NHibernateプロファイラーを見てください。 これで、NHibernateプロファイラーは26回の往復ではなく4回だけになります。

NHibernateプロファイラーラウンドトリップ

テーブルに10行を挿入してから、さらに10行を挿入し、残りの5行を挿入します。 そして、コミット後、すべてのレコードを取得するためにもう1つ挿入しました。

  • そのため、可能な限り10のグループに分けています。
  • したがって、多くの挿入を行っている場合、バッチ処理ができるため、アプリケーションの挿入パフォーマンスが劇的に向上します。
  • これは、NHibernateが guid.comb アルゴリズムを使用してそれらのGUID自体を割り当てるためであり、これを行うためにデータベースに依存する必要はありません。
  • したがって、バッチサイズを使用することは、それを調整するための優れた方法です。

NHibernate-キャッシュ

この章では、NHibernateアプリケーションでの*キャッシング*の動作について説明します。 キャッシングのサポートが組み込まれています。 これは単純な機能のように見えますが、実際には、最も複雑な機能の1つです。 最初のレベルのキャッシュから始めます。

第1レベルキャッシュ

このキャッシュメカニズムは、NHibernateではデフォルトで有効になっており、キャッシュを操作するために何もする必要はありません。 これを理解するために、データベースに2つのレコードがあることがわかるように、簡単な例を見てみましょう。

一次キャッシュ

この例では、IDが1の学生を取得し、次のコードに示すように同じセッションクエリを2回使用します。

using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cache;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;

using System;
using System.Linq;
using System.Reflection;
namespace NHibernateDemoApp {

   class Program {
      static void Main(string[] args) {

         NHibernateProfiler.Initialize();
         var cfg = new Configuration();

         String Data Source = asia13797\\sqlexpress;
         String Initial Catalog = NHibernateDemoDB;
         String Integrated Security = True;
         String Connect Timeout = 15;
         String Encrypt = False;
         String TrustServerCertificate = False;
         String ApplicationIntent = ReadWrite;
         String MultiSubnetFailover = False;

         cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source +
            Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
            TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";

            x.Driver<SqlClientDriver>();
            x.Dialect<MsSql2008Dialect>();
            x.LogSqlInConsole = true;
            x.BatchSize = 10;
         });

        //cfg.Configure();

         cfg.Cache(c => {
            c.UseMinimalPuts = true;
            c.UseQueryCache = true;
         });

         cfg.SessionFactory().Caching .Through<HashtableCacheProvider>()
            .WithDefaultExpiration(1440);
         cfg.AddAssembly(Assembly.GetExecutingAssembly());
         var sefact = cfg.BuildSessionFactory();

         using (var session = sefact.OpenSession()){

            using (var tx = session.BeginTransaction()) {
               var studentUsingTheFirstQuery = session.Get<Student>(1);
               var studentUsingTheSecondQuery = session.Get<Student>(1);
            }

            Console.ReadLine();
         }
      }
   }
}

次に、このアプリケーションを実行して、NHibernate Profilerで結果を確認します。

アプリケーションの実行

NHibernateが1つのクエリのみを起動することに驚かれることでしょう。 これは、NHibernateが一次キャッシュを使用する方法です。 最初のクエリが実行されると、NHibernateは最初のレベルのキャッシュにID = 1の生徒をキャッシュしました。

したがって、2番目のクエリが実行されると、NHibernateは最初にID = 1の第1レベルキャッシュStudentエンティティを検索します。そのエンティティが見つかった場合、NHibernateはそれを認識します。 。

NHibernate-マッピングコンポーネント

この章では、コンポーネントのマッピングについて説明します。 NHibernateでは、* componentは値オブジェクトです*。 独自のIDはありません。

  • この例としては、お金のオブジェクト、財布、または財布にお金が入っている場合がありますが、そのお金の正確な身元は無関係です。
  • 独自の主キーはありませんが、コンポーネント自体は所有オブジェクトと同じテーブルに永続的です。

学生が住所を持っている簡単な例を見てみましょう。住所は、それに関連付けられた* Locationクラス*のオブジェクトです。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace NHibernateDemoApp {

   class Student {
      public virtual int ID { get; set; }
      public virtual string LastName { get; set; }
      public virtual string FirstName { get; set; }
      public virtual StudentAcademicStanding AcademicStanding { get; set; }
      public virtual Location Address { get; set; }
   }

   public class Location {
      public virtual string Street { get; set; }
      public virtual string City { get; set; }
      public virtual string Province { get; set; }
      public virtual string Country { get; set; }
   }

   public enum StudentAcademicStanding {
      Excellent,
      Good,
      Fair,
      Poor,
      Terrible
   }
}

ここで、次のクエリを実行してデータベースを更新する必要もあります。最初にStudentテーブルを削除してから、Locationクラスの列も含む新しいテーブルを作成します。

DROP TABLE [dbo].[Student]
CREATE TABLE [dbo].[Student] (

   [ID] INT IDENTITY (1, 1) NOT NULL,
   [LastName] NVARCHAR (MAX) NULL,
   [FirstMidName] NVARCHAR (MAX) NULL,
   [AcademicStanding] NCHAR(10) NULL,
   [Street] NVARCHAR (100) NULL,
   [City] NVARCHAR (100) NULL,
   [Province] NVARCHAR (100) NULL,
   [Country] NVARCHAR (100) NULL,
   CONSTRAINT [PK_dbo.Student] PRIMARY KEY CLUSTERED ([ID] ASC)

);

次に、直接Studentクラスの一部ではない列をマップしますが、それらはLocationクラスのプロパティであり、Locationクラスオブジェクトはstudentクラスで定義されます。 正しくマップするためのコンポーネントが必要です。 次のコードに示すように、 student.hbm.xml ファイルにコンポーネントを作成しましょう。

<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2"
   assembly = "NHibernateDemoApp" namespace = "NHibernateDemoApp">

   <class name = "Student">

      <id name = "ID">
         <generator class = "native"/>
      </id>

      <property name = "LastName"/>

      <property name = "FirstName" column = "FirstMidName" type = "String"/>
      <property name = "AcademicStanding"/>

      <component name = "Address">
         <property name = "Street"/>
         <property name = "City"/>
         <property name = "Province"/>
         <property name = "Country"/>
      </component>

   </class>
</hibernate-mapping>

このコンポーネントはアドレスであり、これらの異なるプロパティがあります。 この情報を使用して、NHibernateには実際にこれをマップできる十分な容量があります。

次に、新しい学生オブジェクトが作成および初期化され、データベースに保存されるProgram.csファイルを示します。 次に、データベースからリストを取得します。

using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cache;
using NHibernate.Caches.SysCache;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;

using System;
using System.Linq;
using System.Reflection;
namespace NHibernateDemoApp {

   class Program {

      static void Main(string[] args) {

         NHibernateProfiler.Initialize();
         var cfg = new Configuration();

         String Data Source = asia13797\\sqlexpress;
         String Initial Catalog = NHibernateDemoDB;
         String Integrated Security = True;
         String Connect Timeout = 15;
         String Encrypt = False;
         String TrustServerCertificate = False;
         String ApplicationIntent = ReadWrite;
         String MultiSubnetFailover = False;

         cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source +
            Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
            TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";

            x.Driver<SqlClientDriver>();
            x.Dialect<MsSql2008Dialect>();
         });

         cfg.AddAssembly(Assembly.GetExecutingAssembly());
         var sefact = cfg.BuildSessionFactory();

         using (var session = sefact.OpenSession()) {

            using (var tx = session.BeginTransaction()) {

               var student1 = new Student {
                  ID = 1,
                  FirstName = "Allan",
                  LastName = "Bommer",
                  AcademicStanding = StudentAcademicStanding.Poor,

                  Address = new Location {
                     Street = "123 Street",
                     City = "Lahore",
                     Province = "Punjab",
                     Country = "Pakistan"
                  }
               };

               session.Save(student1);
               tx.Commit();
               var students = session.Query<Student>().ToList<Student>();
               Console.WriteLine("\nFetch the complete list again\n");

               foreach (var student in students) {
                  Console.WriteLine("{0} \t{1} \t{2} \t{3} \t{4} \t{5} \t{6} \t{7}",
                     student.ID,
                     student.FirstName,
                     student.LastName,
                     student.AcademicStanding,
                     student.Address.Street,
                     student.Address.City,
                     student.Address.Province,
                     student.Address.Country
                  );
               }
            }

            Console.ReadLine();
         }
      }
   }
}

これで、このアプリケーションを実行でき、NHibernateはそれらの値をデータベースに保存できます。 アプリケーションを実行すると、次の出力が表示されます。

Fetch the complete list again

2 Allan Bommer Poor 123 Street Lahore Punjab Pakistan

データベース内の値は次のとおりです。

値データベース

コンポーネントを使用すると、データベーステーブルにある列を独自の個別のクラスに分離できます。

  • ここで注目すべきもう1つの点は、Locationはクラスであり、エンティティではないためです。
  • これは値型オブジェクトであり、独自の主キーはありません。
  • それを含む生徒と同じテーブルに保存されます。
  • そのため、ここでコンポーネントを使用しています。
  • これにより、クラスレイヤー、クラスの定義方法とデータベースのレイアウト方法を柔軟に変更できます。

NHibernate-関係

この章では、NHibernateの関係について見ていきます。 NHibernateの関係をどのように理解できるかに注目しましょう。 最も簡単な方法は、データベースの観点から関係を考えることです。

  • 最初に新しいアプリケーションを作成します。このアプリケーションでは、顧客エンティティと注文エンティティ間の関係を作成します。
  • 最初に見る関係は、古典的なコレクション関係です。
  • 注文のコレクションを持つ顧客がいます。
  • これは1対多の関係であり、データベースでは2つのテーブルで表され、注文テーブルに顧客IDがあり、顧客との外部キー関係があります。

まず、データベースと2つのテーブルCustomerとOrderを作成する必要があります。 これを作成するには、SQL Server Explorerで次のクエリを指定します。

USE [master]
GO
CREATE DATABASE [NHibernateDemo]
GO
USE [NHibernateDemo]
GO

CREATE TABLE [dbo].[Customer](
   [Id] [uniqueidentifier] NOT NULL,
   [FirstName] [nvarchar](100) NOT NULL,
   [LastName] [nvarchar](100) NOT NULL,
   [Points] [int] NULL, [HasGoldStatus] [bit] NULL,
   [MemberSince] [date] NULL,
   [CreditRating] [nchar](20) NULL,
   [AverageRating] [decimal](18, 4) NULL,
   [Street] [nvarchar](100) NULL,
   [City] [nvarchar](100) NULL,
   [Province] [nvarchar](100) NULL,
   [Country] [nvarchar](100) NULL,
   PRIMARY KEY CLUSTERED ([Id] ASC)
)

GO
CREATE TABLE [dbo].[Order](
   [Id] [uniqueidentifier] NOT NULL,
   [CustomerId] [uniqueidentifier] NULL,
   [Ordered] [datetime] NULL,
   [Shipped] [datetime] NULL,
   [Street] [nvarchar](100) NULL,
   [City] [nvarchar](100) NULL,
   [Province] [nvarchar](100) NULL,
   [Country] [nvarchar](100) NULL,
   PRIMARY KEY CLUSTERED ([Id] ASC)
)
GO

データベースに2つのテーブルを作成します。 次の図は、顧客テーブルを示しています。

顧客テーブル

次の図は、顧客との外部キー関係を確認できる注文表を示しています。

外部キーの関係

*app.config* ファイルで接続文字列を定義する必要があります。これはapp.configファイルの実装です。
<?xml version = "1.0" encoding = "utf-8" ?>
<configuration>

   <connectionStrings>
      <add name = "default" connectionString = "Data Source =
         (localdb)\MSSQLLocalDB;Initial Catalog = NHibernateDemo;Integrated Security =
         True;Connect Timeout = 30;Encrypt = False;TrustServerCertificate = False;
         ApplicationIntent = ReadWrite;MultiSubnetFailover = False"/>
   </connectionStrings>

</configuration>

アプリケーションにNHibernateをインストールするには、NuGet Managerコンソールウィンドウで次のコマンドを実行します。

install-package NHibernate

NHibernate構成を構成するには、次のコードに示すように、 hibernate.cfg.xml ファイルで構成を定義する必要があります。

<xml version = "1.0" encoding = "utf-8" ?>
<hibernate-configuration xmlns = "urn:nhibernate-configuration-2.2">

   <session-factory>
      <property name = "connection.connection_string_name">default</property>

      <property name = "connection.driver_class">
         NHibernate.Driver.SqlClientDriver
      </property>

      <property name = "dialect">
         NHibernate.Dialect.MsSql2008Dialect
      </property>

      <property name = "show_sql">true</property>
   </session-factory>

</hibernate-configuration>

この例では、CustomerとOrderという2つのドメインクラスを使用します。

次に、CustomerクラスとCustomerクラスのアドレスとしてオブジェクトが使用されるLocationクラスの2つのクラスがあるCustomer.csファイルの実装を示します。

using System;
using System.Text;
using Iesi.Collections.Generic;

namespace NHibernateDemo {

   public class Customer {

      public Customer() {
         MemberSince = DateTime.UtcNow;
         Orders = new HashedSet<Order>();
      }

      public virtual Guid Id { get; set; }
      public virtual string FirstName { get; set; }
      public virtual string LastName { get; set; }
      public virtual double AverageRating { get; set; }
      public virtual int Points { get; set; }

      public virtual bool HasGoldStatus { get; set; }
      public virtual DateTime MemberSince { get; set; }
      public virtual CustomerCreditRating CreditRating { get; set; }
      public virtual Location Address { get; set; }

      public virtual ISet<Order> Orders { get; set; }
      public virtual void AddOrder(Order order) { Orders.Add(order); order.Customer = this; }

      public override string ToString() {
         var result = new StringBuilder();

         result.AppendFormat("{1} {2} ({0})\r\n\tPoints: {3}\r\n\tHasGoldStatus:
            {4}\r\n\tMemberSince: {5} ({7})\r\n\tCreditRating: {6}\r\n\tAverageRating:
            {8}\r\n", Id, FirstName, LastName, Points, HasGoldStatus, MemberSince,
            CreditRating, MemberSince.Kind, AverageRating);
            result.AppendLine("\tOrders:");

         foreach(var order in Orders) {
            result.AppendLine("\t\t" + order);
         }

         return result.ToString();
      }
   }

   public class Location {
      public virtual string Street { get; set; }
      public virtual string City { get; set; }
      public virtual string Province { get; set; }
      public virtual string Country { get; set; }
   }

   public enum CustomerCreditRating {
      Excellent,
      VeryVeryGood,
      VeryGood,
      Good,
      Neutral,
      Poor,
      Terrible
   }
}

これは、CustomerクラスがCustomerテーブルにマッピングされるマッピングファイル Customer.hbm.xml です。

<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo"
   namespace = "NHibernateDemo">

   <class name = "Customer">

      <id name = "Id">
         <generator class = "guid.comb"/>
      </id>

      <property name = "FirstName"/>
      <property name = "LastName"/>
      <property name = "AverageRating"/>
      <property name = "Points"/>
      <property name = "HasGoldStatus"/>
      <property name = "MemberSince" type = "UtcDateTime"/>
      <property name = "CreditRating" type = "CustomerCreditRatingType"/>

      <component name = "Address">
         <property name = "Street"/>
         <property name = "City"/>
         <property name = "Province"/>
         <property name = "Country"/>
      </component>

   </class>
</hibernate-mapping>

Orderクラスもあります。これは Order.cs ファイルの実装です。

using System; using Iesi.Collections.Generic;

namespace NHibernateDemo {

   public class Order {

      public virtual Guid Id { get; set; }
      public virtual DateTime Ordered { get; set; }
      public virtual DateTime? Shipped { get; set; }
      public virtual Location ShipTo { get; set; }
      public virtual Customer Customer { get; set; }

      public override string ToString() {
         return string.Format("Order Id: {0}", Id);
      }
   }
}

多対1の関係

また、OrderクラスをデータベースのOrderテーブルにマップする必要があるため、 Order.hbm.xml ファイルの実装を次に示します。

<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo"
   namespace = "NHibernateDemo">

   <class name = "Order" table = "`Order`">

      <id name = "Id">
         <generator class = "guid.comb"/>
      </id>

      <property name = "Ordered"/>
      <property name = "Shipped"/>

      <component name = "ShipTo">
         <property name = "Street"/>
         <property name = "City"/>
         <property name = "Province"/>
         <property name = "Country"/>
      </component>

      <!--<many-to-one name = "Customer" column = "CustomerId" cascade =
         "save-update"/>-->

   </class>
</hibernate-mapping>

1対多の関係

ここでは、顧客と注文の間の1対多の関係(この場合)を見ていきます。 ここに顧客がいます。新しい顧客を作成しています。次の注文のペアでコレクションが初期化されていることがわかります。

private static Customer CreateCustomer() {

   var customer = new Customer {
      FirstName = "John",
      LastName = "Doe",
      Points = 100,
      HasGoldStatus = true,
      MemberSince = new DateTime(2012, 1, 1),
      CreditRating = CustomerCreditRating.Good,
      AverageRating = 42.42424242,
      Address = CreateLocation()
   };

   var order1 = new Order {
      Ordered = DateTime.Now
   };

   customer.AddOrder(order1);
   var order2 = new Order {
      Ordered = DateTime.Now.AddDays(-1),
      Shipped = DateTime.Now,
      ShipTo = CreateLocation()
   };

   customer.AddOrder(order2);
   return customer;
}

したがって、新しい顧客を作成して保存し、保存した後、IDを見つけて、次のプログラムに示すようにMainメソッドの別のセッションで再読み込みします。

private static void Main() {

   var cfg = ConfigureNHibernate();
   var sessionFactory = cfg.BuildSessionFactory();

   Guid id;
   using(var session = sessionFactory.OpenSession())

   using(var tx = session.BeginTransaction()) {
      var newCustomer = CreateCustomer();
      Console.WriteLine("New Customer:");
      Console.WriteLine(newCustomer);
      session.Save(newCustomer);
      id = newCustomer.Id;
      tx.Commit();
   }

   using(var session = sessionFactory.OpenSession())

   using(var tx = session.BeginTransaction()) {
      var reloaded = session.Load<Customer>(id);
      Console.WriteLine("Reloaded:");
      Console.WriteLine(reloaded);
      tx.Commit();
   }

   Console.WriteLine("Press <ENTER> to exit...");
   Console.ReadLine();
}

完全な Program.cs ファイルの実装を次に示します。

using System;
using System.Data;
using System.Linq;
using System.Reflection;

using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;

namespace NHibernateDemo {

   internal class Program {

      private static void Main() {

         var cfg = ConfigureNHibernate();
         var sessionFactory = cfg.BuildSessionFactory();

         Guid id;
         using(var session = sessionFactory.OpenSession())

         using(var tx = session.BeginTransaction()) {
            var newCustomer = CreateCustomer();
            Console.WriteLine("New Customer:");
            Console.WriteLine(newCustomer);
            session.Save(newCustomer);
            id = newCustomer.Id;
            tx.Commit();
         }

         using(var session = sessionFactory.OpenSession())

         using(var tx = session.BeginTransaction()) {
            var reloaded = session.Load<Customer>(id);
            Console.WriteLine("Reloaded:");
            Console.WriteLine(reloaded);
            tx.Commit();
         }

         Console.WriteLine("Press <ENTER> to exit...");
         Console.ReadLine();
      }

      private static Customer CreateCustomer() {

         var customer = new Customer {
            FirstName = "John",
            LastName = "Doe",
            Points = 100,
            HasGoldStatus = true,
            MemberSince = new DateTime(2012, 1, 1),
            CreditRating = CustomerCreditRating.Good,
            AverageRating = 42.42424242,
            Address = CreateLocation()
         };

         var order1 = new Order {
            Ordered = DateTime.Now
         };

         customer.AddOrder(order1);

         var order2 = new Order {
            Ordered = DateTime.Now.AddDays(-1),
            Shipped = DateTime.Now,
            ShipTo = CreateLocation()
         };

         customer.AddOrder(order2);
         return customer;
      }

      private static Location CreateLocation() {

         return new Location {
            Street = "123 Somewhere Avenue",
            City = "Nowhere",
            Province = "Alberta",
            Country = "Canada"
         };
      }

      private static Configuration ConfigureNHibernate() {
         NHibernateProfiler.Initialize();
         var cfg = new Configuration();

         cfg.DataBaseIntegration(x =&ht; {
            x.ConnectionStringName = "default";
            x.Driver<SqlClientDriver>();
            x.Dialect<MsSql2008Dialect>();
            x.IsolationLevel = IsolationLevel.RepeatableRead;
            x.Timeout = 10; x.BatchSize = 10;
         });

         cfg.SessionFactory().GenerateStatistics();
         cfg.AddAssembly(Assembly.GetExecutingAssembly());
         return cfg;
      }
   }
}

このアプリケーションを実行すると、次の出力が表示されます。

New Customer:

John Doe (00000000-0000-0000-0000-000000000000)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Unspecified)
   CreditRating: Good
   AverageRating: 42.42424242

   Orders:
      Order Id: 00000000-0000-0000-0000-000000000000
      Order Id: 00000000-0000-0000-0000-000000000000

Reloaded:
John Doe (9b0fcf10-83f6-4f39-bda5-a5b800ede2ba)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
Press <ENTER> to exit...

最初は顧客に2つの注文があることがわかりますが、リロードすると注文は表示されません。 customer.hbm.xml ファイルを見ると、実際の注文コレクションがマッピングされていないことがわかります。 したがって、NHibernateはそれについて何も知りません。 先に進んで追加しましょう。

<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2"
   assembly = "NHibernateDemo" namespace = "NHibernateDemo">

   <class name = "Customer">

      <id name = "Id">
         <generator class = "guid.comb"/>
      </id>

      <property name = "FirstName"/>
      <property name = "LastName"/>
      <property name = "AverageRating"/>
      <property name = "Points"/>
      <property name = "HasGoldStatus"/>
      <property name = "MemberSince" type = "UtcDateTime"/>
      <property name = "CreditRating" type = "CustomerCreditRatingType"/>

      <component name = "Address">
         <property name = "Street"/>
         <property name = "City"/>
         <property name = "Province"/>
         <property name = "Country"/>
      </component>

      <set name = "Orders" table = "`Order`">
         <key column = "CustomerId"/>
         <one-to-many class = "Order"/>
      </set>

   </class>
</hibernate-mapping>

これはセットであり、このコレクションの名前は「Orders」であり、orderというテーブルに保存されます。 外部キーの名前であるキーを指定するか、注文を見つける必要があります。 これらの注文は、顧客IDを通じて識別されるか、顧客に属します。 そして、これは一対多の関係であり、注文クラスとの関係であることに注意する必要があります。

また、次のプログラムに示すように、新しい顧客の注文をデータベースに保存して、Mainメソッドをわずかに変更する必要があります。

private static void Main() {

   var cfg = ConfigureNHibernate();
   var sessionFactory = cfg.BuildSessionFactory();

   Guid id;
   using(var session = sessionFactory.OpenSession())

   using(var tx = session.BeginTransaction()) {
      var newCustomer = CreateCustomer();
      Console.WriteLine("New Customer:");
      Console.WriteLine(newCustomer);
      session.Save(newCustomer);

      foreach (var order in newCustomer.Orders) {
         session.Save(order);
      }

      id = newCustomer.Id;
      tx.Commit();
   }

   using(var session = sessionFactory.OpenSession())

   using(var tx = session.BeginTransaction()) {
      var reloaded = session.Load<Customer>(id);
      Console.WriteLine("The orders were ordered by: ");

      foreach (var order in reloaded.Orders) {
         Console.WriteLine(order.Customer);
      }

      tx.Commit();
   }
   Console.WriteLine("Press <ENTER> to exit..."); Console.ReadLine();
}

また、特定の製品を注文した顧客を指定しました。 したがって、その注文をその顧客に関連付けるには、多対1の関係を作成する必要があります。

*Order.hbm.xml* ファイルに移動して多対1を追加し、顧客フィールドと列に顧客IDを付けて名前を付けましょう。
<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo"
   namespace = "NHibernateDemo">

   <class name = "Order" table = "`Order`">

      <id name = "Id">
         <generator class = "guid.comb"/>
      </id>

      <property name = "Ordered"/>
      <property name = "Shipped"/>

      <component name = "ShipTo">
         <property name = "Street"/>
         <property name = "City"/>
         <property name = "Province"/>
         <property name = "Country"/>
      </component>

      <many-to-one name = "Customer" column = "CustomerId"/>

   </class>
</hibernate-mapping>

このアプリケーションをもう一度実行すると、次の出力が表示されます。

New Customer:
John Doe (00000000-0000-0000-0000-000000000000)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Unspecified)
   CreditRating: Good
   AverageRating: 42.42424242

   Orders:
      Order Id: 00000000-0000-0000-0000-000000000000
      Order Id: 00000000-0000-0000-0000-000000000000

Reloaded:
John Doe (660a6f29-650e-4380-99e0-a5b800febbde)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: 57314deb-e023-4e55-ac1e-a5b800febbe3
      Order Id: fc065683-d5f5-484b-ae42-a5b800febbe3

The orders were ordered by:
John Doe (660a6f29-650e-4380-99e0-a5b800febbde)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: 57314deb-e023-4e55-ac1e-a5b800febbe3
      Order Id: fc065683-d5f5-484b-ae42-a5b800febbe3

John Doe (660a6f29-650e-4380-99e0-a5b800febbde)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: 57314deb-e023-4e55-ac1e-a5b800febbe3
      Order Id: fc065683-d5f5-484b-ae42-a5b800febbe3

Press <ENTER> to exit...

NHibernate-コレクションマッピング

この章では、コレクションを表す方法について説明します。 次のようなNHibernate内で使用できるさまざまなタイプのコレクションがあります-

  • リスト
  • Sets
  • Bags

現在、.NETの観点からは、一般的にリストなど、非常に単純なデータ構造、リスト、辞書を扱います。 .NETには、さまざまなコレクションタイプがありません。 それでは、なぜNHibernateはこれらすべての異なるタイプを必要としますか? 本当にデータベースに戻ります。

List

  • リストは、必ずしも一意ではない要素の順序付きコレクションです。
  • IList <T> を使用してこれをマッピングできます。
  • したがって、従来はアドレスのリストがあり、アプリケーションの観点からは要素が一意であることはわかっていますが、リストには重複した要素をリストに挿入することを妨げるものはありません。

Set

  • セットは、一意の要素の順序付けられていないコレクションです。 2つの重複する要素をセットに挿入しようとすると、例外がスローされます。
  • NHibernateにはそれについて特別なことは何もありません。
  • これは、汎用セットを実装する便利な方法です。 .NET 4を使用している場合は、新しい HashSet <T> を使用してこれらを表すことができますが、ほとんどのNHibernateアプリケーションでは、これをISetと表します。
  • 順序付けされていないため、データベースからアドレスのリストまたは順序のリストを引き戻した場合、特定のOrder by句を指定しない限り、アドレスの順序はわかりません。
  • したがって、一般的に、データベースからプルバックするデータはセットです。
  • これらは、順序付けられていない要素の一意のコレクションです。

Bag

  • データベースの世界で見られるもう1つの一般的なコレクションはバッグです。これは、重複する要素を持つことができる点を除いて、セットに似ています。
  • .NETの世界では、これをIListで表します。

セットはおそらく最も一般的ですが、アプリケーションに応じてリストとバッグも表示されます。 Set順序が定義されている最後の章の customer.hbm.xml ファイルを見てみましょう。

<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo"
   namespace = "NHibernateDemo">

   <class name = "Customer">

      <id name = "Id">
         <generator class = "guid.comb"/>
      </id>

      <property name = "FirstName"/>
      <property name = "LastName"/>
      <property name = "AverageRating"/>
      <property name = "Points"/>
      <property name = "HasGoldStatus"/>
      <property name = "MemberSince" type = "UtcDateTime"/>
      <property name = "CreditRating" type = "CustomerCreditRatingType"/>

      <component name = "Address">
         <property name = "Street"/>
         <property name = "City"/>
         <property name = "Province"/>
         <property name = "Country"/>
      </component>

      <set name = "Orders" table = "`Order`">
         <key column = "CustomerId"/>
         <one-to-many class = "Order"/>
      </set>

   </class>
</hibernate-mapping>

ご覧のとおり、ordersコレクションをセットとしてマップしました。 セットは一意の要素の順不同のコレクションであることを忘れないでください。

これで、Customerクラスを見ると、次のプログラムに示すように、OrdersプロパティがISetで定義されていることがわかります。

public virtual ISet<Order> Orders { get; set; }

このアプリケーションを実行すると、次の出力が表示されます。

New Customer:
John Doe (00000000-0000-0000-0000-000000000000)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Unspecified)
   CreditRating: Good
   AverageRating: 42.42424242

   Orders:
      Order Id: 00000000-0000-0000-0000-000000000000
      Order Id: 00000000-0000-0000-0000-000000000000

Reloaded:
John Doe (1f248133-b50a-4ad7-9915-a5b8017d0ff1)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: c41af8f2-7124-42a7-91c5-a5b8017d0ff6
      Order Id: 657f6bb0-1f42-45fc-8fc7-a5b8017d0ff7

The orders were ordered by:
John Doe (1f248133-b50a-4ad7-9915-a5b8017d0ff1)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: c41af8f2-7124-42a7-91c5-a5b8017d0ff6
      Order Id: 657f6bb0-1f42-45fc-8fc7-a5b8017d0ff7

John Doe (1f248133-b50a-4ad7-9915-a5b8017d0ff1)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: c41af8f2-7124-42a7-91c5-a5b8017d0ff6
      Order Id: 657f6bb0-1f42-45fc-8fc7-a5b8017d0ff7

Press <ENTER> to exit...

コレクション内のアイテムが一意である必要がない場合、このコレクション内で同じプライマリキーを持つ複数の注文が複数回発生する可能性がある場合、次のプログラムに示すように、これをバッグとしてマッピングする方が適切です。

<bag name = "Orders" table = "`Order`">
   <key column = "CustomerId"/>
   <one-to-many class = "Order"/>
</bag>

このアプリケーションを実行すると、例外が発生します。顧客クラスを見ると、注文がC#コードでISetとしてマークされていることに気付くでしょう。

したがって、これをIListに変更する必要があります。次に、コンストラクターでHashSetからListに変更する必要があります。

public class Customer {

   public Customer() {
      MemberSince = DateTime.UtcNow;
      Orders = new List<Order>();
   }

   public virtual Guid Id { get; set; }
   public virtual string FirstName { get; set; }
   public virtual string LastName { get; set; }
   public virtual double AverageRating { get; set; }
   public virtual int Points { get; set; }

   public virtual bool HasGoldStatus { get; set; }
   public virtual DateTime MemberSince { get; set; }
   public virtual CustomerCreditRating CreditRating { get; set; }
   public virtual Location Address { get; set; }
   public virtual IList<Order> Orders { get; set; }
   public virtual void AddOrder(Order order) { Orders.Add(order); order.Customer = this; }

   public override string ToString() {
      var result = new StringBuilder();

      result.AppendFormat("{1} {2} ({0})\r\n\tPoints: {3}\r\n\tHasGoldStatus:
         {4}\r\n\tMemberSince: {5} ({7})\r\n\tCreditRating: {6}\r\n\tAverageRating:
         {8}\r\n", Id, FirstName, LastName, Points, HasGoldStatus, MemberSince,
         CreditRating, MemberSince.Kind, AverageRating); result.AppendLine("\tOrders:");

      foreach(var order in Orders) {
         result.AppendLine("\t\t" + order);
      }

      return result.ToString();
   }
}

アプリケーションを実行すると、同じ動作が見られます。 しかし、同じコレクション内で複数回注文を行うことができます。

John Doe (00000000-0000-0000-0000-000000000000)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Unspecified)
   CreditRating: Good
   AverageRating: 42.42424242

   Orders:
      Order Id: 00000000-0000-0000-0000-000000000000
      Order Id: 00000000-0000-0000-0000-000000000000

Reloaded:
John Doe (fbde48f5-d620-4d1c-9a7f-a5b8017c3280)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: 6dd7dbdb-354f-4c82-9c39-a5b8017c3286
      Order Id: 9b3e2441-a81b-404d-9aed-a5b8017c3287

The orders were ordered by:
John Doe (fbde48f5-d620-4d1c-9a7f-a5b8017c3280)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: 6dd7dbdb-354f-4c82-9c39-a5b8017c3286
      Order Id: 9b3e2441-a81b-404d-9aed-a5b8017c3287

John Doe (fbde48f5-d620-4d1c-9a7f-a5b8017c3280)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: 6dd7dbdb-354f-4c82-9c39-a5b8017c3286
      Order Id: 9b3e2441-a81b-404d-9aed-a5b8017c3287

Press <ENTER> to exit...

NHibernate-カスケード

この章では、カスケード機能の使用方法について説明します。 アイテムのセットまたはコレクション、または顧客と注文などの2つのクラス間の関係があり、外部キー関係がある場合。 デフォルトで顧客を削除すると、NHibernateは子オブジェクトに対して何もしません。そのため、その顧客に属するオブジェクトは孤立した注文になる可能性があります。

  • 外部キー制約に違反している可能性もあるため、カスケードの概念を使用できます。
  • デフォルトでは、NHibernateは操作を子オブジェクトにカスケードしません。
  • これは、デフォルトの配送先住所を持つ顧客などの関係を持つことができ、その配送先住所が多くの異なる顧客と共有されるためです。
  • そのため、他の顧客がまだ関係を参照しているため、その関係を必ずしもカスケードすることは望ましくありません。
  • したがって、カスケードの全体的な概念は、NHibernateに子エンティティーの処理方法を伝えることです。

カスケードには、次のようなさまざまなオプションがあります-

  • none -これはデフォルトであり、カスケードなしを意味します。

  • all -保存、更新、削除をカスケードします。

  • save-update -カスケード、保存、更新します。

  • delete -削除をカスケードします。

  • all-delete-orphan -これは非常に頻繁に使用される特別なもので、All Exceptと同じです。Delete-orphan行が見つかった場合は、それらも削除されます。

    *hbm.xml* ファイルでデフォルトを指定できるため、そのHibernateマッピング要素にデフォルトのカスケードを提供したり、多対1などの特定のコレクションおよび関係に対してデフォルトのカスケードを指定したりできます。

簡単なカスケードの例を見てみましょう。プログラムの問題を修正しましょう。次のコードに示すように、保存をオーダーに手動でカスケードする必要があります。

using(var session = sessionFactory.OpenSession())

using(var tx = session.BeginTransaction()) {
   var newCustomer = CreateCustomer();
   Console.WriteLine("New Customer:");
   Console.WriteLine(newCustomer);
   session.Save(newCustomer);

   foreach (var order in newCustomer.Orders) {
      session.Save(order);
   }

   id = newCustomer.Id;
   tx.Commit();
}

上記のコードスニペットでは、顧客のすべての注文を手動で保存していることがわかります。 次に、すべての注文が保存されている手動カスケードコードを削除しましょう。

using(var session = sessionFactory.OpenSession())

using(var tx = session.BeginTransaction()) {
   var newCustomer = CreateCustomer();
   Console.WriteLine("New Customer:");
   Console.WriteLine(newCustomer);

   session.Save(newCustomer);
   id = newCustomer.Id;
   tx.Commit();
}
*customer.hbm.xml* でカスケードオプションを指定する必要があります。
<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo"
   namespace = "NHibernateDemo">

   <class name = "Customer">

      <id name = "Id">
         <generator class = "guid.comb"/>
      </id>

      <property name = "FirstName"/>
      <property name = "LastName"/>
      <property name = "AverageRating"/>
      <property name = "Points"/>
      <property name = "HasGoldStatus"/>
      <property name = "MemberSince" type = "UtcDateTime"/>
      <property name = "CreditRating" type = "CustomerCreditRatingType"/>

      <component name = "Address">
         <property name = "Street"/>
         <property name = "City"/>
         <property name = "Province"/>
         <property name = "Country"/>
      </component>

      <set name = "Orders" table = "`Order`" cascade = "all-delete-orphan">
         <key column = "CustomerId"/>
         <one-to-many class = "Order"/>
      </set>

   </class>
</hibernate-mapping>
  • 現在、注文は完全に顧客のものです。 したがって、顧客がデータベースから削除された場合、ここでのアプリケーションは、孤立している可能性のある注文を含め、これらの注文をすべて削除する必要があります。
  • 削除を行うことになります。 それにより、顧客IDが削除する顧客と等しい注文テーブルから削除します。 したがって、これらの削除を実際にカスケードできます。 All *を使用すると、保存、更新、削除が実行されます。

このアプリケーションを実行すると、次の出力が表示されます。

New Customer:
John Doe (00000000-0000-0000-0000-000000000000)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Unspecified)
   CreditRating: Good
   AverageRating: 42.42424242

   Orders:
      Order Id: 00000000-0000-0000-0000-000000000000
      Order Id: 00000000-0000-0000-0000-000000000000

Reloaded:
John Doe (10b2a3d7-7fcf-483c-b1da-a5bb00b8512e)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: e6680e30-5b3b-4efa-b017-a5bb00b85133
      Order Id: b03858e7-8c36-4555-8878-a5bb00b85134

The orders were ordered by:
John Doe (10b2a3d7-7fcf-483c-b1da-a5bb00b8512e)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: e6680e30-5b3b-4efa-b017-a5bb00b85133
      Order Id: b03858e7-8c36-4555-8878-a5bb00b85134

John Doe (10b2a3d7-7fcf-483c-b1da-a5bb00b8512e)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: e6680e30-5b3b-4efa-b017-a5bb00b85133
      Order Id: b03858e7-8c36-4555-8878-a5bb00b85134

Press <ENTER> to exit...

ご覧のとおり、手動でカスケードされたプログラムからコードを削除しましたが、アプリケーションは引き続き動作しています。

したがって、関係によっては、それらをカスケードすることもできます。 次に、別のカスケード関係を見てみましょう。 Order.hbm.xml ファイルに移動して、その多対1の関係をカスケードできます。

<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo"
   namespace = "NHibernateDemo">

   <class name = "Order" table = "`Order`">

      <id name = "Id">
         <generator class = "guid.comb"/>
      </id>

      <property name = "Ordered"/>
      <property name = "Shipped"/>

      <component name = "ShipTo">
         <property name = "Street"/>
         <property name = "City"/>
         <property name = "Province"/>
         <property name = "Country"/>
      </component>

      <many-to-one name = "Customer" column = "CustomerId" cascade = "save-update"/>

   </class>
</hibernate-mapping>

したがって、新しい注文を作成し、それに新しい顧客がアタッチされ、その注文を保存すると言うと、それをカスケードすることができます。 しかし、おそらくしたくないことの1つは、対応する顧客を削除するために注文が削除された場合です。

そのため、ここでは保存更新を行いたいので、保存更新を使用して、保存または更新をその顧客にカスケードします。 したがって、新しい顧客を獲得した場合、または顧客を変更している場合、それはカスケードされます。 削除の場合、データベースからは削除されません。

したがって、アプリケーションを再度実行しても、すべてが期待どおりに機能します。

New Customer:
John Doe (00000000-0000-0000-0000-000000000000)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Unspecified)
   CreditRating: Good
   AverageRating: 42.42424242

   Orders:
      Id: 00000000-0000-0000-0000-000000000000
      Order Id: 00000000-0000-0000-0000-000000000000

Reloaded:
John Doe (10b2a3d7-7fcf-483c-b1da-a5bb00b8512e)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: e6680e30-5b3b-4efa-b017-a5bb00b85133
      Order Id: b03858e7-8c36-4555-8878-a5bb00b85134

The orders were ordered by:
John Doe (10b2a3d7-7fcf-483c-b1da-a5bb00b8512e)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: e6680e30-5b3b-4efa-b017-a5bb00b85133
      Order Id: b03858e7-8c36-4555-8878-a5bb00b85134
      John Doe (10b2a3d7-7fcf-483c-b1da-a5bb00b8512e)

   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: e6680e30-5b3b-4efa-b017-a5bb00b85133
      Order Id: b03858e7-8c36-4555-8878-a5bb00b85134

Press <ENTER> to exit...

ここで、アプリケーションを確認します。デフォルトはNoneであり、エンティティとエンティティ間の関係を考慮して、各エンティティとそのデータベース内の各関係の適切なカスケードを決定する必要があることに注意してください。

NHibernate-遅延読み込み

この章では、遅延読み込み機能について説明します。 デフォルトではまったく異なる概念であり、NHibernateには遅延読み込みがありません。たとえば、顧客を読み込む場合、すべての注文を読み込むわけではありません。

  • 注文コレクションはオンデマンドでロードされます。
  • 多対一であってもコレクションであっても、デフォルトで関連付けが遅延ロードされる場合は、 Open ISession が必要です。
  • セッションを閉じた場合、またはトランザクションをコミットした場合、これらの追加オブジェクトをプルできない遅延読み込み例外を取得できます。
  • 遅延読み込みと実際に必要なデータ量に注意する必要があります。
  • アソシエーション全体の遅延読み込みをオフにするか、lazy equals falseを設定するか、フェッチ戦略を指定することもできます。

これが Program.cs ファイルの実装です。

using System;
using System.Data;
using System.Linq;
using System.Reflection;

using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;

namespace NHibernateDemo {

   internal class Program {

      private static void Main() {

         var cfg = ConfigureNHibernate();
         var sessionFactory = cfg.BuildSessionFactory();

         Guid id;
         using(var session = sessionFactory.OpenSession())

         using(var tx = session.BeginTransaction()) {
            var newCustomer = CreateCustomer();
            Console.WriteLine("New Customer:");
            Console.WriteLine(newCustomer);
            session.Save(newCustomer);
            id = newCustomer.Id;
            tx.Commit();
         }

         using(var session = sessionFactory.OpenSession())

         using(var tx = session.BeginTransaction()) {
            var reloaded = session.Load<Customer>(id);
            Console.WriteLine("Reloaded:");
            Console.WriteLine(reloaded);
            Console.WriteLine("The orders were ordered by: ");

            foreach (var order in reloaded.Orders) {
               Console.WriteLine(order.Customer);
            }

            tx.Commit();
         }

         Console.WriteLine("Press <ENTER> to exit...");
         Console.ReadLine();
      }

      private static Customer CreateCustomer() {

         var customer = new Customer {
            FirstName = "John",
            LastName = "Doe",
            Points =100,
            HasGoldStatus = true,
            MemberSince = new DateTime(2012, 1, 1),
            CreditRating = CustomerCreditRating.Good,
            AverageRating = 42.42424242,
            Address = CreateLocation()
         };

         var order1 = new Order { Ordered = DateTime.Now };
         customer.AddOrder(order1);

         var order2 = new Order {
            Ordered = DateTime.Now.AddDays(-1),
            Shipped = DateTime.Now,
            ShipTo = CreateLocation()
         };

         customer.AddOrder(order2); return customer;
      }

      private static Location CreateLocation() {
         return new Location {
            Street = "123 Somewhere Avenue",
            City = "Nowhere",
            Province = "Alberta",
            Country = "Canada"
         };
      }

      private static Configuration ConfigureNHibernate() {

         NHibernateProfiler.Initialize();
         var cfg = new Configuration();

         cfg.DataBaseIntegration(x => {
            x.ConnectionStringName = "default";
            x.Driver<SqlClientDriver>();
            x.Dialect<MsSql2008Dialect<();
            x.IsolationLevel = IsolationLevel.RepeatableRead;
            x.Timeout = 10;
            x.BatchSize = 10;
         });

         cfg.SessionFactory().GenerateStatistics();
         cfg.AddAssembly(Assembly.GetExecutingAssembly());
         return cfg;
      }
   }
}

これを理解するために、アプリケーションを実行して、NHibernate Profilerを見てみましょう。

顧客コレクション

ご覧のように、特定の顧客IDを指定したSelect From Customerがあり、実際にその顧客のコレクションにアクセスするときに別のSelect From Ordersテーブルもあります。

したがって、データベースへの往復は2回あります。 さて、時々、これを最適化したいと思うでしょう。 これを行うには、 customer.hbm.xml ファイルに移動し、フェッチ戦略を追加して、結合フェッチを実行するように依頼します。

<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo"
   namespace = "NHibernateDemo">

   <class name = "Customer">

      <id name = "Id">
         <generator class = "guid.comb"/>
      </id>

      <property name = "FirstName"/>
      <property name = "LastName"/>
      <property name = "AverageRating"/>
      <property name = "Points"/>
      <property name = "HasGoldStatus"/>
      <property name = "MemberSince" type = "UtcDateTime"/>
      <property name = "CreditRating" type = "CustomerCreditRatingType"/>

      <component name = "Address">
         <property name = "Street"/>
         <property name = "City"/>
         <property name = "Province"/>
         <property name = "Country"/>
      </component>

      <set name = "Orders" table = "`Order`" cascade = "all-delete-orphan"
         fetch = "join">
         <key column = "CustomerId"/>
         <one-to-many class = "Order"/>
      </set>

   </class>
</hibernate-mapping>

アプリケーションのコードを変更していないことがわかるように、 customer.hbm.xml にフェッチ戦略を追加しました。 このアプリケーションを再度実行してみましょう。それでもまったく同じように動作します。 NHibernate Profilerを見てみましょう。

顧客プロファイル

  • 以前は、プログラムはデータベースへの2回のラウンドトリップを持っていましたが、現在は1回だけであり、それはここで左外部結合を行っているためです。
  • 顧客IDに基づいて、顧客テーブルと注文テーブルの間で左外部結合を行っていることがわかります。したがって、すべての情報を一度に読み込むことができます。
  • データベースに1回の往復を保存しました。
  • 欠点は、顧客情報が両方の行に複製されることであり、これがSQLの左外部結合が機能する方法です。
  • したがって、フェッチ戦略では、もう少しデータを引き戻し、ラウンドトリップを節約しています。

クエリレベルでこれを行うこともできますので、 Program.cs ファイルに移動して、より単純なリロードされた例を見てみましょう。

using(var session = sessionFactory.OpenSession())

using(var tx = session.BeginTransaction()) {
  //var query = from customer in session.Query<Customer>()
  //select customer;
  //var reloaded = query.Fetch(x => x.Orders).ToList();

   var reloaded = session.Load<Customer>(id);
   Console.WriteLine("Reloaded:");
   Console.WriteLine(reloaded);
   Console.WriteLine("The orders were ordered by: ");

   foreach (var order in reloaded.Orders) {
      Console.WriteLine(order.Customer);
   }

   tx.Commit();
}

ここでは、お客様によるロードを行っています。 それをクエリに変更しましょう。次のコードに示すように、リンククエリを使用します。

using(var session = sessionFactory.OpenSession())

using(var tx = session.BeginTransaction()) {
   var query = from customer in session.Query<Customer>()
   where customer.Id == id select customer;
   var reloaded = query.Fetch(x => x.Orders).ToList().First();

   Console.WriteLine("Reloaded:");
   Console.WriteLine(reloaded);

   tx.Commit();
}

また、 customer.hbm.xml ファイルからフェッチ戦略を削除しましょう。

<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo"
   namespace = "NHibernateDemo">

   <class name = "Customer">

      <id name = "Id">
         <generator class = "guid.comb"/>
      </id>

      <property name = "FirstName"/>
      <property name = "LastName"/>
      <property name = "AverageRating"/>
      <property name = "Points"/>
      <property name = "HasGoldStatus"/>
      <property name = "MemberSince" type = "UtcDateTime"/>
      <property name = "CreditRating" type = "CustomerCreditRatingType"/>

      <component name = "Address">
         <property name = "Street"/>
         <property name = "City"/>
         <property name = "Province"/>
         <property name = "Country"/>
      </component>

      <set name = "Orders" table = "`Order`" cascade = "all-delete-orphan">
         <key column = "CustomerId"/>
         <one-to-many class = "Order"/>
      </set>

   </class>
</hibernate-mapping>

このアプリケーションを再度実行すると、次の出力が表示されます。

New Customer:
John Doe (00000000-0000-0000-0000-000000000000)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Unspecified)
   CreditRating: Good
   AverageRating: 42.42424242

   Orders:
      Order Id: 00000000-0000-0000-0000-000000000000
      Order Id: 00000000-0000-0000-0000-000000000000

Reloaded:
John Doe (6ebacd17-f9ba-4ad8-9817-a5bb01112a5a)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: 16a6596b-d56e-41c7-9681-a5bb01112a60
      Order Id: d41d615b-0f21-4032-81db-a5bb01112a61

Press <ENTER> to exit...

NHibernate Profilerを見てみましょう。この熱心な結合フェッチが再び発生していることがわかりますが、今回はクエリに基づいています。

Join Fetch

NHibernate-逆関係

この章では、逆関係というもう1つの機能について説明します。 これは、コレクションに逆にtrueと等しい面白いオプションであり、多くの開発者を混乱させます。 それでは、このオプションについて話しましょう。 これを理解するには、リレーショナルモデルについて考える必要があります。 単一の外部キーを使用する双方向の関連付けがあるとします。

  • リレーショナルの観点から、1つの外部キーを取得しました。これは、注文する顧客と注文する顧客の両方を表します。

  • OOモデルから、これらの参照を使用した単方向の関連付けがあります。

  • 2つの単方向の関連付けがデータベース内の同じ双方向の関連付けを表しているという説明はありません。

  • ここでの問題は、NHibernateには customer.ordersorder.customer がデータベース内の同じ関係を表すことを知るのに十分な情報がないことです。

  • ヒントとして inverse equals true を提供する必要があります。これは、単方向の関連付けが同じデータを使用しているためです。

  • 2つの参照を持つこれらの関係を保存しようとすると、NHibernateはその参照を2回更新しようとします。

  • 実際には、データベースへの余分なラウンドトリップを実行し、その外部キーに対して2つの更新も行います。

  • 逆の値がtrueの場合、NHibernateは関係のどちらの側を無視するかを指示します。

  • これをコレクション側に適用すると、NHibernateは常に、子オブジェクト側から反対側から外部キーを更新します。

  • その後、その外部キーに対する更新は1つだけであり、そのデータに対する追加の更新はありません。

  • これにより、外部キーに対するこれらの重複した更新を防ぐことができ、外部キー違反の防止にも役立ちます。

    *AddOrder* メソッドが表示される *customer.cs* ファイルを見てみましょう。ここでの考え方は、注文から顧客に戻るためのこのバックポインターがあり、設定する必要があるということです。 そのため、注文が顧客に追加されると、その顧客のバックポインターが設定されます。それ以外の場合はnullになるため、オブジェクトグラフで適切に接続するためにこれが必要です。
using System;
using System.Text;
using Iesi.Collections.Generic;

namespace NHibernateDemo {

   public class Customer {

      public Customer() {
         MemberSince = DateTime.UtcNow; Orders = new HashedSet<Order>();
      }

      public virtual Guid Id { get; set; }
      public virtual string FirstName { get; set; }
      public virtual string LastName { get; set; }
      public virtual double AverageRating { get; set; }
      public virtual int Points { get; set; }
      public virtual bool HasGoldStatus { get; set; }

      public virtual DateTime MemberSince { get; set; }
      public virtual CustomerCreditRating CreditRating { get; set; }
      public virtual Location Address { get; set; }
      public virtual ISet<Order> Orders { get; set; }
      public virtual void AddOrder(Order order) { Orders.Add(order); order.Customer = this; }

      public override string ToString() {
         var result = new StringBuilder();

         result.AppendFormat("{1} {2} ({0})\r\n\tPoints: {3}\r\n\tHasGoldStatus:
            {4}\r\n\tMemberSince: {5} ({7})\r\n\tCreditRating: {6}\r\n\tAverageRating:
            {8}\r\n", Id, FirstName, LastName, Points, HasGoldStatus, MemberSince,
            CreditRating, MemberSince.Kind, AverageRating);
         result.AppendLine("\tOrders:");

         foreach(var order in Orders) {
            result.AppendLine("\t\t" + order);
         }

         return result.ToString();
      }
   }

   public class Location {
      public virtual string Street { get; set; }
      public virtual string City { get; set; }
      public virtual string Province { get; set; }
      public virtual string Country { get; set; }
   }

   public enum CustomerCreditRating {
      Excellent,
      VeryVeryGood,
      VeryGood,
      Good,
      Neutral,
      Poor,
      Terrible
   }
}

これが Program.cs ファイルの実装です。

using System;
using System.Data;
using System.Linq;
using System.Reflection;

using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;

namespace NHibernateDemo {

   internal class Program {

      private static void Main() {

         var cfg = ConfigureNHibernate();
         var sessionFactory = cfg.BuildSessionFactory();
         Guid id;
         using(var session = sessionFactory.OpenSession())

         using(var tx = session.BeginTransaction()) {
            var newCustomer = CreateCustomer();
            Console.WriteLine("New Customer:");
            Console.WriteLine(newCustomer);
            session.Save(newCustomer);
            id = newCustomer.Id;
            tx.Commit();
         }

         using(var session = sessionFactory.OpenSession())

         using(var tx = session.BeginTransaction()) {
            var query = from customer in session.Query<Customer>() where
               customer.Id == id select customer;

            var reloaded = query.Fetch(x => x.Orders).ToList().First();
            Console.WriteLine("Reloaded:"); Console.WriteLine(reloaded);

            tx.Commit();
         }

         Console.WriteLine("Press <ENTER> to exit...");
         Console.ReadLine();
      }

      private static Customer CreateCustomer() {
         var customer = new Customer {
            FirstName = "John",
            LastName = "Doe",
            Points = 100,
            HasGoldStatus = true,
            MemberSince = new DateTime(2012, 1, 1),
            CreditRating = CustomerCreditRating.Good,
            AverageRating = 42.42424242,
            Address = CreateLocation()
         };

         var order1 = new Order { Ordered = DateTime.Now };

         customer.AddOrder(order1); var order2 = new Order {
            Ordered = DateTime.Now.AddDays(-1),
            Shipped = DateTime.Now,
            ShipTo = CreateLocation()
         };

         customer.AddOrder(order2);
         return customer;
      }

      private static Location CreateLocation() {
         return new Location {
            Street = "123 Somewhere Avenue",
            City = "Nowhere",
            Province = "Alberta",
            Country = "Canada"
         };
      }

      private static Configuration ConfigureNHibernate() {
         NHibernateProfiler.Initialize();
         var cfg = new Configuration();

         cfg.DataBaseIntegration(x => {
            x.ConnectionStringName = "default";
            x.Driver<SqlClientDriver>();
            x.Dialect<MsSql2008Dialect>();
            x.IsolationLevel = IsolationLevel.RepeatableRead;
            x.Timeout = 10;
            x.BatchSize = 10;
         });

         cfg.SessionFactory().GenerateStatistics();
         cfg.AddAssembly(Assembly.GetExecutingAssembly());
         return cfg;
      }
   }
}

それをデータベースに保存してからリロードします。 それでは、アプリケーションを実行してNHibernate Profilerを開き、実際にどのように保存されたかを見てみましょう。

逆NHibernateプロファイラー

3つのグループのステートメントがあることに気付くでしょう。 最初の1つは顧客を挿入し、その顧客のIDはGuidであり、強調表示されています。 2番目のステートメントは、ordersテーブルへの挿入です。

Customer Id Guid

同じCustomer Id Guidがそこに設定されていることに気付くので、その外部キーを設定します。 最後のステートメントは更新であり、外部キーを再度同じ顧客IDに更新します。

Customer Hbm

問題は、顧客に注文があり、注文に顧客がいるということです。NHibernateに実際には同じ関係であることを伝えなかったわけではありません。 これを行う方法は、逆がtrueの場合です。

次のコードに示すように、 customer.hbm.xml マッピングファイルに移動し、逆数をtrueに設定します。

<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo"
   namespace = "NHibernateDemo">

   <class name = "Customer">

      <id name = "Id">
         <generator class = "guid.comb"/>
      </id>

      <property name = "FirstName"/>
      <property name = "LastName"/>
      <property name = "AverageRating"/>
      <property name = "Points"/>
      <property name = "HasGoldStatus"/>
      <property name = "MemberSince" type = "UtcDateTime"/>
      <property name = "CreditRating" type = "CustomerCreditRatingType"/>

      <component name = "Address">
         <property name = "Street"/>
         <property name = "City"/>
         <property name = "Province"/>
         <property name = "Country"/>
      </component>

      <set name = "Orders" table = "`Order`" cascade = "all-delete-orphan"
         inverse = "true">
         <key column = "CustomerId"/>
         <one-to-many class = "Order"/>
      </set>

   </class>
</hibernate-mapping>

注文を保存するとき、注文側からその外部キーを設定します。 次に、このアプリケーションを再度実行して、NHibernateプロファイラーを開きます。

外部キー

それらがどのように挿入されるかを見ると、顧客への挿入とオーダーへの挿入を取得しますが、オーダーの保存時に更新されているため、外部キーの重複した更新はありません。

  • ここで、単方向の関連付けのみがあり、この関係を維持しているのがセットである場合、逆に等しいをtrueにすると、その外部キーは決して設定されず、それらのアイテムはデータベースに設定された外部キー。
  • Order.hbm.xml ファイルで多対1の関係を見て、逆を探した場合、実際には逆属性はありません。
  • 常に子アイテムから設定されますが、多対多のコレクションがある場合は、どちら側からでも設定できます。

NHibernate-ロード/取得

この章では、LoadおよびGet機能がどのように機能し、どのように使用できるかについて説明します。 これらは、主キーによってオブジェクトをロードするために ISession によって提供される2つの非常に類似したAPIです。

  • Get -オブジェクトまたはnullを返します。
  • Load -オブジェクトを返すか、 ObjectNotFoundException をスローします。

さて、なぜこれら2つの異なるAPIがあるのでしょうか?

Load

  • これは、Loadがデータベースのラウンドトリップをより効率的に最適化できるためです。
  • Loadは実際にプロキシオブジェクトを返すため、Load呼び出しを発行するときにデータベースにアクセスする必要はありません。
  • そのプロキシにアクセスすると、オブジェクトはデータベースに存在しないため、その時点でObjectNotFoundExceptionをスローする可能性があります。

Get

  • 逆に、CLRまたは Common Language Runtime の制限のためにGetを使用し、NHibernateはすぐにデータベースにアクセスする必要があり、オブジェクトが存在するかどうかを確認し、存在しない場合はnullを返します。
  • プロキシオブジェクトを返すことができず、ユーザーが実際にアクセスしたときにそのプロキシオブジェクトをnullに交換したため、そのフェッチ、データベースへの往復を遅らせるオブジェクトオプションはありません。

これらが実際にどのように使用されるか、そしてGetとLoadの違いを見る簡単な例を見てみましょう。 同じドメインクラス CustomersOrders 、および同様に前の章の同じマッピングファイルを使用します。

この例では、最初に次のプログラムに示すようにGetを使用します。

using System;
using System.Data;
using System.Linq;
using System.Reflection;

using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Criterion;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;

namespace NHibernateDemo {

   internal class Program {

      private static void Main() {

         var cfg = ConfigureNHibernate();
         var sessionFactory = cfg.BuildSessionFactory();
         using(var session = sessionFactory.OpenSession())

         using(var tx = session.BeginTransaction()) {
            var id1 = Guid.Parse("4e97c816-6bce-11e1-b095-6cf049ee52be");
            var id2 = Guid.Parse("AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE");

            var customer1 = session.Get<Customer>(id1);
            Console.WriteLine("Customer1 data");
            Console.WriteLine(customer1);

            var customer2 = session.Get<Customer>(id2);
            Console.WriteLine("Customer2 data");
            Console.WriteLine(customer2);

            tx.Commit();
         }

         Console.WriteLine("Press <ENTER> to exit...");
         Console.ReadLine();
      }

      private static Configuration ConfigureNHibernate() {

         NHibernateProfiler.Initialize();
         var cfg = new Configuration();

         cfg.DataBaseIntegration(x => {
            x.ConnectionStringName = "default";
            x.Driver<SqlClientDriver>();
            x.Dialect<MsSql2008Dialect>();
            x.IsolationLevel = IsolationLevel.RepeatableRead;
            x.Timeout = 10;
            x.BatchSize = 10;
         });

         cfg.SessionFactory().GenerateStatistics();
         cfg.AddAssembly(Assembly.GetExecutingAssembly());
         return cfg;
      }
   }
}

ご覧のとおり、2つの Guid IDがあります。最初のIDは適切なIDです。データベースにあることがわかっているのは顧客のIDです。 一方、2番目のIDはデータベースに存在しません。 これらのIDは両方ともパラメーターとして* Get()*メソッドに渡され、結果がコンソールに出力されます。

上記のコードをコンパイルして実行すると、次の出力が表示されます。

Customer1 data
Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be)
   Points: 74
   HasGoldStatus: True
   MemberSince: 4/4/2009 12:00:00 AM (Utc)
   CreditRating: Neutral
   AverageRating: 0

Orders:
   Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be
   Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be
   Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be
   Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be

Customer2 data
Press <ENTER> to exit...

ご覧のとおり、Customer1データは印刷されていますが、Customer2データは空です。これは、Customer2レコードがデータベースで使用できないためです。

アプリケーションを再度実行するとき、コミットステートメントの前にブレークポイントを挿入し、[ウォッチ]ウィンドウで両方の顧客を見てみましょう。

Customer2 Record

ご覧のとおり、Customer1のデータは利用可能ですが、Customer2はnullで、両方のタイプは NHibernateDemo.Customer です。

次のコードに示すのと同じ例で、Getの代わりにLoadメソッドを使用してみましょう。

using System;
using System.Data;
using System.Linq;
using System.Reflection;

using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Criterion;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;

namespace NHibernateDemo {

   internal class Program {

      private static void Main() {

         var cfg = ConfigureNHibernate();
         var sessionFactory = cfg.BuildSessionFactory();
         using(var session = sessionFactory.OpenSession())

         using(var tx = session.BeginTransaction()) {
            var id1 = Guid.Parse("4e97c816-6bce-11e1-b095-6cf049ee52be");
            var id2 = Guid.Parse("AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE");

            var customer1 = session.Load<Customer>(id1);
            Console.WriteLine("Customer1 data");
            Console.WriteLine(customer1);

            var customer2 = session.Load<Customer>(id2);
            Console.WriteLine("Customer2 data");
            Console.WriteLine(customer2);

            tx.Commit();
         }

         Console.WriteLine("Press <ENTER> to exit...");
         Console.ReadLine();
      }

      private static Configuration ConfigureNHibernate() {

         NHibernateProfiler.Initialize();
         var cfg = new Configuration();

         cfg.DataBaseIntegration(x => {
            x.ConnectionStringName = "default";
            x.Driver<SqlClientDriver>();
            x.Dialect<MsSql2008Dialect>();
            x.IsolationLevel = IsolationLevel.RepeatableRead;
            x.Timeout = 10;
            x.BatchSize = 10;
         });

         cfg.SessionFactory().GenerateStatistics();
         cfg.AddAssembly(Assembly.GetExecutingAssembly());
         return cfg;
      }
   }
}

この例を実行してみましょう。スクリーンショットに示されているように、次の例外がスローされます。

例外Nhibernateプロファイル

[ウォッチ]ウィンドウを見ると、タイプが両方のオブジェクトのカスタマープロキシであることがわかります。 また、コンソールウィンドウにCustomer1の同じデータが表示されます。

Customer1 data
Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be)
   Points: 74
   HasGoldStatus: True
   MemberSince: 4/4/2009 12:00:00 AM (Utc)
   CreditRating: Neutral
   AverageRating: 0

   Orders:
      Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be

Customer2 data

NHibernate-Linq

この章では、人々が使用する別の一般的なAPIであるNHibernate LINQプロバイダーについて説明します。 ISessionの拡張メソッドおよび署名を介したアクセスは、 Query <T> です。 LINQを使用しているときに構文の2種類があります-

  • クエリチェーン構文
  • クエリ内包構文

クエリチェーン構文

次のプログラムに示すように、メソッドチェーン構文を使用して、データベースの任意のレコードにアクセスできます。

var customer = session.Query<Customer>() .Where(c => c.FirstName == "Laverne")
  • クエリがあり、WHERE句があり、追加のWHERE句と同様にselect句があることがわかります。
  • これは、通常のLINQで使用できる標準のメソッドチェーン構文です。
  • LINQ to ObjectsまたはLINQ to SQL、おなじみのその他のLINQプロバイダー。

名前がLaverneである顧客を取得する簡単な例を見てみましょう。 現在、名がLaverneである顧客が複数いる可能性があるため、最初の顧客のみを取得します。

using System;
using System.Data;
using System.Linq;
using System.Reflection;

using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Criterion;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;

namespace NHibernateDemo {

   internal class Program {

      private static void Main() {

         var cfg = ConfigureNHibernate();
         var sessionFactory = cfg.BuildSessionFactory();
         using(var session = sessionFactory.OpenSession())

         using(var tx = session.BeginTransaction()) {
            var customer = session.Query<Customer>()
               .Where(c => c.FirstName == "Laverne").First();
            Console.WriteLine(customer);
            tx.Commit();
         }

         Console.WriteLine("Press <ENTER> to exit...");
         Console.ReadLine();
      }

      private static Configuration ConfigureNHibernate() {

         NHibernateProfiler.Initialize();
         var cfg = new Configuration();

         cfg.DataBaseIntegration(x => {
            x.ConnectionStringName = "default";
            x.Driver<SqlClientDriver>();
            x.Dialect<MsSql2008Dialect>();
            x.IsolationLevel = IsolationLevel.RepeatableRead;
            x.Timeout = 10;
            x.BatchSize = 10;
         });

         cfg.SessionFactory().GenerateStatistics();
         cfg.AddAssembly(Assembly.GetExecutingAssembly());
         return cfg;
      }
   }
}

上記のコードをコンパイルして実行すると、次の出力が表示されます。

Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be)
   Points: 74
   HasGoldStatus: True
   MemberSince: 4/4/2009 12:00:00 AM (Utc)
   CreditRating: Neutral
   AverageRating: 0

   Orders:
      Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be

Press <ENTER> to exit...

クエリ内包構文

クエリ内包構文もあります。これは、from、where、およびselectキーワードを使用するSQLによく似ています。

それでは、同じ例を見てみましょうが、今回はLINQ内包構文を使用します。これは、次のプログラムに示すように、SQLによく似ています。

using System;
using System.Data;
using System.Linq;
using System.Reflection;

using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Criterion;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;

namespace NHibernateDemo {

   internal class Program {

      private static void Main() {

         var cfg = ConfigureNHibernate();
         var sessionFactory = cfg.BuildSessionFactory();
         using(var session = sessionFactory.OpenSession())

         using(var tx = session.BeginTransaction()) {
            var customer = (from c in session.Query<Customer>()
               where c.FirstName == "Laverne" select c).First();
            Console.WriteLine(customer);
            tx.Commit();
         }

         Console.WriteLine("Press <ENTER> to exit...");
         Console.ReadLine();
      }

      private static Configuration ConfigureNHibernate() {

         NHibernateProfiler.Initialize();
         var cfg = new Configuration();

         cfg.DataBaseIntegration(x => {
            x.ConnectionStringName = "default";
            x.Driver<SqlClientDriver>();
            x.Dialect<MsSql2008Dialect>();
            x.IsolationLevel = IsolationLevel.RepeatableRead;
            x.Timeout = 10;
            x.BatchSize = 10;
         });

         cfg.SessionFactory().GenerateStatistics();
         cfg.AddAssembly(Assembly.GetExecutingAssembly());
         return cfg;
      }
   }
}

このアプリケーションを再度実行してみましょう。次の出力が表示されます。

Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be)
   Points: 74
   HasGoldStatus: True
   MemberSince: 4/4/2009 12:00:00 AM (Utc)
   CreditRating: Neutral
   AverageRating: 0

   Orders:
      Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be

Press <ENTER> to exit...

FirstNameが文字Hで始まるすべての顧客を取得する別の例を見てみましょう。

using System;
using System.Data;
using System.Linq;
using System.Reflection;

using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Criterion;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;

namespace NHibernateDemo {

   internal class Program {

      private static void Main() {

         var cfg = ConfigureNHibernate();
         var sessionFactory = cfg.BuildSessionFactory();
         using(var session = sessionFactory.OpenSession())

         using(var tx = session.BeginTransaction()) {
            var customers = session.Query<Customer>() .Where(c =<
               c.FirstName.StartsWith("H"));

            foreach (var customer in customers.ToList()) {
               Console.WriteLine(customer);
            }

            tx.Commit();
         }

         Console.WriteLine("Press <ENTER> to exit...");
         Console.ReadLine();
      }

      private static Configuration ConfigureNHibernate() {

         NHibernateProfiler.Initialize();
         var cfg = new Configuration();

         cfg.DataBaseIntegration(x => {
            x.ConnectionStringName = "default";
            x.Driver<SqlClientDriver>();
            x.Dialect<MsSql2008Dialect>();
            x.IsolationLevel = IsolationLevel.RepeatableRead;
            x.Timeout = 10;
            x.BatchSize = 10;
         });

         cfg.SessionFactory().GenerateStatistics();
         cfg.AddAssembly(Assembly.GetExecutingAssembly());
         return cfg;
      }
   }
}

同様に、クエリ内包構文は次のプログラムのようになります。

using System;
using System.Data;
using System.Linq;
using System.Reflection;

using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Criterion;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;

namespace NHibernateDemo {

   internal class Program {

      private static void Main() {

         var cfg = ConfigureNHibernate();
         var sessionFactory = cfg.BuildSessionFactory();
         using(var session = sessionFactory.OpenSession())

         using(var tx = session.BeginTransaction()) {
            var customers = from c in session.Query<Customer>()
               where c.FirstName.StartsWith("H") select c;

            foreach (var customer in customers.ToList()) {
               Console.WriteLine(customer);
            }

            tx.Commit();
         }

         Console.WriteLine("Press <ENTER> to exit...");
         Console.ReadLine();
      }

      private static Configuration ConfigureNHibernate() {

         NHibernateProfiler.Initialize();
         var cfg = new Configuration();

         cfg.DataBaseIntegration(x => {
            x.ConnectionStringName = "default";
            x.Driver<SqlClientDriver>();
            x.Dialect<MsSql2008Dialect>();
            x.IsolationLevel = IsolationLevel.RepeatableRead;
            x.Timeout = 10;
            x.BatchSize = 10;
         });

         cfg.SessionFactory().GenerateStatistics();
         cfg.AddAssembly(Assembly.GetExecutingAssembly());
         return cfg;
      }
   }
}

このアプリケーションをもう一度実行してみましょう。すべての顧客の名前がアルファベットHで始まります。

Herman Crooks (4ead3480-6bce-11e1-b15c-6cf049ee52be)
   Points: 74
   HasGoldStatus: True
   MemberSince: 12/3/2010 12:00:00 AM (Utc)
   CreditRating: Neutral
   AverageRating: 0

   Orders:
      Order Id: 4ead3480-6bce-11e1-b15d-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b15e-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b15f-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b160-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b161-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b162-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b163-6cf049ee52be

Hudson Bins (4ec03f80-6bce-11e1-b2b7-6cf049ee52be)
   Points: 56
   HasGoldStatus: False
   MemberSince: 10/20/2008 12:00:00 AM (Utc)
   CreditRating: Terrible
   AverageRating: 0

   Orders:
      Order Id: 4ec03f80-6bce-11e1-b2b8-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2b9-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2ba-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2bb-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2bc-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2bd-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2be-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2bf-6cf049ee52be

Hettie Feest (4ec50240-6bce-11e1-b300-6cf049ee52be)
   Points: 82
   HasGoldStatus: False
   MemberSince: 4/10/2009 12:00:00 AM (Utc)
   CreditRating: Neutral
   AverageRating: 0

   Orders:
      Order Id: 4ec50240-6bce-11e1-b301-6cf049ee52be
      Order Id: 4ec50240-6bce-11e1-b302-6cf049ee52be
      Order Id: 4ec50240-6bce-11e1-b303-6cf049ee52be

Press <ENTER> to exit...

NHibernate-Hibernateクエリ言語

この章では、Hibernate Query Languageについて説明します。 HQLは、JavaのHibernateとNHibernateの両方で共有されます。

  • これは Criteria とともに最も古いクエリメカニズムです。
  • それは非常に早く実装され、文字列ベースのクエリ API です。
  • ISession CreateQuery を介してアクセスします。これは、SQLとほぼ同じです。
  • 同じキーワードの多くを使用しますが、構文は単純化されています。
  • これは最も一般的な例の1つです。クエリの実行方法を探している場合、HQLの例がよく見られます。

以下はHQLの簡単な例です-

var customers = session.CreateQuery("select c from Customer c where c.FirstName = 'Laverne'");
  • ここで、顧客からCを選択していることがわかります。SQLによく似ています。 これはNHibernateに関する限り不透明な文字列なので、これが実行時まで有効なHQLであるかどうかはわかりませんが、これは欠点の1つです。
  • LINQプロバイダーの強みの1つは、コンパイル時のサポートを取得できることです。 *しかし、HQLは、頻繁に使用される最も柔軟なクエリメカニズムの1つです。 他に方法がない場合は、HQLでそれを行う方法があると言われています。

代わりにHQLを使用してLINQクエリを再作成する簡単な例を見てみましょう。* session.CreateQuery *を呼び出してHQLにアクセスし、HQL文字列を使用してパラメーターとして渡すことができます。

using System;
using System.Data;
using System.Linq;
using System.Reflection;

using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Criterion;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;

namespace NHibernateDemo {

   internal class Program {

      private static void Main() {

         var cfg = ConfigureNHibernate();
         var sessionFactory = cfg.BuildSessionFactory();
         using(var session = sessionFactory.OpenSession())

         using(var tx = session.BeginTransaction()) {
            var customers = session.CreateQuery("select c from Customer c
               where c.FirstName = 'Laverne'");

            foreach (var customer in customers.List<Customer>()) {
               Console.WriteLine(customer);
            }

            tx.Commit();
         }

         Console.WriteLine("Press <ENTER> to exit...");
         Console.ReadLine();
      }

      private static Configuration ConfigureNHibernate() {

         NHibernateProfiler.Initialize();
         var cfg = new Configuration();

         cfg.DataBaseIntegration(x => {
            x.ConnectionStringName = "default";
            x.Driver<SqlClientDriver>();
            x.Dialect<MsSql2008Dialect>();
            x.IsolationLevel = IsolationLevel.RepeatableRead;
            x.Timeout = 10;
            x.BatchSize = 10;
         });

         cfg.SessionFactory().GenerateStatistics();
         cfg.AddAssembly(Assembly.GetExecutingAssembly());
         return cfg;
      }
   }
}
  • このHQL文字列はSQLによく似ていますが、主な違いはFirstNameが列名ではなくプロパティ名であることです。
  • したがって、2つの間に矛盾がある場合は、プロパティ名を使用します。 同じことは、テーブル名のように見えますが、実際には、選択するクラスの名前です。
  • バックエンドテーブルの名前がCustomersである場合、HQLクエリでCustomerを使用します。

このアプリケーションを実行してみましょう。次の出力が表示されます。

Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be)
   Points: 74
   HasGoldStatus: True
   MemberSince: 4/4/2009 12:00:00 AM (Utc)
   CreditRating: Neutral
   AverageRating: 0

   Orders:
      Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be

Press <ENTER> to exit...

HQLを使用してFirstNameが文字Hで始まるすべての顧客を取得する別の簡単な例を見てみましょう。

using System;
using System.Data;
using System.Linq;
using System.Reflection;

using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Criterion;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;

namespace NHibernateDemo {

   internal class Program {

      private static void Main() {

         var cfg = ConfigureNHibernate();
         var sessionFactory = cfg.BuildSessionFactory();
         using(var session = sessionFactory.OpenSession())

         using(var tx = session.BeginTransaction()) {
            var customers = session.CreateQuery("select c from Customer c
               where c.FirstName like 'H%'");

            foreach (var customer in customers.List<Customer>()) {
               Console.WriteLine(customer);
            }

            tx.Commit();
         }

         Console.WriteLine("Press <ENTER> to exit...");
         Console.ReadLine();
      }

      private static Configuration ConfigureNHibernate() {

         NHibernateProfiler.Initialize();
         var cfg = new Configuration();

         cfg.DataBaseIntegration(x => {
            x.ConnectionStringName = "default";
            x.Driver<SqlClientDriver>();
            x.Dialect<MsSql2008Dialect>();
            x.IsolationLevel = IsolationLevel.RepeatableRead;
            x.Timeout = 10;
            x.BatchSize = 10;
         });

         cfg.SessionFactory().GenerateStatistics();
         cfg.AddAssembly(Assembly.GetExecutingAssembly());
         return cfg;
      }
   }
}

アプリケーションを再度実行してみましょう。名前がHで始まるすべての顧客がこのクエリから返されることがわかります。

Herman Crooks (4ead3480-6bce-11e1-b15c-6cf049ee52be)
   Points: 74
   HasGoldStatus: True
   MemberSince: 12/3/2010 12:00:00 AM (Utc)
   CreditRating: Neutral
   AverageRating: 0

   Orders:
      Order Id: 4ead3480-6bce-11e1-b15d-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b15e-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b15f-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b160-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b161-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b162-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b163-6cf049ee52be

Hudson Bins (4ec03f80-6bce-11e1-b2b7-6cf049ee52be)
   Points: 56
   HasGoldStatus: False
   MemberSince: 10/20/2008 12:00:00 AM (Utc)
   CreditRating: Terrible
   AverageRating: 0

   Orders:
      Order Id: 4ec03f80-6bce-11e1-b2b8-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2b9-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2ba-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2bb-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2bc-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2bd-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2be-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2bf-6cf049ee52be

Hettie Feest (4ec50240-6bce-11e1-b300-6cf049ee52be)
   Points: 82
   HasGoldStatus: False
   MemberSince: 4/10/2009 12:00:00 AM (Utc)
   CreditRating: Neutral
   AverageRating: 0

   Orders:
      Order Id: 4ec50240-6bce-11e1-b301-6cf049ee52be
      Order Id: 4ec50240-6bce-11e1-b302-6cf049ee52be
      Order Id: 4ec50240-6bce-11e1-b303-6cf049ee52be

Press <ENTER> to exit...

注文数が9を超える顧客すべての注文が必要になるなど、より複雑なことを行うことができます。 以下は同じものに対するHQLクエリです。

var customers = session.CreateQuery("select c from Customer c
   where size(c.Orders) > 9");

foreach (var customer in customers.List<Customer>()) {
   Console.WriteLine(customer);
}

また、ここでサイズまたはカウントまたは長さが必要であることを示す必要があります。 HQLには、上記の特別なサイズの方法を使用するオプションがあります。

これを記述する別の方法は、必要に応じて c.Orders.size であり、これは正確な効果があります。

var customers = session.CreateQuery("select c from Customer c
   where c.Orders.size > 9");

foreach (var customer in customers.List<Customer>()) {
   Console.WriteLine(customer);
}

このアプリケーションを実行しましょう。

Lindsay Towne (4ea3aef6-6bce-11e1-b0cb-6cf049ee52be)
   Points: 50
   HasGoldStatus: False
   MemberSince: 4/13/2007 12:00:00 AM (Utc)
   CreditRating: VeryGood
   AverageRating: 0

   Orders:
      Order Id: 4ea3aef6-6bce-11e1-b0cc-6cf049ee52be
      Order Id: 4ea3aef6-6bce-11e1-b0cd-6cf049ee52be
      Order Id: 4ea3aef6-6bce-11e1-b0ce-6cf049ee52be
      Order Id: 4ea3aef6-6bce-11e1-b0cf-6cf049ee52be
      Order Id: 4ea3aef6-6bce-11e1-b0d0-6cf049ee52be
      Order Id: 4ea3aef6-6bce-11e1-b0d1-6cf049ee52be
      Order Id: 4ea3aef6-6bce-11e1-b0d2-6cf049ee52be
      Order Id: 4ea3aef6-6bce-11e1-b0d3-6cf049ee52be
      Order Id: 4ea3aef6-6bce-11e1-b0d4-6cf049ee52be
      Order Id: 4ea3aef6-6bce-11e1-b0d5-6cf049ee52be

Wyman Hammes (4ea61056-6bce-11e1-b0e2-6cf049ee52be)
   Points: 32
   HasGoldStatus: False
   MemberSince: 2/5/2011 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 0

   Orders:
      Order Id: 4ea61056-6bce-11e1-b0e3-6cf049ee52be
      Order Id: 4ea61056-6bce-11e1-b0e4-6cf049ee52be
      Order Id: 4ea61056-6bce-11e1-b0e5-6cf049ee52be
      Order Id: 4ea61056-6bce-11e1-b0e6-6cf049ee52be
      Order Id: 4ea61056-6bce-11e1-b0e7-6cf049ee52be
      Order Id: 4ea61056-6bce-11e1-b0e8-6cf049ee52be
      Order Id: 4ea61056-6bce-11e1-b0e9-6cf049ee52be
      Order Id: 4ea61056-6bce-11e1-b0ea-6cf049ee52be
      Order Id: 4ea61056-6bce-11e1-b0eb-6cf049ee52be
      Order Id: 4ea61056-6bce-11e1-b0ec-6cf049ee52be

Press <ENTER> to exit...

9件以上の注文があるすべての顧客がデータベースから取得されていることがわかります。

NHibernate-基準クエリ

この章では、基準クエリメカニズムについて説明します。 NHibernate Query by Criteria API を使用すると、実行時に条件オブジェクトを操作してクエリを作成できます。

  • このアプローチにより、文字列を直接操作せずに動的に制約を指定できますが、HQLの柔軟性やパワーをあまり失うことはありません。
  • 一方、基準として表現されたクエリは、HQLで表現されたクエリよりも読みにくいことがよくあります。
  • 古典的な基準構文は、次のプログラムに示すように、オブジェクトベースのクエリAPIです。
var customers = session.CreateCriteria<Customer>().Add(Restrictions.Like("FirstName", "H%"));
  • ご覧のとおり、顧客のセッション作成基準を実行しており、現在、そのクエリに制限オブジェクトを追加しています。
  • これは、ユーザーが特定のオプションを選択できるが、他のオプションは選択できないクエリページに役立ちます。
  • WHERE句でANDまたはORを使用できるHQLまたはLINQではなく、クエリ構造のようなツリーのようなクエリを作成する方が簡単です。
  • これらの条件オブジェクトを使用して、追加の制限を追加するだけで簡単です。

クエリを作成し、 createCriteria を使用して条件APIにアクセスし、最初の名前がHで始まる制限を追加する簡単な例を見てみましょう。

using System;
using System.Data;
using System.Linq;
using System.Reflection;

using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Criterion;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;

namespace NHibernateDemo {

   internal class Program {

      private static void Main() {

         var cfg = ConfigureNHibernate();
         var sessionFactory = cfg.BuildSessionFactory();
         using(var session = sessionFactory.OpenSession())

         using(var tx = session.BeginTransaction()) {
            var customers = session.CreateCriteria<Customer>()
               .Add(Restrictions.Like("FirstName", "H%"));

            foreach (var customer in customers.List<Customer>()) {
               Console.WriteLine(customer);
            }

            tx.Commit();
         }

         Console.WriteLine("Press <ENTER> to exit...");
         Console.ReadLine();
      }

      private static Configuration ConfigureNHibernate() {

         NHibernateProfiler.Initialize();
         var cfg = new Configuration();

         cfg.DataBaseIntegration(x => {
            x.ConnectionStringName = "default";
            x.Driver<SqlClientDriver>();
            x.Dialect<MsSql2008Dialect>();
            x.IsolationLevel = IsolationLevel.RepeatableRead;
            x.Timeout = 10;
            x.BatchSize = 10;
         });

         cfg.SessionFactory().GenerateStatistics();
         cfg.AddAssembly(Assembly.GetExecutingAssembly());
         return cfg;
      }
   }
}

上記のコードをコンパイルして実行すると、次の出力が表示されます。

Herman Crooks (4ead3480-6bce-11e1-b15c-6cf049ee52be)
   Points: 74
   HasGoldStatus: True
   MemberSince: 12/3/2010 12:00:00 AM (Utc)
   CreditRating: Neutral
   AverageRating: 0

   Orders:
      Order Id: 4ead3480-6bce-11e1-b15d-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b15e-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b15f-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b160-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b161-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b162-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b163-6cf049ee52be

Hudson Bins (4ec03f80-6bce-11e1-b2b7-6cf049ee52be)
   Points: 56
   HasGoldStatus: False
   MemberSince: 10/20/2008 12:00:00 AM (Utc)
   CreditRating: Terrible
   AverageRating: 0

   Orders:
      Order Id: 4ec03f80-6bce-11e1-b2b8-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2b9-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2ba-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2bb-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2bc-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2bd-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2be-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2bf-6cf049ee52be

Hettie Feest (4ec50240-6bce-11e1-b300-6cf049ee52be)
   Points: 82
   HasGoldStatus: False
   MemberSince: 4/10/2009 12:00:00 AM (Utc)
   CreditRating: Neutral
   AverageRating: 0

   Orders:
      Order Id: 4ec50240-6bce-11e1-b301-6cf049ee52be
      Order Id: 4ec50240-6bce-11e1-b302-6cf049ee52be
      Order Id: 4ec50240-6bce-11e1-b303-6cf049ee52be

Press <ENTER> to exit…

名が「Laverne」に等しい顧客を取得する別の簡単な例を見てみましょう

using System;
using System.Data;
using System.Linq;
using System.Reflection;

using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Criterion;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;

namespace NHibernateDemo {

   internal class Program {

      private static void Main() {

         var cfg = ConfigureNHibernate();
         var sessionFactory = cfg.BuildSessionFactory();
         using(var session = sessionFactory.OpenSession())

         using(var tx = session.BeginTransaction()) {
            var customers = session.CreateCriteria<Customer>()
               .Add(Restrictions.Eq("FirstName", "Laverne")) .List<Customer>();

            foreach (var customer in customers) {
               Console.WriteLine(customer);
            }

            tx.Commit();
         }

         Console.WriteLine("Press <ENTER> to exit...");
         Console.ReadLine();
      }

      private static Configuration ConfigureNHibernate() {

         NHibernateProfiler.Initialize();
         var cfg = new Configuration();

         cfg.DataBaseIntegration(x => {
            x.ConnectionStringName = "default";
            x.Driver<SqlClientDriver>();
            x.Dialect<MsSql2008Dialect>();
            x.IsolationLevel = IsolationLevel.RepeatableRead;
            x.Timeout = 10;
            x.BatchSize = 10;
         });

         cfg.SessionFactory().GenerateStatistics();
         cfg.AddAssembly(Assembly.GetExecutingAssembly());
         return cfg;
      }
   }
}

このアプリケーションを再度実行すると、次の出力が表示されます。

Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be)
   Points: 74
   HasGoldStatus: True
   MemberSince: 4/4/2009 12:00:00 AM (Utc)
   CreditRating: Neutral
   AverageRating: 0

   Orders:
      Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be

Press <ENTER> to exit...

現在、基準APIの主な欠点の1つは、プロパティ名に含まれるこれらの不透明な文字列です。 したがって、名が別の名前にリファクタリングされた場合、リファクタリングツールは必ずしも不透明な文字列を取得するとは限りません。

NHibernate-QueryOverクエリ

この章では、QueryOverクエリについて説明します。 次のクエリに示すように、メソッドチェーン構文を使用したLINQに似た新しい構文です。

var customers = session.QueryOver<Customer>() .Where(x => x.FirstName == "Laverne");
  • それはまだ隠れた基準ですが、今ではクエリは強く型付けされています。
  • 基準クエリで見たように、最初の名前は単なる不透明な文字列であり、実際には x.FirstName を使用しているため、最初の名前はリファクタリングされ、名前が変更されます。クエリオーバー。
  • 似たようなことはまだたくさんできますが、クエリの理解構文をクエリオーバーで使用することはできません。メソッドチェーン構文を使用する必要があり、リンクと条件を組み合わせて一致させることはできません。
  • 多くのクエリでは、APIを介したクエリは非常に便利で、Criteriaを直接使用するよりもオブジェクトの構文を理解するのがはるかに簡単です。

クエリオーバーを使用して、名前がLaverneである顧客を取得する簡単な例を見てみましょう。

using System;
using System.Data;
using System.Linq;
using System.Reflection;

using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Criterion;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;

namespace NHibernateDemo {

   internal class Program {

      private static void Main() {

         var cfg = ConfigureNHibernate();
         var sessionFactory = cfg.BuildSessionFactory();
         using(var session = sessionFactory.OpenSession())

         using(var tx = session.BeginTransaction()) {
            var customers = session.QueryOver<Customer>()
               .Where(x => x.FirstName == "Laverne");

            foreach (var customer in customers.List()) {
               Console.WriteLine(customer);
            }

            tx.Commit();
         }

         Console.WriteLine("Press <ENTER> to exit...");
         Console.ReadLine();
      }

      private static Configuration ConfigureNHibernate() {

         NHibernateProfiler.Initialize();
         var cfg = new Configuration();

         cfg.DataBaseIntegration(x => {
            x.ConnectionStringName = "default";
            x.Driver<SqlClientDriver>();
            x.Dialect<MsSql2008Dialect>();
            x.IsolationLevel = IsolationLevel.RepeatableRead;
            x.Timeout = 10;
            x.BatchSize = 10;
         });

         cfg.SessionFactory().GenerateStatistics();
         cfg.AddAssembly(Assembly.GetExecutingAssembly());
         return cfg;
      }
   }
}

お分かりのように、それはまだカバーの下の基準ですが、より良い構文です。

上記のコードをコンパイルして実行すると、次の出力が表示されます。

Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be)
   Points: 74
   HasGoldStatus: True
   MemberSince: 4/4/2009 12:00:00 AM (Utc)
   CreditRating: Neutral
   AverageRating: 0

   Orders:
      Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be

Press <ENTER> to exit...

欠点の1つは、次のプログラムに示すように、* FirstName.StartsWith(“ A”)*と言いたいことです。

var customers = session.QueryOver<Customer>() .Where(x => x.FirstName.StartsWith("A"));

foreach (var customer in customers.List()) {
   Console.WriteLine(customer);
}

tx.Commit();

アプリケーションを再度実行してみましょう。この StartsWith メソッドがわからないため、これはLINQプロバイダーではないことがわかります。したがって、* RunTime例外*が発生します。

実行時例外

例外は、認識されないメソッド呼び出しを示しています。 ここでは明らかなことを行っていますが、必ずしも機能するとは限りません。

次のコードに示すように、FirstNameが「A%」に等しいなど、他のことを試してみましょう。

var customers = session.QueryOver<Customer>() .Where(x => x.FirstName == "A%");

foreach (var customer in customers.List()) {
   Console.WriteLine(customer);
}

もう一度実行してみましょう。次のように結果が返されないことがわかります。

Press <ENTER> to exit...

結果が得られない理由を理解するために、NHibernateプロファイラーを見てみましょう。

NHibernateプロファイルの結果

ご覧のとおり、名はA%に等しく、そうではありません。 A%は、like演算子を使用したSQLで使用されます。 次のプログラムに示すように、WHERE句に制限を作成する必要があります。

var customers = session.QueryOver<Customer>()
   .Where(Restrictions.On<Customer>(c => c.FirstName).IsLike("A%"));

foreach (var customer in customers.List()) {
   Console.WriteLine(customer);
}

アプリケーションを再度実行してみましょう。すべての顧客が、Aで始まる名で取得されていることがわかります。

Alejandrin Will (4ea3aef6-6bce-11e1-b0b4-6cf049ee52be)
   Points: 24
   HasGoldStatus: False
   MemberSince: 10/1/2011 12:00:00 AM (Utc)
   CreditRating: VeryVeryGood
   AverageRating: 0

   Orders:
      Order Id: 4ea3aef6-6bce-11e1-b0b5-6cf049ee52be

Austyn Nolan (4ea871b6-6bce-11e1-b110-6cf049ee52be)
   Points: 67
   HasGoldStatus: True
   MemberSince: 12/29/2007 12:00:00 AM (Utc)
   CreditRating: Neutral
   AverageRating: 0

   Orders:
      Order Id: 4ea871b6-6bce-11e1-b111-6cf049ee52be

Antonia Murphy (4ea871b6-6bce-11e1-b121-6cf049ee52be)
   Points: 72
   HasGoldStatus: True
   MemberSince: 6/15/2009 12:00:00 AM (Utc)
   CreditRating: Terrible
   AverageRating: 0

   Orders:
      Order Id: 4ea871b6-6bce-11e1-b122-6cf049ee52be
      Order Id: 4ea871b6-6bce-11e1-b123-6cf049ee52be
      Order Id: 4ea871b6-6bce-11e1-b124-6cf049ee52be
      Order Id: 4ea871b6-6bce-11e1-b125-6cf049ee52be
      Order Id: 4ea871b6-6bce-11e1-b126-6cf049ee52be
      Order Id: 4ea871b6-6bce-11e1-b127-6cf049ee52be
      Order Id: 4ea871b6-6bce-11e1-b128-6cf049ee52be
      Order Id: 4ea871b6-6bce-11e1-b129-6cf049ee52be
      Order Id: 4ea871b6-6bce-11e1-b12a-6cf049ee52be

この新しい QueryOver 構文を使用することを除いて、以前と同じように機能します。 多くの開発者は、LINQ構文がより親しみやすく、多くの場合正しいことを行うことを認識しています。

LINQで処理できない場合は、HQLまたは基準を調べて、それがより適切かどうかを確認します。

異なる構文を提供するだけなので、作成基準とQueryOverの両方の基準は、NHibernateを使用してデータベースからデータを引き出すことができるさらに別のクエリメカニズムを提供します。

NHibernate-ネイティブSQL

この章では、NHibernateでネイティブSQLクエリを使用する方法について説明します。 手書きのSQLを何年も使用している場合、ORMがあなたが慣れている表現力と柔軟性の一部を奪うことを心配するかもしれません。

  • NHibernateの強力なクエリ機能を使用すると、SQLで行うことのほとんどすべてを実行できます。
  • NHibernateの独自のクエリ機能を使用して、望みどおりの動作をさせることができないまれな場合。 *NHibernateでは、データベースのネイティブSQLダイアレクトを使用してオブジェクトを取得できます。

NHibernateのネイティブSQLクエリの簡単な例を見てみましょう。

using System;
using System.Data;
using System.Linq;
using System.Reflection;

using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Criterion;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;
using NHibernate;

namespace NHibernateDemo {

   internal class Program {

      private static void Main() {

         var cfg = ConfigureNHibernate();
         var sessionFactory = cfg.BuildSessionFactory();
         using(var session = sessionFactory.OpenSession())

         using(var tx = session.BeginTransaction()) {
            IQuery sqlQuery = session.CreateSQLQuery("SELECT* FROM
               CUSTOMER").AddEntity(typeof(Customer));
            var customers = sqlQuery.List<Customer>();

            foreach (var customer in customers) {
               Console.WriteLine(customer);
            }

            tx.Commit();
         }

         Console.WriteLine("Press <ENTER> to exit...");
         Console.ReadLine();
      }

      private static Configuration ConfigureNHibernate() {

         NHibernateProfiler.Initialize();
         var cfg = new Configuration();

         cfg.DataBaseIntegration(x => {
            x.ConnectionStringName = "default";
            x.Driver<SqlClientDriver>();
            x.Dialect<MsSql2008Dialect>();
            x.IsolationLevel = IsolationLevel.RepeatableRead;
            x.Timeout = 10;
            x.BatchSize = 10;
         });

         cfg.SessionFactory().GenerateStatistics();
         cfg.AddAssembly(Assembly.GetExecutingAssembly());
         return cfg;
      }
   }
}

上記の例では、* CreateSQLQuery()*を使用してオブジェクトのリストを取得します。また、クエリが返すルートエンティティタイプがCustomerとして指定されていることにも気付くでしょう。

アプリケーションを実行してみましょう。すべての顧客がデータベースから取得されていることがわかります。

Emerson Prosacco (4ec2a0e0-6bce-11e1-b2cf-6cf049ee52be)
   Points: 17
   HasGoldStatus: False
   MemberSince: 6/22/2007 12:00:00 AM (Utc)
   CreditRating: Excellent
   AverageRating: 0

   Orders:
      Order Id: 4ec2a0e0-6bce-11e1-b2d0-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2d1-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2d2-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2d3-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2d4-6cf049ee52be

Kaci Friesen (4ec2a0e0-6bce-11e1-b2d5-6cf049ee52be)
   Points: 30
   HasGoldStatus: True
   MemberSince: 5/25/2007 12:00:00 AM (Utc)
   CreditRating: VeryVeryGood
   AverageRating: 0

   Orders:
      Order Id: 4ec2a0e0-6bce-11e1-b2d6-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2d7-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2d8-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2d9-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2da-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2db-6cf049ee52be

Eveline Waters (4ec2a0e0-6bce-11e1-b2dc-6cf049ee52be)
   Points: 58
   HasGoldStatus: False
   MemberSince: 10/29/2009 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 0

   Orders:
      Order Id: 4ec2a0e0-6bce-11e1-b2dd-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2de-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2df-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2e0-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2e2-6cf049ee52be

Molly Kuhn (4ec2a0e0-6bce-11e1-b2e3-6cf049ee52be)
   Points: 73
   HasGoldStatus: False
   MemberSince: 12/16/2007 12:00:00 AM (Utc)
   CreditRating: VeryGood
   AverageRating: 0

   Orders:
      Order Id: 4ec2a0e0-6bce-11e1-b2e4-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2e5-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2e6-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2e7-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2e8-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2e9-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2ea-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2eb-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2ec-6cf049ee52be

以下は、ネイティブSQLクエリを記述する別の方法です。

IList<Customer> customers = session.CreateSQLQuery("SELECT * FROM CUSTOMER")
   .AddScalar("Id", NHibernateUtil.Guid)
   .AddScalar("FirstName", NHibernateUtil.String)
   .AddScalar("LastName", NHibernateUtil.String) .List<Customer>();
  • ご覧のとおり、上記のクエリでは、SQLクエリ文字列と、返される列と型が指定されています。
  • これにより、Customerテーブルの各列にスカラー値を持つオブジェクト配列のIListが返されます。
  • クエリが*を使用しており、リストされている3つ以上の列を返す可能性がある場合でも、これらの3つの列のみが返されます。

別の簡単な例を見てみましょう。

IList<Customer> customers = session.CreateSQLQuery("SELECT * FROM CUSTOMER WHERE
   FirstName = 'Laverne'")
   .AddEntity(typeof(Customer)) .List<Customer>();

foreach (var customer in customers) {
   Console.WriteLine(customer);
}

アプリケーションを再度実行すると、次の出力が表示されます。

Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be)
   Points: 74
   HasGoldStatus: True
   MemberSince: 4/4/2009 12:00:00 AM (Utc)
   CreditRating: Neutral
   AverageRating: 0

   Orders:
      Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be

Press <ENTER> to exit...

同様に、任意のタイプのSQLクエリを指定して、データベースからデータを取得できます。

NHibernate-流Hな休止状態

この章では、流NHなNHibernateを取り上げます。 Fluent NHibernateはマッピングのもう1つの方法です。または、NHibernateの標準XMLマッピングファイルの代替と言えます。 XML *(。hbm.xmlファイル)*ドキュメントを記述する代わりに。 Fluent NHibernateの助けを借りて、強く型付けされたC#コードでマッピングを書くことができます。

  • Fluent NHibernateでは、マッピングはアプリケーションの他の部分とともにコンパイルされます。
  • アプリケーションコードと同じようにマッピングを簡単に変更でき、コンパイラはタイプミスで失敗します。
  • 従来の構成システムがあり、命名規則や他の多くのものをオーバーライドするためのパターンを指定できます。
  • 一度名前を付ける方法を設定することもできます。その後、Fluent NHibernateが残りを行います。

新しいコンソールプロジェクトを作成して、簡単な例を見てみましょう。 この章では、次の図に示すように、単純なCustomerテーブルを持つ単純なデータベースを使用します。

シンプルな顧客テーブル

Fluent NHibernateをインストールする

最初のステップは、Fluent NHibernateを開始することです。FluentNHibernateパッケージをインストールします。 したがって、 NuGet Package Manager Console を開き、次のコマンドを入力します。

PM> install-package FluentNHibernate

正常にインストールされると、次のメッセージが表示されます。

正常にインストールされました

Customerの単純なモデルクラスを追加してみましょう。次のプログラムは、Customerクラスの実装を示しています。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace FluentNHibernateDemo {
   class Customer {
      public virtual int Id { get; set; }
      public virtual string FirstName { get; set; }
      public virtual string LastName { get; set; }
   }
}

流NHなNHibernateを使用してマッピングを作成する必要があるため、プロジェクトにもう1つのクラス CustomerMap を追加します。 CustomerMapクラスの実装は次のとおりです。

using FluentNHibernate.Mapping;
using System;
using System.Collections.Generic;
using System.Linq; using System.Text;
using System.Threading.Tasks;

namespace FluentNHibernateDemo {
   class CustomerMap : ClassMap<Customer> {
      public CustomerMap() {
         Id(x => x.Id);
         Map(x => x.FirstName);
         Map(x => x.LastName);
         Table("Customer");
      }
   }
}

別のクラス NHibernateHelper を追加して、異なる構成設定を設定します。

using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using NHibernate;
using NHibernate.Tool.hbm2ddl;

namespace FluentNHibernateDemo {

   public class NHibernateHelper {

      private static ISessionFactory _sessionFactory;

      private static ISessionFactory SessionFactory {
         get {
            if (_sessionFactory == null)
            InitializeSessionFactory(); return _sessionFactory;
         }
      }

      private static void InitializeSessionFactory() {
         _sessionFactory = Fluently.Configure()

         String Data Source = asia13797\\sqlexpress;
         String Initial Catalog = NHibernateDemoDB;
         String Integrated Security = True;
         String Connect Timeout = 15;
         String Encrypt = False;
         String TrustServerCertificate = False;
         String ApplicationIntent = ReadWrite;
         String MultiSubnetFailover = False;

         .Database(MsSqlConfiguration.MsSql2008 .ConnectionString(
            @"Data Source + Initial Catalog + Integrated Security + Connect Timeout
            + Encrypt + TrustServerCertificate + ApplicationIntent +
            MultiSubnetFailover") .ShowSql() )

         .Mappings(m => m.FluentMappings
         .AddFromAssemblyOf<Program>())
         .ExposeConfiguration(cfg => new SchemaExport(cfg)
         .Create(true, true))
         .BuildSessionFactory();
      }

      public static ISession OpenSession() {
         return SessionFactory.OpenSession();
      }
   }
}

次に、セッションを開始する Program.cs ファイルに移動して、新しい顧客を作成し、その顧客を以下に示すようにデータベースに保存します。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace FluentNHibernateDemo {
   class Program {

      static void Main(string[] args) {

         using (var session = NHibernateHelper.OpenSession()) {

            using (var transaction = session.BeginTransaction()) {
               var customer = new Customer {
                  FirstName = "Allan",
                  LastName = "Bomer"
               };

               session.Save(customer);
               transaction.Commit();
               Console.WriteLine("Customer Created: " + customer.FirstName + "\t" +
                  customer.LastName);
            }

            Console.ReadKey();
         }
      }
   }
}

アプリケーションを実行してみましょう。次の出力が表示されます。

if exists (select * from dbo.sysobjects where id = object_id(N'Customer') and
   OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table Customer

create table Customer (
   Id INT IDENTITY NOT NULL,
   FirstName NVARCHAR(255) null,
   LastName NVARCHAR(255) null,
   primary key (Id)
)

NHibernate: INSERT INTO Customer (FirstName, LastName) VALUES (@p0, @p1);
   select SCOPE_IDENTITY();@p0 = 'Allan' [Type: String (4000)],
   @p1 = 'Bomer' [Type: String (4000)]
   Customer Created: Allan Bomer

ご覧のとおり、新しい顧客が作成されます。 顧客レコードを表示するには、データベースに移動してデータの表示を確認します。1人の顧客が追加されていることがわかります。

顧客レコード