Nhibernate-relationships

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

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...