Sunday, February 26, 2012

NHibernate and Caching - Part - 1

NHibernate has built in support for caching.  It might sound like a very simple feature to implement but, in reality, its one of the most complex piece.   Using various Caching strategies provided by NHibernate we can achieve extremely good performance improvements.

NHibernate currently provides two separate caching mechanism.  First Level Cache and Second Level Cache.  In this post we will talk about First Level Cache only.  In the next post we will see the Second Level Cache in more details.

First Level Cache

This cache is implemented using the NHibernate Session.  Each instance of NHibernate Session acts as a cache.  NHibernate keeps all objects loaded using a specific instance of Session, in the cache. This cache is lost as soon as the session is disposed.

This cache is enabled by default and nothing special has to be done to work with this cache.  Lets look at the performance improvements that we get because of the first level cache.

For the sake of this post lets consider a very simple data model.  Let's say that we want to model Employees and Departments tables using NHibernate.  One department can have many employees and one employee belongs to one and only one department.

We will be using NHibernate Fluent API to map the NHibernate model classes.  The POCO classes for Employee and Department would look like
Mapping files for these classes are also very stock standard. The data in the departments table looks like:
Department Data shows only one record
We have inserted one department called Operations.

The data in the employees table looks like:

Employee SpiderMan belongs to Department Operations
There is just one employee, SpiderMan who belongs to the Operations department.

Enough setup, Lets write a test to see first level cache in action.

To see the first level cache in action lets write a NUnit test.

Case - 1 - Multiple queries will not be fired if entity is queried using the identifier column

In this unit test lets see what happens when we retrieve an Employee with ID = 1 twice using the same session instance. Before we begin lets setup the NHibernateSessionHelper that will build the SessionFactory for us
The static class NHibernateSessionHelper builds the NHibernate Session Factory. Notice that we have configured NHibernate in such a way that it will display all the SQL's that it executes (using the ShowSql() method). Lets look at the actual test code.
As you can see, we are opening up a new NHibernate Session and fetching the Employee with ID = 1 twice. Notice that we are fetching the Employee with ID = 1 twice but using the same session.

What do you think, how many queries will be fired? 

Lets look at the NUnit output to see how many queries are fired.
NHibernate fires only one query.

Why?

This is the first example of how NHibernate makes use of the first level cache.
After the above line of code is executed, NHibernate has already cached the Employee with ID = 1 in its first level cache. Hence, when the second statement executes, NHibernate first looks up the entity Employee with ID = 1 in the first level cache, it finds it, now it knows that, there is no need to fire another query to retrieve the same employee object all over again.

This is a very important optimization. In a complex flow of events, if you happen to load the same entity, using its primary key more than once in the same session then, NHibernate would not fire multiple queries!

The last statement in the test is also an important point, it asserts that the employee reference returned by query 1, is the same reference that is returned by query 2. Another important point, in a given NHibernate session there can be one and only one reference of a given entity with a given identifier!

Case - 2 - Multiple queries will not be fired if, entity with a given identifier is already loaded in the session

Lets consider this test:
In this test we are first fetching the Department with ID = 1, then asserting that the employee count of this department = 1. We are then fetching the employee with ID = 1 explicitly.

What do you think how many queries will be fired?
Yes, only two queries are fired.
  • First query was fired to get the department with ID = 1
  • Second query was fired to get all the employees belonging to the department with ID = 1 (since the employees of department are Lazy loaded). 
Question: Where is the third query, i.e. the query to load the employee with ID = 1?  Why was it not fired?

Again, we can see the First level cache in action.  The department with ID = 1 has one employee whose ID = 1.  This employee got loaded when the second query was fired to load all the employees belonging to department with ID = 1.

When we fire the query to fetch the employee with ID = 1, NHibernate first looks into its first level cache to see if the entity is already loaded, and guess what, it finds that it was already loaded and there is no need to fire another query to load the same employee all over again!


NHibernate first level cache is a life savior, but it works only when we deal with the same NHibernate session instance.  It does not work across multiple NHibernate sessions.  Is there something that works across NHibernate Session's? 

Yes, the Second Level Cache.  We will talk about the Second level cache in the next post.  Stay tuned guys!

No comments:

Post a Comment

Have some Fun!