Lazy loading

In the beginning of the chapter we saw that when the Employee entity was loaded, NHibernate did not load the associated collection entities such as Benefits and Communities. That is default behavior of NHibernate. At first it may sound strange, but think what would happen if NHibernate tried loading Benefits and Communities along with the Employee entity. Each Community instance in the Communities collection has collection of the Employee instances associated with it. NHibernate will need to load all those Employee instances. Each of those Employee instances will have further Community instances to load. This cycle will only stop when the entire database is loaded into memory. To avoid situations like this, NHibernate only loads the entity that you asked for with all its primitive properties (or properties which are mapped to columns on the table of the entity) populated. Associated collection entities are loaded only when they are accessed. This feature is called lazy loading. NHibernate marks all the mapped entities as lazily loaded unless we override the default in the mapping configuration at each entity level. In mapping by code, we can include a call to method Lazy which takes a Boolean value to indicate whether the entity should be marked lazy or not.

Note

You can even have primitive properties of entity configured to be loaded only when they are accessed similar to collections. They are called lazy properties. I omitted the detail to simplify the discussion. We would look at lazy properties in the section ahead.

Internal mechanism of lazy loading for collection associations (one-to-many and many-to-many) are slightly different from that of single-ended association (many-to-one). Similarly, mechanism for lazy loading of properties is again different from that of associations. In this section, we would take a look at lazy loading in detail, understand how it works, and get acquainted with best practices around lazy loading.

Lazy collections

There is nothing you need to do to enable lazy behavior on collections. All collection mappings are lazy by default. With lazy loading enabled, when an entity is loaded, NHibernate sets all the collection properties on this entity to a special collection type implemented by NHibernate. This special collection type is capable of loading entities in the collection when the collection is accessed (on demand). Let's take an example and see how this works.

In the following test, we are loading the Employee entity having first name as John:

[Test]
public void QueryEmployeeByName()
{
  IList<Employee> employees = null;
  using (var transaction = Database.Session.BeginTransaction())
  {
    employees = Database.Session.CreateCriteria<Employee>()
                        .Add(Restrictions.Eq("Firstname", "John"))
                        .List<Employee>();

    transaction.Commit();

  }
Assert.That(employees.Count, Is.EqualTo(1));
}

If you look at the SQL generated for the preceding code then you would notice that NHibernate has not generated any SQL to load the Benefits and Communities collection. As shown in the following image, on inspection of the loaded employees collection, we can see that the Benefits and Communities collection on these entities is set to a special type NHibernate.Collection.Generic.PersistentGenericSet<T>:

Lazy collections

PersistentGenericSet<T> is a special type that NHibernate implements. This is commonly referred to as collection wrapper. Let's take a close look at definition of this type.

public class PersistentGenericSet<T> : AbstractPersistentCollection,
ISet<T>,

PersistentGenericSet<T> is NHibernate's persistence-aware implementation of ISet<T>. Whenever you access the collection property in your code, PersistentGenericSet<T> will issue required SQL statements to load the items in the collection. There are several implementations of collection wrappers in NHibernate. Depending on the type of the collection, NHibernate chooses the right collection wrapper. In our case, since the Communities collection property on the Employee class is of type ICollection<T> and ISet<T> inherits from ICollection<T>, NHibernate chooses to use PersistentGenericSet<T>. For other collection types, other implementations are available. As a user of NHibernate, you do not need to know what those types are. NHibernate manages this transparently for you.

Disabling laziness

Using lazy collections is recommended but if there is a need to disable laziness on a collection then you can do so in mapping declaration. Following code listing shows how to disable laziness on the Benefits collection of the Employee entity:

Set(e => e.Benefits,mapper =>
{
  //… other mappings…//
  mapper.Lazy(CollectionLazy.NoLazy);
  },
relation => relation.OneToMany(mapping => mapping.Class(typeof(Benefit))));

Preceding code is from mapping of the Employee entity. Mapping of other properties is skipped to keep the text brief. Remember that disabling laziness via mappings stops the lazy loading for that collection globally. As stated at the beginning of the section, lack of lazy loading can lead to entire table data being loaded into memory. In the worse case scenario, even entire database can be loaded into the memory. So make sure you understand nature of the data before you turn off lazy loading globally. NHibernate lets you disable lazy loading of collections for a particular query. This is called eager fetching. If there is a need to load a collection eagerly only for a particular data retrieval operation, then you can use eager fetching for that query without disabling lazy loading globally. We will look at eager fetching in the next section.

Note

Another option to disable laziness on collections is to disable laziness on the collection entity itself. But that would have a bigger impact as the entity is then always eager fetched and not just when fetched as part of a particular collection.

Different lazy behaviors

Did you notice that we used an enumeration named CollectionLazy to disable lazy loading in the previous code listing? If lazy loading has only two states – enabled and disabled – then why not use a Boolean to enables or disable it? That is because lazy loading has not two but three different settings to choose from. You have already looked at CollectionLazy.NoLazy which is used to disable lazy loading. Rest of the two settings are as follows:

  • Collection.Lazy: Enables lazy loading. This is default value when none is specified.
  • Collection.Extra: This option adds extra smartness to lazy loading in some cases. If the collection is an indexed collection, then marking it as extra lazy will let you load individual items in the collection as you access them, as opposed to all items being loaded when collection is accessed. For non-indexed collections, extra laziness will generate more optimized SQL statements when aggregate functions such as Count() are called on the collection. For instance, if code that is accessing the collection calls the Count() method on the collection, then NHibernate would issue SQL statement that returns the count of the items in collection instead of loading the collection and then counting the number of items in memory.

Single-ended lazy associations

Single-ended associations are the other end of collection type of association. In mapping, these are represented by many-to-one element. Similar to lazy collections, lazy behavior for these type of associations is enabled by default. But unlike lazy collections, lazy behavior setting takes different values indicated as follows:

  • NoLazy: Disable lazy behavior.
  • Proxy: Enable lazy behavior. Why such a strange name? This goes back to what happens during lazy loading of single-ended associations. If you remember, in case of lazy collections, NHibernate used special wrapper collection types which load the collection when accessed. It is possible to implement such wrapper collection types because the actual collection types you are allowed to use in your code are known to NHibernate. In case of single-ended associations, the type that is to be loaded lazily is not a .NET framework type but a type defined by developers as part of their domain model. NHibernate does not know of these types and hence cannot provide an implementation that can be substituted for these types dynamically at runtime. Instead, NHibernate uses proxies that it builds dynamically. Every entity that is mapped has a corresponding proxy class generated by NHibernate dynamically at the start of the application unless lazy loading is disabled during mapping. This proxy class is implemented by inheriting from the entity being mapped. Like collection wrapper, proxy classes are capable of lazily loading themselves. NHibernate uses these classes for lazy loading of single-ended associations and hence the value Proxy.
  • NoProxy: From lazy behavior point of view, this is exactly same as the previous Proxy value. Using value of Proxy may give you trouble if the entity being lazily loaded is at the root of an inheritance hierarchy. NoProxy solves this problem still maintaining the lazy behavior.

Let's look into an example to see how lazy loading for single-ended associations works. We will use the association from Benefit to Employee to explore this topic. Following image from Chapter 3, Let's Tell NHibernate About Our Database, summarizes this association briefly:

Single-ended lazy associations

Following code listing from the mapping of the Benefit entity shows how to explicitly enable lazy behavior on the Employee property of the Benefit class:

ManyToOne(a => a.Employee, mapper =>
{

  mapper.Column("Employee_Id");
  mapper.Class(typeof (Employee));
  mapper.Lazy(LazyRelation.Proxy);
});

In the preceding code, we have told NHibernate that Employee association on Benefit should be treated as lazy and in particular, Proxy type.

Tip

LazyRelation enumeration defines the other two lazy behaviors we discussed previously.

Next, we will write a test to load all the SeasonTicketLoan benefits having amount greater than 1000:

[Test]
public void LoadSeasonTicketLoanBenefitsHavingAmountGreaterThan1000()
{
  using (var transaction = Database.Session.BeginTransaction())
  {
    var seasonTicketLoans = Database.Session
                                   .QueryOver<SeasonTicketLoan>()
                                   .Where(s => s.Amount > 1000)
                                   .List<SeasonTicketLoan>();
    Assert.That(seasonTicketLoans.Count, Is.EqualTo(1));
    Assert.That(seasonTicketLoans[0].Employee, Is.Not.Null);

    transaction.Commit();
  }
}

If we inspect the actual value of the Employee property on the previous seasonTicketLoans[0] then we can see that it is of type EmployeeProxy, which is a type generated by NHibernate at runtime.

Single-ended lazy associations

Proxy versus NoProxy

To understand the difference between Proxy and NoProxy, we will have to assume that Employee has one more entity inheriting from it. Suppose we have FormerEmployee inheriting from Employee. If we also consider the NHibernate's dynamically generated EmployeeProxy type, then we get the following class diagram:

Proxy versus NoProxy

When a benefit entity is loaded, the Employee association is lazily loaded and set to an instance of type EmployeeProxy. NHibernate has not yet gone to database to check whether the actual entity is of the Employee type or the FormerEmployee type. If you are expecting an instance of FormerEmployee and downcast the loaded instance to that type, you would be greeted with an exception because the actual type held in there is EmployeeProxy.

This is where NoProxy plays its role. In the previous example, when the lazy setting was set to Proxy and the Benefit instances was loaded from database, its type was set to actual type – SeasonTicketLoan in this case. It was the Employee property that was set to proxy type EmployeeProxy. But when lazy setting on the Employee association is set to NoProxy, then NHibernate will load a proxy instance of the Benefit class first -, which is SeasonTicketLoanProxy. Now because the parent object is a proxy, NHibernate is capable of intercepting it. When you access the Employee property on this proxy, NHibernate will go to database and load the correct instance of Employee and return it.

Note

NHibernate lets you intercept different operations that it performs so that you can run your custom logic before NHibernate finishes its own operation. One such interception, called property load interception, will let you run your logic before/after value of a property is loaded from database. Implementation of NoProxy uses property load interception. What that means for you, the user of NHibernate is that the NoProxy option will work only if the association uses property. If your association uses field then NoProxy would not work.

Lazy properties

Besides lazy collections and lazy single-ended associations, NHibernate also supports lazy properties. One or more mapped properties of an entity can be declared lazy by using the syntax shown next:

Property(e => e.EmployeeNumber, mapper => { mapper.Lazy(true);});

In the preceding syntax, the EmployeeNumber property on the Employee class is mapped as lazy. Value of a lazy property is loaded from database when the property is accessed for the first time. If a class has more than one lazy property then all lazy properties are loaded at once when any of the lazy property is accessed.

Why use lazy loading?

In the introduction of lazy loading, we discussed how lazy loading can avoid whole databases being loaded into memory on retrieval of a single entity. Well, that is a great benefit of lazy loading but there is another more apparent benefit that lazy loading brings to the table.

Suppose you are working on a business requirement with makes an employee a member of a community. Assume that you load this employee by its ID. Ideally, you would only want to load the employee and its Communities collection. In absence of lazy loading, you would have to resort to writing a detailed query to load only the parts you need.

Now, suppose you have another requirement that involves loading employee by its ID and the Benefits collection on the employee instance. The query you used for the previous requirement cannot be used here because that query did not load the Benefits collection. You have two options now – one, write a new query to load employee and its Benefits collection and two, update the previous query so that it loads both the Communities and Benefits collection of employee instance. Neither of these solutions are ideal. The first solution is not scalable as you would end up writing similar queries with slight variations in details that they load. The second solution leads to unnecessary loading of collection items that may not be required.

Lazy loading handles such situations quite nicely. You write one query to retrieve employee by its ID and use it in both the requirements. When the code for the first requirement navigates to the Communities collection, it is loaded. When the code for the second requirement accesses the Benefits collection, it is loaded at that point. Neither do you have to write duplicate querying code nor load unnecessary collection items.

Lazy properties are slightly different. If an entity has lazy properties, then NHibernate ends up making two database trips, once to load the non-lazy properties of the entity and second time to load the lazy properties of the entity. This behavior may actually result in unnecessary round trips to database. So do not use lazy properties unless you are absolutely sure that you need lazy behavior. One situation where you might want lazy property is when the property holds large amount of data which is not needed to be fetched all the time along with other properties on the same entity. SQL types such as BLOB, CLOB, TEXT, and Binary are examples of such properties. Making such properties lazy, you can save large amount of data coming out of database every time the entity is loaded.

Lazy loading gotcha's

While lazy loading is a great feature, it is very easy to get into issues if lazy loading is used without care. There are some gotchas around lazy loading that we will look into briefly in this section. Knowing these will help you avoid problems and frustration while working with lazy loading.

Keeping the session open

Any database interaction that NHibernate does, requires an open session to communicate with database. With lazy loading, the associated entities are loaded at the point of them being accessed for the first time. For this to work, it is mandatory that the session in which the parent entity was loaded is still around in usable state. Lazy loading, specifically the proxies used by NHibernate, rely on the original session object in order to load the proxied entities. So make sure that you do not close the session object until the parent object is in use if you know that lazy loading will be triggered at some point later.

Chapter 8, Using NHibernate in Real-world Applications, we will look at some guidelines and recommended patterns that can help with this situation.

Being aware of the select N+1 problem

Use of lazy loading leads to a performance problem often called as select N+1 problem. This is not much of an issue if the lazy collections have small number of items in them. For collections having large number of items, select N+1 problem can result in unnecessary loading of entities and significant performance degradation. Choosing right fetching strategy or correct batching settings can help with addressing select N+1 issues to some extent. We will look at fetching strategies next and in the next chapter we are going to look at select N+1 problem in detail along with batching.

Using lazy loading together with the right fetching strategy

select N+1 problem can be addressed by fetching strategies, but their role is not limited to fixing select N+1 problem only. Lazy loading used in combination with right fetching strategy can offer the best optimization of your data access code. Always consider using lazy loading in unison with right fetching strategy. Again, in the next chapter, we will look at this aspect in little more detail.

Using automatic properties

Lazy properties only work if they are declared as automatic properties. If a property is declared using backing fields and you try to access the field directly, then you may get unexpected results.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.217.220