NHibernate - ความสัมพันธ์

ในบทนี้เราจะดูความสัมพันธ์ใน NHibernate เรามาสนใจวิธีที่เราจะเข้าใจความสัมพันธ์ใน NHibernate กันเถอะ วิธีที่ง่ายที่สุดคือการคิดถึงความสัมพันธ์จากมุมมองของฐานข้อมูล

  • ก่อนอื่นเราจะสร้างแอปพลิเคชันใหม่ซึ่งเราจะสร้างความสัมพันธ์ระหว่างลูกค้าและหน่วยงานสั่งซื้อ

  • ความสัมพันธ์แรกที่เราจะดูคือความสัมพันธ์คอลเลกชันแบบคลาสสิก

  • เรามีลูกค้าที่รวบรวมคำสั่งซื้อ

  • นี่คือความสัมพันธ์แบบหนึ่งต่อกลุ่มและแสดงในฐานข้อมูลด้วยตาราง 2 ตารางและมีรหัสลูกค้าอยู่ในตารางคำสั่งซื้อและเรามีความสัมพันธ์คีย์ต่างประเทศกลับไปยังลูกค้า

อันดับแรกเราต้องสร้างฐานข้อมูลและสองตาราง Customer and 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

มันจะสร้างตารางสองตารางในฐานข้อมูล ภาพต่อไปนี้แสดงตารางลูกค้า

ภาพต่อไปนี้แสดงตารางคำสั่งซื้อซึ่งคุณสามารถดูความสัมพันธ์ของ Foreign Key กลับไปยังลูกค้าได้

เราจำเป็นต้องกำหนดสตริงการเชื่อมต่อในไฟล์ 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 Console

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.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.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.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); 
      } 
   } 
}

ความสัมพันธ์แบบหลายต่อหนึ่ง

เราต้องแมปคลาส 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>

ความสัมพันธ์แบบหนึ่งต่อกลุ่ม

ในที่นี้เราจะดูความสัมพันธ์แบบหนึ่งต่อกลุ่มในกรณีนี้ระหว่างลูกค้าและคำสั่งซื้อ เรามีลูกค้าของเราที่นี่เรากำลังสร้างใหม่และคุณจะเห็นว่าคอลเล็กชันเริ่มต้นด้วยคำสั่งซื้อคู่ต่อไปนี้

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 แล้วโหลดซ้ำในเซสชันอื่นในวิธีการหลักดังที่แสดงในโปรแกรมต่อไปนี้

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>

นี่คือชุดและชื่อของคอลเลกชันนี้คือ 'คำสั่งซื้อ' ซึ่งเก็บไว้ในตารางที่เรียกว่าคำสั่งซื้อ เราจำเป็นต้องระบุคีย์ซึ่งเป็นชื่อของคีย์ต่างประเทศหรือเพื่อค้นหาคำสั่งซื้อ คำสั่งซื้อเหล่านี้ถูกระบุหรือเป็นของลูกค้าผ่านรหัสลูกค้า จากนั้นฉันต้องสังเกตว่านี่เป็นความสัมพันธ์แบบหนึ่งต่อกลุ่มและเป็นไปตามคลาสลำดับ

เราจำเป็นต้องเปลี่ยนแปลงวิธีการหลักเล็กน้อยโดยบันทึกคำสั่งซื้อของลูกค้าใหม่ลงในฐานข้อมูลตามที่แสดงในโปรแกรมต่อไปนี้

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();
}

เราได้ระบุด้วยว่าลูกค้ารายใดสั่งซื้อสินค้านั้น ๆ ดังนั้นเราจำเป็นต้องสร้างความสัมพันธ์แบบกลุ่มต่อหนึ่งเพื่อเชื่อมโยงคำสั่งซื้อนั้นกลับไปยังลูกค้ารายนั้น

ลองเข้าไปใน 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"/> 
		
   </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...