NHibernate - Ленивая загрузка
В этой главе мы рассмотрим функцию отложенной загрузки. По умолчанию это совершенно другая концепция, и в NHibernate нет отложенной загрузки, например, если вы загружаете клиента, он не загружает все заказы.
Коллекция заказов будет загружена по запросу.
Любая ассоциация, будь то многие-к-одному или коллекция, загружается лениво по умолчанию, для нее требуется Open ISession.
Если вы закрыли сеанс или зафиксировали транзакцию, вы можете получить исключение ленивой загрузки, которое не может извлечь эти дополнительные объекты.
Вы должны быть осторожны с отложенной загрузкой и тем, сколько данных вам действительно нужно.
Вы можете отключить ленивую загрузку для всей ассоциации или поставить lazy равным 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.
Как вы можете видеть, у нас есть Select From Customer, с заданным идентификатором клиента, а также у нас есть еще одна таблица Select From Orders, когда она фактически обращается к коллекции этого клиента.
Итак, у нас есть 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.
Раньше у программы было два обращения к базе данных, теперь у нее только один, и это потому, что здесь выполняется левое внешнее соединение.
Мы видим, что он выполняет левое внешнее соединение между таблицей клиентов и таблицей заказов на основе идентификатора клиента, и, следовательно, он может загрузить всю эту информацию одновременно.
Мы сохранили 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, вы увидите, что у нас снова происходит активная загрузка соединения, но на этот раз она основана на запросе.