📜  NHibernate-逆关系

📅  最后修改于: 2020-11-19 05:40:39             🧑  作者: Mango


在本章中,我们将介绍另一个功能,即逆关系。这是一个有趣的选项,您会在集合中看到它与true相反,并且还使很多开发人员感到困惑。因此,让我们谈谈这个选项。要理解这一点,您确实必须考虑关系模型。假设您有一个使用单个外键的双向关联。

  • 从关系的角度来看,您有一个外键,它既代表要订购的客户,又代表对客户的订单。

  • 在OO模型中,您可以使用这些引用进行单向关联。

  • 没有什么可以说两个单向关联表示数据库中相同的双向关联。

  • 这里的问题是NHibernate没有足够的信息来知道customer.ordersorder.customer在数据库中表示相同的关系。

  • 我们需要提供反等于true作为提示,这是因为单向关联正在使用相同的数据。

  • 如果我们尝试保存有2个引用的关系,则NHibernate将尝试两次更新该引用。

  • 实际上,它将对数据库进行一次额外的往返,并且还将对该外键进行2次更新。

  • 反之等于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();
      } 
      
      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,看看它是如何保存它的。

反向NHibernate Profiler

您会注意到我们有3组语句。第一个将插入客户,该客户的ID为Guid,突出显示。第二条语句插入订单表中。

客户编号指南

您将注意到在其中设置了相同的客户ID指南,因此设置了该外键。最后一条语句是更新,它将外键再次更新为相同的客户ID。

客户Hbm

现在的问题是,客户有订单,而订单有客户,所以我们没有办法告诉NHibernate实际上是相同的关系。我们做到这一点的方法就是逆等于真。

因此,让我们转到customer.hbm.xml映射文件,并将反函数设置为true,如以下代码所示。

 
 
   
   
    
       
          
       
      
       
       
       
       
       
      
      
      
       
          
          
          
          
      
      
       
          
          
       
   
    

保存订单时,它将在订单侧设置该外键。现在,让我们再次运行该应用程序,然后打开NHibernate Profiler。

外键

如果我们看一下插入方式,我们会在客户中获得插入内容,并将插入内容插入订单中,但是我们没有外键的重复更新,因为在保存订单时会对其进行更新。

  • 现在,您应该注意,如果只有一个单向关联并且是保持这种关系的集合,那么如果您将逆等于设为真,则永远不会设置外键,并且这些项目也永远不会具有它们的数据库中设置的外键。

  • 如果查看Order.hbm.xml文件中的多对一关系,然后查找逆,则它实际上没有逆属性。

  • 它始终是从子项设置的,但是如果您有多对多集合,则可以从任一侧进行设置。