NHibernate - Leniwe ładowanie
W tym rozdziale zajmiemy się funkcją leniwego ładowania. Domyślnie jest to zupełnie inna koncepcja, a NHibernate nie ma leniwego ładowania, na przykład jeśli ładujesz klienta, nie załaduje wszystkich zamówień.
Kolekcja zamówień zostanie załadowana na żądanie.
Każde skojarzenie, niezależnie od tego, czy jest to wiele do jednego, czy kolekcja, jest domyślnie ładowane z opóźnieniem, wymaga rozszerzenia Open ISession.
Jeśli zamknąłeś sesję lub zatwierdziłeś transakcję, możesz uzyskać wyjątek z opóźnionym ładowaniem, który nie może pobrać tych dodatkowych obiektów.
Musisz uważać na leniwe ładowanie i ile danych faktycznie potrzebujesz.
Możesz wyłączyć leniwe ładowanie dla całego skojarzenia lub ustawić leniwe równa się fałsz lub możesz również określić strategię pobierania.
Tutaj jest Program.cs implementacja plików.
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;
}
}
}
Aby to zrozumieć, uruchommy aplikację i przyjrzyjmy się NHibernate Profiler.
Jak widać, mamy opcję Select From Customer z określonym identyfikatorem klienta, a także inną tabelę Select From Orders, która faktycznie uzyskuje dostęp do kolekcji tego klienta.
Mamy więc 2 wycieczki do bazy danych. Czasami chcielibyśmy to zoptymalizować. Aby to zrobić, przejdźmy docustomer.hbm.xml file i dodaj strategię pobierania i poproś o wykonanie operacji pobierania z dołączeniem.
<?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>
Jak widać, nie zmieniliśmy żadnego kodu w naszej aplikacji, właśnie dodaliśmy strategię pobierania w customer.hbm.xml. Uruchommy ponownie tę aplikację, nadal zachowuje się dokładnie tak samo. Spójrzmy na NHibernate Profiler.
Wcześniej program miał dwie rundy do bazy danych, teraz ma tylko jedną, a to dlatego, że wykonuje tutaj lewe sprzężenie zewnętrzne.
Widzimy, że wykonuje lewe zewnętrzne połączenie między tabelą klientów a tabelą zamówień na podstawie identyfikatora klienta, a zatem jest w stanie załadować wszystkie te informacje naraz.
Zapisaliśmy 1 podróż w obie strony do bazy danych.
Wadą jest to, że informacje o kliencie zostaną zduplikowane w obu wierszach i tak działa lewe sprzężenie zewnętrzne SQL.
Tak więc dzięki strategii pobierania pobieramy nieco więcej danych i oszczędzamy podróż w obie strony.
Możesz to również zrobić na poziomie zapytania, więc przejdźmy do Program.cs plik i spójrz na prostszy, ponownie załadowany przykład.
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();
}
Tutaj robimy ładunek przez klienta. Teraz zmieńmy to na zapytanie i użyjemy zapytania łączącego, jak pokazano w poniższym kodzie.
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();
}
Usuńmy również strategię pobierania z customer.hbm.xml plik.
<?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>
Uruchommy ponownie tę aplikację, a zobaczysz następujące dane wyjściowe.
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...
Teraz spójrzmy na NHibernate Profiler, widzisz, że mamy to niecierpliwe pobieranie dołączeń, które ma miejsce ponownie, ale tym razem jest oparte na zapytaniu.