Ayende @ Rahien

It's a girl

NHibernate Mapping -

I am going to post a few things about NHibernate, going in depth into seemingly understood mapping. We will start with the most basic of them all: <property/>

<property
        name="propertyName"                 (1)
        column="column_name"                (2)
        type="typename"                     (3)
        update="true|false"                 (4)
        insert="true|false"                 (4)
        formula="arbitrary SQL expression"  (5)
        access="field|property|ClassName"   (6)
        optimistic-lock="true|false"        (7)
        generated="never|insert|always"     (8)
/>

1) is pretty obvious, it is the name of the property on the persistent class.

2) should be obvious as well, this is the column name in the database, which by default is the name of the property. This allows us to map a property to a column, and adds a small optimization if you have one to one mapping.

3) type is interesting. This is the CLR type of the property that we map, but it can also be used to customize the way that NHibernate works with our data types by specifying a custom IUserType.

4.1) should NHibernate update this property in the database when updating the object? Let us look at an example:

<property name="Title" update="false"/>

Given this mapping, and the following code:

using (var session = sessionFactory.OpenSession())
using (var tx = session.BeginTransaction())
{
	var blog = session.Get<Blog>(6);
	blog.Title = "changed";

	tx.Commit();
}

NHibernate will not try to update the row:

image

Note that we have no update here, even though we updated the actual property value, and usually NHibernate will save that value.

4.2) insert behaves in much the same way, disabling inserts for a property. For example:

<property name="AllowsComments" insert="false"/>

And this code:

object id;
using (var session = sessionFactory.OpenSession())
using (var tx = session.BeginTransaction())
{
	id = session.Save(new Blog
	{
		AllowsComments = true,
		CreatedAt = DateTime.Now,
		Subtitle = "test",
		Title = "test"
	});

	tx.Commit();
}

Produces:

image

Note that we don't insert the AllowComments column. And if we try to update this entity:

using (var session = sessionFactory.OpenSession())
using (var tx = session.BeginTransaction())
{
	var blog = session.Get<Blog>(id);
	blog.AllowsComments = false;
	blog.Title = "blah";

	tx.Commit();
}

We would get...

image

An update of AllowComments, but not of Title.

5) formula is a way to specify any arbitrary SQL that we want to associate with a property. Obviously, this is a read only value, and it is something that we would use on fairly rare occasions. Nevertheless, it can be pretty useful at times. Let us take a look at the mapping:

<property name="CountOfPosts"
	formula="(select count(*) from Posts where Posts.Id = Id)"/>

And selecting an entity will now result in:

image

Note that the formula was slightly preprocessed in order to make it work as a subquery.

6) access determines how we are going to actually set and get the actual value with NHibernate. We aren't limited to a simple public property, in fact, we can use: private variables, private auto property variable, custom implementation, field, and many more. This isn't actually very interesting at the moment to me, so I am just going to mention it and move on.

7) optimistic-lock is pretty complex, I am afraid. Mostly because it is a way to interact with the <version/> option of NHibernate. NHibernate has intrinsic support for optimistic concurrency, but sometimes there are reasons that you don't want to change the value of the version of the entity if a particular value changed. This is the role that optimistic-lock plays.

It will probably be better when we see the code. Let us take the following entity definition:

<class name="Blog"
	   table="Blogs">
  
	<id name="Id">
		<generator class="identity"/>
	</id>
	<version name="Version"/>
	<property name="Title" update="false"/>
	<property name="Subtitle"/>
	<property name="AllowsComments" insert="false"/>
	<property name="CreatedAt" />
	<property name="CountOfPosts"
		formula="(select count(*) from Posts where Posts.Id = Id)"/>
</class>

And now execute the following code:

using (var session = sessionFactory.OpenSession())
using (var tx = session.BeginTransaction())
{
	var blog = session.Get<Blog>(1);
	blog.Subtitle = "new value 6";
	tx.Commit();
}

The SQL that is going to be executed is:

image

Note that we increment the value of the version column. But, if we specify optimistic-lock="false"...

<property name="Subtitle"
	optimistic-lock="false"/>

We will get:

image

Note that in this case, we do not increase the value of the version column.

8) generated is an instruction to NHibernate that the value of this property is set by the database, usually using a default value (in which case you'll use "insert") or a trigger (in which case you'll use "always").

When we use it like this:

<property name="AllowsComments" generated="insert"/>

And execute the following code:

using (var session = sessionFactory.OpenSession())
using (var tx = session.BeginTransaction())
{
	session.Save(new Blog
	{
		CreatedAt = DateTime.Now,
		Title = "hello",
		Subtitle = "world",
	});
	tx.Commit();
}

We will get an insert followed immediately by a select:

image \

And the select is:

image

So we have to get it back from the database before we can actually make any sort of use of it.

And that was my in depth tour into <property/>, more will probably follow...

Comments

labilbe
04/07/2009 05:26 PM by
labilbe

Thank you for this useful short tutorial!

Jason
04/07/2009 06:09 PM by
Jason

learn something new every day!

Frank Quednau
04/07/2009 07:21 PM by
Frank Quednau

Well done, thanks! A minor question, are the version + optimistic locking semantics the same as with timestamp + optimistic locking? (legacy, i have no version, but a timestamp...)

Sven
04/07/2009 07:34 PM by
Sven

Every time you open your (blog-)mouth about NHibernate I learn something new. Thank you !

BTW, in case you're running out of ideas with regard to NHibernate topics, I am especially keen on guidance with regard to eager fetching deep hierarchies (e.g. 4+ levels) for which NHibernate's behaviour (left join fetch or ICriteria's FetchMode.Eager) kills performance.

BTW2: big fan of NHibernate Profiler (yes we bought it :-) )

Ayende Rahien
04/07/2009 09:06 PM by
Ayende Rahien

Sven,

For that you usually need to use multi query instead

Diego Jancic
04/08/2009 04:08 AM by
Diego Jancic

Really cool guide! Hope you keep writing about it.

Althought, I think it would be much better to have it written in the official documentation ( nhforge.org/.../index.html)

Oll
04/08/2009 12:20 PM by
Oll

more will probably follow...

This is one of the most useful posts I've found for someone starting out with NHibernate. Please don't stop now!

Shawn
04/08/2009 01:01 PM by
Shawn

Thanks for putting this up, it is nice to see it in such detail.

G
04/08/2009 02:08 PM by
G

Thanks alot!

Scott Muc
04/08/2009 03:51 PM by
Scott Muc

I love short tutorials like this, thanks for the info!

I have a question about the formula attribute. I've run into the scenario where I will have a list of objects that have a child collection of related objects (eg BlogPost ---> Comments). Is that subquery going to pose a problem if I have a page that lists a large collection of the parent objects and displays that counted property?

For a lot of my entities I pre-compute the child collection count and store it in the database because I did not want to perform a join, or invoke lazy loading to obtain the child collect to retrieve its count.

I hope that makes sense to you. The solution might seem obvious to you, but I'm still a newb at NHibernate.

Thanks a lot for all your great work!

Mahendra Mavani
04/08/2009 04:09 PM by
Mahendra Mavani

Frank

little amendment to Ayende's answer for your query about timestamp vs. version

  • Yes they serve the same purpose and almost same.

Only difference would be in very very high transactional app you might have rare case of two update happening at same millisecond. Depending upon your app's tolerance power this may or may not be issue. This is the reason why new recommendation is to go for incremental number i.e. version which will never fail even in above rare rare case

Ayende Rahien
04/08/2009 04:18 PM by
Ayende Rahien

Scott,

That depends on a lot of things, the size of the data, the efficiency of the subquery, what indexes you have, etc.

Take the SQL to your DBA, if he screams, it is probably going to be slow.

Scott Muc
04/08/2009 04:30 PM by
Scott Muc

Thanks Ayende,

We don't have a dba... I'm the go to guy for all database stuff. In my opinion I would favour this computed count column since it would remove a lot of logic from the codebase and our database is small enough that the subquery wouldn't be much of a performance hit.

Working on some entities right now that can take advantage of this right now. Looking forward to seeing how it turns out.

Scott

Hendry Luk
04/14/2009 02:43 AM by
Hendry Luk

Mapping to private auto property variable? Really? Why do we ever want to do that?

Comments have been closed on this topic.