NHibernate - Lazy Loading
Neste capítulo, abordaremos o recurso de carregamento lento. É um conceito totalmente diferente por padrão e o NHibernate não tem carregamento lento, por exemplo, se você carregar um cliente, ele não carregará todos os pedidos.
A coleção de pedidos será carregada sob demanda.
Qualquer associação, seja um muitos-para-um ou uma coleção, é carregada lentamente por padrão, requer um Open ISession.
Se você tiver fechado sua sessão ou se tiver confirmado sua transação, poderá obter uma exceção de carregamento lento que não pode puxar esses objetos adicionais.
Você deve ter cuidado com o carregamento lento e com a quantidade de dados de que realmente precisa.
Você pode desligar o carregamento lento para uma associação inteira ou pode colocar lazy igual a false ou também pode especificar uma estratégia de busca.
Aqui está o Program.cs implementação de arquivo.
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;
}
}
}
Para entender isso, vamos executar o aplicativo e dar uma olhada no NHibernate Profiler.
Como você pode ver, temos o Select From Customer, dado um determinado ID de cliente e, em seguida, também temos outra tabela Select From Orders, quando ela realmente acessa a coleção desse cliente.
Portanto, temos 2 viagens de ida e volta para o banco de dados. Agora, às vezes, gostaríamos de otimizar isso. Para fazer isso, vamos para ocustomer.hbm.xml arquivo e adicione uma estratégia de busca e peça a ele para fazer uma busca de junção.
<?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>
Como você pode ver que não alteramos nenhum código em nosso aplicativo, apenas adicionamos uma estratégia de busca no customer.hbm.xml. Vamos executar este aplicativo novamente, ele ainda se comporta exatamente da mesma maneira. Vejamos o NHibernate Profiler.
Antes, o programa fazia duas idas e voltas ao banco de dados, agora ele só tem uma e isso porque está fazendo uma junção externa à esquerda aqui.
Podemos ver que ele está fazendo uma junção externa esquerda entre a tabela de clientes e a tabela de pedidos com base no ID do cliente e, portanto, é capaz de carregar todas essas informações de uma vez.
Salvamos 1 viagem de ida e volta para o banco de dados.
O lado negativo é que as informações do cliente serão duplicadas em ambas as linhas e é assim que funciona uma junção externa esquerda SQL.
Portanto, com a estratégia de busca, estamos recuperando um pouco mais de dados e salvando uma viagem de ida e volta.
Você também pode fazer isso no nível da consulta, então vamos para o Program.cs arquivo e veja o exemplo recarregado mais simples.
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();
}
Aqui, estamos fazendo uma carga pelo cliente. Agora vamos alterá-lo para uma consulta e usaremos uma consulta de link conforme mostrado no código a seguir.
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();
}
Vamos também remover a estratégia de busca do customer.hbm.xml Arquivo.
<?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>
Vamos executar este aplicativo novamente e você verá a seguinte saída.
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...
Agora, vamos dar uma olhada no NHibernate Profiler, você pode ver que temos essa busca de junção ansiosa acontecendo mais uma vez, mas desta vez, é baseado na consulta.