- 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 - 反向關係
在本章中,我們將介紹另一個功能,即反向關係。這是一個有趣的選項,您將在集合上看到它,這些集合的反向等於 true,並且它也讓許多開發人員感到困惑。所以讓我們來談談這個選項。要理解這一點,您確實需要考慮關係模型。假設您使用單個外部索引鍵進行雙向關聯。
從關係的角度來看,您有一個外部索引鍵,它表示客戶到訂單和訂單到客戶。
從 OO 模型來看,您使用這些引用進行單向關聯。
沒有任何東西表明兩個單向關聯在資料庫中表示相同的雙向關聯。
這裡的問題是 NHibernate 沒有足夠的資訊來知道customer.orders 和order.customer 在資料庫中表示相同的關係。
我們需要提供inverse equals true 作為提示,這是因為單向關聯使用相同的資料。
如果我們嘗試儲存這些有兩個引用指向它們的關係,NHibernate 將嘗試更新該引用兩次。
它實際上會對資料庫進行額外的往返,並且它還將對該外部索引鍵進行兩次更新。
inverse equals true 告訴 NHibernate 忽略關係的哪一側。
當您將其應用於集合側時,NHibernate 將始終從另一側,即從子物件側更新外部索引鍵。
然後我們只對該外部索引鍵進行一次更新,並且我們沒有對該資料進行額外的更新。
這使我們能夠防止對外部索引鍵進行重複更新,並且還有助於我們防止外部索引鍵衝突。
讓我們看一下customer.cs 檔案,您將在其中看到AddOrder 方法,這裡的想法是我們現在有了從訂單返回到客戶的反向指標,並且需要設定它。因此,當訂單新增到客戶時,該客戶的反向指標被設定,否則它將為 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,看看它實際上是如何儲存的。
您會注意到我們有 3 組語句。第一個將插入客戶,該客戶的 ID 是 Guid,已突出顯示。第二個語句是插入 orders 表。
您會注意到相同的 Customer Id Guid 在其中設定,因此設定了該外部索引鍵。最後一個語句是更新,它將再次將外部索引鍵更新為相同的客戶 ID。
現在問題是客戶擁有訂單,訂單擁有客戶,我們沒有告訴 NHibernate 它實際上是相同的關係。我們使用 inverse equals true 來做到這一點。
因此,讓我們轉到我們的customer.hbm.xml 對映檔案並將 inverse 設定為 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 Profiler。
如果我們檢視這些是如何插入的,我們會得到客戶的插入,以及訂單的插入,但我們沒有重複更新外部索引鍵,因為在儲存訂單時正在更新它。
現在,您應該注意,如果您只有一個單向關聯,並且是正在維護此關係的集合,那麼如果您將 inverse equals true,則永遠不會設定外部索引鍵,並且這些專案永遠不會在資料庫中設定其外部索引鍵。
如果您檢視Order.hbm.xml 檔案中的多對一關係,並查詢 inverse,它實際上沒有 inverse 屬性。
它始終從子項設定,但如果您有多對多集合,則可以從任一側設定它。