
- NHibernate 教程
- NHibernate - 首頁
- NHibernate - 概述
- NHibernate - 架構
- NHibernate - ORM
- NHibernate - 環境設定
- NHibernate - 入門
- NHibernate - 基本 ORM
- NHibernate - 基本 CRUD 操作
- NHibernate - Profiler
- 為對映檔案新增 IntelliSense
- NHibernate - 資料型別對映
- NHibernate - 配置
- NHibernate - 覆蓋配置
- NHibernate - 批次大小
- NHibernate - 快取
- NHibernate - 對映元件
- NHibernate - 關係
- NHibernate - 集合對映
- NHibernate - 級聯操作
- NHibernate - 延遲載入
- NHibernate - 反向關係
- NHibernate - Load/Get
- NHibernate - Linq
- NHibernate - 查詢語言
- NHibernate - Criteria 查詢
- NHibernate - QueryOver 查詢
- NHibernate - 原生 SQL
- NHibernate - Fluent Hibernate
- NHibernate 有用資源
- NHibernate - 快速指南
- NHibernate - 有用資源
- NHibernate - 討論
NHibernate - 關係
在本章中,我們將瞭解 NHibernate 中的關係。讓我們將注意力轉向如何理解 NHibernate 中的關係。最簡單的方法是從資料庫的角度來思考這些關係。
我們首先建立一個新的應用程式,在其中我們將建立客戶和訂單實體之間的一些關係。
我們將要研究的第一個關係是經典的集合關係。
我們有一個客戶,以及一個訂單集合。
這是一個一對多關係,它在資料庫中由 2 個表表示,訂單表上有一個客戶 ID,並且我們有一個指向客戶的反向外部索引鍵關係。
首先,我們需要建立一個數據庫和兩個表 Customer 和 Order。您可以在 SQL Server 資源管理器中指定以下查詢來建立它們。
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
它將在資料庫中建立兩個表。下圖顯示了 Customer 表。

下圖顯示了 Order 表,您可以在其中看到指向客戶的反向外部索引鍵關係。

我們需要在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。
以下是 Customer.cs 檔案的實現,其中我們有兩個類,一個是 Customer 類,另一個是 Location 類,其中物件用作 Customer 類中的地址。
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,其中 Customer 類對映到 Customer 表。
<?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); } } }
多對一關係
我們還需要將 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 並在 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 檔案,您可以在此處看到我們沒有對映實際的 orders 集合。因此,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 被識別或屬於某個客戶。然後我必須注意這是一個一對多關係,它與 Order 類有關。
我們還需要稍微更改 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(); }
我們還指定了哪個客戶訂購了該特定產品。因此,我們需要建立一個多對一關係,以將該訂單關聯回該客戶。
所以讓我們進入Order.hbm.xml 檔案並新增一個多對一,然後命名客戶欄位和帶有客戶 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...