NHibernate-지연 로딩

이 장에서는 지연 로딩 기능에 대해 다룰 것입니다. 기본적으로 완전히 다른 개념이며 NHibernate에는 지연로드가 없습니다. 예를 들어 고객을로드하면 모든 주문을로드하지 않습니다.

  • 주문 컬렉션은 요청시로드됩니다.

  • 다 대일이든 컬렉션이든 관계없이 모든 연결은 기본적으로 지연로드됩니다. Open ISession.

  • 세션을 닫았거나 트랜잭션을 커밋 한 경우 이러한 추가 개체를 가져올 수없는 지연로드 예외가 발생할 수 있습니다.

  • 지연로드와 실제로 필요한 데이터의 양에주의해야합니다.

  • 전체 연결에 대해 지연로드를 끄거나 lazy equals false를 입력하거나 가져 오기 전략을 지정할 수도 있습니다.

여기 있습니다 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); 
            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(); 
      }
		
      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가 지정된 고객에서 선택이 있고 해당 고객의 컬렉션에 실제로 액세스 할 때 다른 주문에서 선택 테이블도 있습니다.

그래서 우리는 데이터베이스에 2 번 왕복합니다. 이제 때로는이를 최적화하고 싶을 것입니다. 이렇게하려면customer.hbm.xml 파일 가져 오기 전략을 추가하고 조인 가져 오기를 요청하십시오.

<?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" 
         fetch = "join"> 
         <key column = "CustomerId"/> 
         <one-to-many class = "Order"/> 
      </set> 
   
   </class> 
</hibernate-mapping>

애플리케이션에서 코드를 변경하지 않았 음을 알 수 있듯이 방금 가져 오기 전략을 추가했습니다. customer.hbm.xml. 이 애플리케이션을 다시 실행 해 보겠습니다. 여전히 똑같은 방식으로 작동합니다. NHibernate Profiler를 살펴 보겠습니다.

  • 이전에 프로그램은 데이터베이스에 대해 두 번의 왕복을 가졌지 만 지금은 하나만 있고 여기에서 왼쪽 외부 조인을 수행하기 때문입니다.

  • 고객 ID를 기반으로 고객 테이블과 주문 테이블 사이에 왼쪽 외부 조인을 수행하고 있으므로 모든 정보를 한 번에로드 할 수 있습니다.

  • 데이터베이스에 1 회 왕복을 저장했습니다.

  • 단점은 고객 정보가 두 줄에 모두 복제된다는 점이며 이것이 SQL 왼쪽 외부 조인이 작동하는 방식입니다.

  • 따라서 가져 오기 전략을 사용하면 더 많은 데이터를 가져와 왕복 시간을 절약 할 수 있습니다.

쿼리 수준에서도이 작업을 수행 할 수 있으므로 Program.cs 파일을 열고 더 간단한 다시로드 된 예제를보십시오.

using(var session = sessionFactory.OpenSession()) 

using(var tx = session.BeginTransaction()) { 
   //var query = from customer in session.Query<Customer>() 
   // select customer; 
   //var reloaded = query.Fetch(x => x.Orders).ToList();
	
   var reloaded = session.Load<Customer>(id); 
   Console.WriteLine("Reloaded:"); 
   Console.WriteLine(reloaded); 
   Console.WriteLine("The orders were ordered by: "); 
   
   foreach (var order in reloaded.Orders) { 
      Console.WriteLine(order.Customer); 
   } 
	
   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();
}

또한 가져 오기 전략을 customer.hbm.xml 파일.

<?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"> 
         <key column = "CustomerId"/> 
         <one-to-many class = "Order"/> 
      </set> 
   
   </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 (6ebacd17-f9ba-4ad8-9817-a5bb01112a5a)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: 16a6596b-d56e-41c7-9681-a5bb01112a60
      Order Id: d41d615b-0f21-4032-81db-a5bb01112a61
		
Press <ENTER> to exit...

이제 NHibernate Profiler를 살펴보면이 열성적인 조인 가져 오기가 다시 발생하는 것을 볼 수 있지만 이번에는 쿼리를 기반으로합니다.