Feb 27

More Configuring NHibernate Caches

Tag: NHibernate,ORMSymon Rottem @ 6:52 am

One of my readers recently asked to see a sample of configuring NHibernate caching through the config file after reading my previous post Configuring NHibernate Caches, so here goes. Please bear with me in case there are any typos as this has been rolled by hand.

In this particular example I’m configuring NHibernate to use SysCache for the second level cache and have done it all in the app.config (or web.config if it’s a web application). Note that this is not a fully complete config file but deals with the parts necessary for this example.


<configuration>

<configsections>
<section name="hibernate-configuration" type="NHibernate.Cfg.ConfigurationSectionHandler, NHibernate"/>
<section name="syscache" type="NHibernate.Caches.SysCache.SysCacheSectionHandler,NHibernate.Caches.SysCache"/>
</configsections>

<!--
NHibernate specific configuration.
-->
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">

<session-factory>

<property name="hibernate.connection.connection_string">Your Connection String</property>
<property name="hibernate.connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<property name="hibernate.dialect">NHibernate.Dialect.MsSql2000Dialect</property>
<property name="hibernate.connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
<property name="hibernate.connection.isolation">ReadCommitted</property>
<property name="hibernate.cache.provider_class">NHibernate.Caches.SysCache.SysCacheProvider, NHibernate.Caches.SysCache</property>
<property name="hibernate.cache.use_query_cache">true</property>

<!--
Sets up class and configuration caching for domain classes.
These cache definitions can be tweaked to change how each class is cached.
-->
<class-cache class="Core.User, Core" region="User" usage="read-write"/>
<collection-cache collection="Core.User.Roles" region="User.Roles" usage="read-write"/>

<class-cache class="Core.Role, Core" region="Role" usage="read-write"/>

</session-factory>

</hibernate-configuration>

<!--
Defines Syscache specific configuration
-->
<syscache>

<!-- Class cache regions -->
<cache region="User" expiration="60" priority="3"/>
<cache region="User.Roles" expiration="60" priority="3"/>
<cache region="Role" expiration="60" priority="3"/>

</syscache>

</configuration>

To explain this in a little more detail, the first section deals with telling the application about the configuration sections that will be included in the config file and what handlers to use to interpret them. This is really just standard .NET confg stuff:


<configsections>
<section name="hibernate-configuration" type="NHibernate.Cfg.ConfigurationSectionHandler, NHibernate"/>
<section name="syscache" type="NHibernate.Caches.SysCache.SysCacheSectionHandler,NHibernate.Caches.SysCache"/>
</configsections>

Then we deal with the main NHibernate configuration inside the hibernate-configuration section specified in the configsections:


<property name="hibernate.connection.connection_string">Your Connection String</property>
<property name="hibernate.connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<property name="hibernate.dialect">NHibernate.Dialect.MsSql2000Dialect</property>
<property name="hibernate.connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
<property name="hibernate.connection.isolation">ReadCommitted</property>
<property name="hibernate.cache.provider_class">NHibernate.Caches.SysCache.SysCacheProvider, NHibernate.Caches.SysCache</property>
<property name="hibernate.cache.use_query_cache">true</property>

The key things to note here are the hibernate.cache.provider_class and hibernate.cache.use_query_cache properties which are indicating that we should be using the SysCacheProvider for caching and that the query cache should be enabled.

It’s important to note that just adding the hibernate.cache.provider_class property and configuring some of your classes will cause those classes to be put into the second level class cache so that when you attempt to load them using ISession.Get or ISession.Load the the database will only be hit if the matching entity is not found in the cache.

What it will not do, however, is provide any optimisation when you attempt to load entities using an HQL or ICriteria query. Queries will still go to the database to get their results unless, of course, you add the hibernate.cache.use_query_cache property to your configuration and explicitly specify that a query should be cachable using the IQuery.SetCachable(true) method – this must be done explicitly for each query you want to be cached.

When a query is cached the entity IDs in the results of a query will be stored along with the query itself in the second level query cache so that if it’s executed again it will take the list of IDs from the cache and will then re-hydrate the entities from the class cache. Because of this the query cache is useless without the class cache.

The next section deals with configuring the how each class should be cached. This is still part of the hibernate config section:


<!--
Sets up class and configuration caching for domain classes.
These cache definitions can be tweaked to change how each class is cached.
-->
<class-cache class="Core.User, Core" region="User" usage="read-write"/>
<collection-cache collection="Core.User.Roles" region="User.Roles" usage="read-write"/>

<class-cache class="Core.Role, Core" region="Role" usage="read-only"/>

Essentially there are two separate caching definitions to work with; the class-cache element, which deals with how a specific entity type should be cached and the collection-cache element that specifies how an entity’s dependent collection should be cached.

In this example I have elected to cache my User and Role entities as well as the Roles collection on the User entity.

Note that for each mapping you can specify a cache region, which provides additional granularity regarding how specific types should be expired from the cache and a usage that can provide some additional optimization based on whether or not the entities will ever be updated or new entities created by the application.

The final section of my example deals specifically with configuring the SysCache caching provider:

<!--
Defines Syscache specific configuration
-->
<syscache>

<!-- Class cache regions -->
<cache region="User" expiration="60" priority="3"/>
<cache region="User.Roles" expiration="60" priority="3"/>
<cache region="Role" expiration="86400" priority="3"/>

</syscache>

Here you can define the cache regions used in the class-cache and collection-cache mappings above as well as any cache regions you decide to use in queries inside your application. Each cache region describes how long items in that region should stay in the cache before being expired so they will be reloaded again from the database and a priority that indicates which classes should be expired from memory first if the available memory is getting too low.

You can, of course, dump everything into a single cache region, but then all entities will be treated equally in the cache. In my case I want the User entities and their Roles collections to be cached for one minute as this information could be updated externally and I want to have the changes reflected in the application fairly quickly. The Role entities, however, will hardly ever change so I’ve chosen to cache them for a whole day before they should be re-obtained from the database.

Hopefully that clears things up a bit. If there are any questions or comments please feel free to post one – don’t be shy!

Related Posts