NHibernate - Lazy Loading
In diesem Kapitel werden wir die Funktion zum verzögerten Laden behandeln. Standardmäßig handelt es sich um ein völlig anderes Konzept, und NHibernate lädt nicht verzögert. Wenn Sie beispielsweise einen Kunden laden, werden nicht alle Bestellungen geladen.
Die Auftragsabholung wird bei Bedarf geladen.
Jede Zuordnung, egal ob es sich um eine Viele-zu-Eins-Zuordnung oder eine Sammlung handelt, ist standardmäßig faul geladen Open ISession.
Wenn Sie Ihre Sitzung geschlossen oder Ihre Transaktion festgeschrieben haben, kann eine Ausnahme beim verzögerten Laden auftreten, die diese zusätzlichen Objekte nicht abrufen kann.
Sie müssen vorsichtig sein, wenn Sie verzögert laden und wie viele Daten Sie tatsächlich benötigen.
Sie können das verzögerte Laden für eine gesamte Zuordnung deaktivieren oder das verzögerte Laden auf false setzen oder eine Abrufstrategie angeben.
Hier ist das Program.cs Dateiimplementierung.
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;
}
}
}
Um dies zu verstehen, führen wir die Anwendung aus und werfen einen Blick auf den NHibernate Profiler.
Wie Sie sehen können, haben wir die Option Aus Kunde auswählen, eine bestimmte Kunden-ID angegeben, und dann haben wir auch eine andere Tabelle Aus Bestellungen auswählen, wenn sie tatsächlich auf die Sammlung dieses Kunden zugreift.
Wir haben also 2 Roundtrips zur Datenbank. Jetzt möchten wir dies manchmal optimieren. Gehen wir dazu zumcustomer.hbm.xml Datei und fügen Sie eine Abrufstrategie hinzu und bitten Sie sie, einen Join-Abruf durchzuführen.
<?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>
Wie Sie sehen, haben wir in unserer Anwendung keinen Code geändert. Wir haben gerade eine Abrufstrategie in die Anwendung eingefügt customer.hbm.xml. Lassen Sie uns diese Anwendung erneut ausführen. Sie verhält sich immer noch genauso. Schauen wir uns NHibernate Profiler an.
Früher hatte das Programm zwei Roundtrips zur Datenbank, jetzt hat es nur noch einen, und das liegt daran, dass hier ein linker äußerer Join ausgeführt wird.
Wir können sehen, dass eine Linksaußenverknüpfung zwischen der Kundentabelle und der Auftragstabelle basierend auf der Kunden-ID durchgeführt wird, und daher können alle diese Informationen gleichzeitig geladen werden.
Wir haben 1 Hin- und Rückfahrt in der Datenbank gespeichert.
Der Nachteil ist, dass die Kundeninformationen in beiden Zeilen dupliziert werden und auf diese Weise ein linker SQL-Outer-Join funktioniert.
Mit der Abrufstrategie ziehen wir also etwas mehr Daten zurück und sparen eine Rundreise.
Sie können dies auch auf Abfrageebene tun. Gehen wir also zu Program.cs Datei und schauen Sie sich das einfachere neu geladene Beispiel an.
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();
}
Hier machen wir eine Ladung durch den Kunden. Jetzt ändern wir es in eine Abfrage und wir werden eine Linkabfrage verwenden, wie im folgenden Code gezeigt.
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();
}
Entfernen wir auch die Abrufstrategie aus dem customer.hbm.xml Datei.
<?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>
Lassen Sie uns diese Anwendung erneut ausführen und Sie werden die folgende Ausgabe sehen.
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...
Schauen wir uns nun den NHibernate Profiler an. Sie können sehen, dass dieser eifrige Join-Abruf erneut stattfindet, aber diesmal basiert er auf der Abfrage.