NHibernate – The difference between Get, Load and querying by id

time to read 3 min | 450 words

One of the more common mistakes that I see people doing with NHibernate is related to how they are loading entities by the primary key. This is because there are important differences between the three options.

The most common mistake that I see is using a query to load by id. in particular when using Linq for NHibernate.

var customer = (
	select customer from s.Linq<Customer>()
	where customer.Id = customerId
	select customer
	).FirstOrDefault();

Every time that I see something like that, I wince a little inside. The reason for that is quite simple. This is doing a query by primary key. The key word here is a query.

This means that we have to hit the database in order to get a result for this query. Unless you are using the query cache (which by default you won’t), this force a query on the database, bypassing both the first level identity map and the second level cache.

Get  and Load are here for a reason, they provide a way to get an entity by primary key. That is important for several aspects, most importantly, it means that NHibernate can apply quite a few optimizations for this process.

But there is another side to that, there is a significant (and subtle) difference between Get and Load.

Load will never return null. It will always return an entity or throw an exception. Because that is the contract that we have we it, it is permissible for Load to not hit the database when you call it, it is free to return a proxy instead.

Why is this useful? Well, if you know that the value exist in the database, and you don’t want to pay the extra select to have that, but you want to get that value so we can add that reference to an object, you can use Load to do so:

s.Save(
	new Order
	{
		Amount = amount,
		customer = s.Load<Customer>(1)
	}
);

The code above will not result in a select to the database, but when we commit the transaction, we will set the CustomerID column to 1. This is how NHibernate maintain the OO facade when giving you the same optimization benefits of working directly with the low level API.

Get, however, is different. Get will return null if the object does not exist. Since this is its contract, it must return either the entity or null, so it cannot give you a proxy if the entity is not known to exist. Get will usually result in a select against the database, but it will check the session cache and the 2nd level cache first to get the values first.

So, next time that you need to get some entity by its primary key, just remember the differences…