NHibernate-역 관계

이 장에서는 역 관계라는 또 다른 기능을 다룰 것입니다. 컬렉션에서 볼 수있는 재미있는 옵션이 사실과 반비례하고 많은 개발자를 혼란스럽게합니다. 이 옵션에 대해 이야기 해 봅시다. 이것을 이해하려면 관계형 모델에 대해 생각해야합니다. 단일 외래 키를 사용하는 양방향 연결이 있다고 가정 해 보겠습니다.

  • 관계 적 관점에서 보면 외래 키가 하나 있으며, 이는 주문할 고객과 고객에게 주문을 모두 나타냅니다.

  • OO 모델에서 이러한 참조를 사용하는 단방향 연관이 있습니다.

  • 두 개의 단방향 연관이 데이터베이스에서 동일한 양방향 연관을 나타낸다는 말은 없습니다.

  • 여기서 문제는 NHibernate가이를 알기에 충분한 정보가 없다는 것입니다. customer.ordersorder.customer 데이터베이스에서 동일한 관계를 나타냅니다.

  • 우리는 제공해야합니다 inverse equals true 힌트로 단방향 연관이 동일한 데이터를 사용하고 있기 때문입니다.

  • 2 개의 참조가있는 관계를 저장하려고하면 NHibernate는 해당 참조를 두 번 업데이트하려고합니다.

  • 실제로 데이터베이스에 대한 추가 왕복을 수행하고 해당 외래 키에 대한 2 개의 업데이트도 포함합니다.

  • 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를 열고 실제로 어떻게 저장했는지 살펴 보겠습니다.

세 그룹의 진술이 있음을 알 수 있습니다. 첫 번째는 고객을 삽입하고 해당 고객의 ID는 강조 표시된 Guid입니다. 두 번째 명령문은 orders 테이블에 삽입하는 것입니다.

동일한 Customer Id Guid가 여기에 설정되어 있으므로 해당 외래 키를 설정하십시오. 마지막 문장은 업데이트로, 외래 키를 동일한 고객 ID로 다시 한 번 업데이트합니다.

이제 문제는 고객에게 주문이 있고 주문에 고객이 있다는 것입니다. NHibernate에게 실제로 동일한 관계라고 말하지 않은 방법이 없습니다. 우리가 이것을하는 방법은 역이 참입니다.

그래서 우리의 customer.hbm.xml 매핑 파일을 만들고 다음 코드와 같이 역을 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 프로파일 러를 엽니 다.

그것들이 어떻게 삽입되는지 살펴보면, 우리는 고객에게 삽입을 가져오고 주문에 삽입하지만, 주문이 저장 될 때 업데이트되고 있기 때문에 외래 키의 중복 업데이트가 없습니다.

  • 이제 단방향 연결 만 있고이 관계를 유지하는 집합 인 경우 역으로 설정하면 참이되면 해당 외래 키는 설정되지 않으며 해당 항목은 데이터베이스에 설정된 외래 키.

  • 다 대일 관계를 살펴보면 Order.hbm.xml 파일에서 역을 찾으면 실제로 역 속성이 없습니다.

  • 항상 자식 항목에서 설정되지만 다 대다 컬렉션이있는 경우 양쪽에서 설정할 수 있습니다.