在本章中,我们将介绍另一个反向关系特征.这是一个有趣的选项,你会在集合上看到它与true相反,它也会让许多开发人员感到困惑.那么让我们谈谈这个选项.要理解这一点,您必须考虑关系模型.假设您使用单个外键进行双向关联.
从关系角度来看,您有一个外键,它代表客户订购和订单给客户.
从OO模型中,您可以使用这些参考进行单向关联.
没有任何内容表明两个单向关联在数据库中表示相同的双向关联.
问题这里是NHibernate没有足够的信息知道 customer.orders 和 order.customer 代表数据库中的相同关系.
我们需要提供 inverse equals true 作为提示,这是因为单向关联使用相同的数据.
如果我们尝试保存这些有2个引用的关系,NHibernate会尝试两次更新该引用.
反向等于true告诉NHibernate忽略关系的哪一方.
当你将它应用到收集方时,NHibernate将始终从另一方更新外键,子对象端.
然后我们只对该外键进行一次更新,我们没有对该数据的其他更新.
这使我们可以防止对外键的这些重复更新,这也有助于我们防止外键违规.
让我们看一下 customer.cs 文件,您将在其中看到 AddOrder 方法,这里的想法是我们现在拥有的这个后退指针从订单返回到客户,需要进行设置.因此,当一个订单被添加到客户时,该客户的后退指针被设置,否则,它将为空,因此我们需要这个以在对象图中将它们正确连接在一起.
using System; using System.Text; using Iesi.Collections.Generic;namespace NHibernateDemo { public class Customer { public Customer() { MemberSince = DateTime.UtcNow; Orders = new HashedSet(); } 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 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() 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 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 (); x.Dialect (); x.IsolationLevel = IsolationLevel.RepeatableRead; x.Timeout = 10; x.BatchSize = 10; }); cfg.SessionFactory().GenerateStatistics(); cfg.AddAssembly(Assembly.GetExecutingAssembly()); return cfg; } } }
它会将其保存到数据库然后重新加载.现在让我们运行你的应用程序并打开NHibernate Profiler,看看它是如何实际保存它的.
您会注意到我们有3组语句.第一个将插入客户,该客户的ID是Guid,突出显示.第二个语句是插入到订单表中.
您会注意到在那里设置了相同的Customer Id Guid,因此设置了该外键.最后一个语句是更新,它将再次将外键更新为相同的客户ID.
现在的问题是客户有订单,订单有客户,我们没有办法告诉NHibernate它实际上是相同的关系.我们这样做的方法是反等于真.
所以让我们转到我们的 customer.hbm.xml 映射文件并设置反转等于true,如图所示在以下代码中.
保存订单时,它会从订单侧设置该外键.现在让我们再次运行这个应用程序并打开NHibernate探查器.
如果我们看看它们是如何插入的,我们在客户中获得插入,并插入订单,但我们没有外键的重复更新,因为它在保存订单时正在更新./p>
现在,您应该注意,如果您只有单向关联,并且它是维持此关系的集合,那么如果反转等于true,则永远不会设置外键,并且这些项永远不会在数据库中设置外键.
如果您查看 Order.hbm.xml 文件中的多对一关系,并且您查找了反向关系,则它实际上没有反向属性.
它总是从子项设置,但如果你有一个多对多的集合,你可以从任何一方设置它.