A natural id is a way to refer to a unique field of an object as a substitute of the real entity identifier. A good (and typical) example of that would be with the User entity. We have the user name and the user id, both are unique, but the user id is usually something that is generated by our application and has no relation to the a human being. In other words, user #123814 doesn’t mean anything to me, while user ‘ayende’ has a meaning to us.
There are many reasons for choosing this approach, but the most common one is that we want a small primary key, since it is duplicated to all related tables (especially important for things like user, which is associated with just about everything).
So far, it doesn’t seem really interesting, right? Why am I boring you with talking about this?
Well, it is all about the second level cache and optimizing access to it. Let us take a look at how we set it up:
<class name="User"
table="Users">
<cache usage="nonstrict-read-write"/>
<id name="Id">
<generator class="identity"/>
</id>
<natural-id mutable="false">
<property name="Username"/>
</natural-id>
</class>
And now we are going to query it:
using (var s = sf.OpenSession())
using (var tx = s.BeginTransaction())
{
s.CreateCriteria<User>()
.Add(Restrictions.NaturalId()
.Set("Username", "Ayende"))
.SetCacheable(true)
.UniqueResult();
tx.Commit();
}
<natrual-id/> is used almost exclusively for documentation purposes. That means that internally the query above will be translated to:
using (var s = sf.OpenSession())
using (var tx = s.BeginTransaction())
{
s.CreateCriteria<User>()
.Add(Restrictions.Eq("Username", "Ayende"))
.SetCacheable(true)
.UniqueResult();
tx.Commit();
}
Well, almost. There is one subtle different between the two. When querying on <natural-id/>, we also bypass the usual expiration check in the second level cache implemented by NHibernate.
That means that even if we changed the underlying table, NHibernate will not automatically expire the results in the cache for this query, it will still consider it valid, because it assumes that like primary keys, there isn’t really a reason to perform an expiration check on natural ids.