NHibernate Forge
The official new home for the NHibernate for .NET community

NHibernate Mapping - Concurrency

NHibernate has several concurrency models that you can use:

  • None
  • Optimistic
    • Dirty
    • All
  • Versioned
    • Numeric
    • Timestamp
    • DB timestamp
  • Pessimistic

We will explore each of those in turn.

None basically means that we fall back to the transaction semantics that we use in the database. The database may throw us out, but aside from that, we don’t really care much about things.

Optimistic is more interesting. It basically states that if we detect a change in the entity, we cannot update it. Let us see a simple example of using optimistic dirty checking for changed fields only:

<class name="Person"
			 optimistic-lock="dirty"
			 dynamic-update="true"
			 table="People">

Using this with this code:

using (var session = sessionFactory.OpenSession())
using (var tx = session.BeginTransaction())
{
	var person = session.Get<Person>(1);
	person.Name = "other";
	tx.Commit();
}

Will result in:

image

Note that we have so specify dynamic-update to true. This is required because doing so will generally cause much greater number of query plan to exist in the database cache.

Setting optimistic-lock to all would result in:

image

If the update fails because the row was updated, we will get a StaleObjectException. Like all exceptions, this will make the session ineligible for use, and you would have to create a new session to handle it.

Usually a better strategy is to use an explicit version column. We can do it by specifying <version/>:

<version name="Version" column="Version"/>

And that would result in:

image

As you can probably guess, if the version doesn’t match, we will get StaleObjectException.

Instead of using numeric values, we can use a timestamp:

<version name="Version" column="Version" type="timestamp"/>

In this case, the property type should be DateTime, and the resulting SQL would be:

image

This is, of course, a less safe way of doing things, and I recommend that you would use a numeric value instead.

Another option is to use the database facilities to handle that. in MS SQL Server, this is the TimeStamp column, which is a 8 byte binary that is changed any time that the row is updated.

We do this by changing the type of the Version property to byte array, and changing the mapping to:

<version name="Version"
				 generated="always"
				 unsaved-value="null"
				 type="BinaryBlob">
	<column name="Version"
					not-null="false"
					sql-type="timestamp"/>
</version>

Executing the code listed above will result in:

image

We use the value of the timestamp to ensure that we aren’t overwriting the row data after it was changed. The database will ensure that the row timestamp will change whenever the row itself is updated. This plays well with system where you may need to update the underlying tables outside of NHibernate.

Pessimistic concurrency is also expose with NHibernate, by using the overloads that takes a LockMode. This is done in a database independent way, using each database facilities and syntax.

For example, let us example the following code:

using (var session = sessionFactory.OpenSession())
using (var tx = session.BeginTransaction())
{
	var person = session.Get<Person>(1,LockMode.Upgrade);
	person.Name = "other";
	tx.Commit();
}

This will result in the following SQL:

image

We can also issue a separate command to the database to obtain a lock on the row representing the entity:

using (var session = sessionFactory.OpenSession())
using (var tx = session.BeginTransaction())
{
	var person = session.Get<Person>(1);
	session.Lock(person, LockMode.Upgrade);
	person.Name = "other";
	tx.Commit();
}

The Get() would generate a standard select, without the locks, but the Lock() method would generate the following SQL:

image

The behavior for conflict in this case is very simple, we wait. If we wait for too long, the timeout will expire and we will get a timeout exception, because we could not obtain the lock.

That is consistent with how we would use pessimistic concurrency elsewhere.


Posted abr 15 2009, 12:40 a.m. by Ayende
Filed under:

Comments

JuanCho wrote re: NHibernate Mapping - Concurrency
on 04-28-2009 17:21

Is this post specifically for NH v2.1? I get schema validation errors when I try to define a column element inside the version one:

<version name="Version" type="BinaryBlob">

<column name="Version"

                     sql-type="timestamp"/>

</version>

Ayende wrote re: NHibernate Mapping - Concurrency
on 04-29-2009 0:24

Juan,

Yes, this was done for NH 2.1

JuanCho wrote re: NHibernate Mapping - Concurrency
on 04-29-2009 11:51

Thanks, I can also confirm that after using NH 2.1, I was able to make it work with oracle via ORA_ROWSCN.

If anybody is heading this route (supported in oracle 9+) keep in mind the tables need to be created with ROWDEPENDENCIES to guarantee unique row version

With this, oracle will keep the version of the row, similar to the timestamp feature on sql server. You now, don't need to worry about manually incrementing the value of a custom version column anymore (via sequence, trigger, nhibernate, etc). It will be handled by oracle, enabling non-nhibernate processes to interact with the data and keeping the NH concurrency under control.

NH 2.1 specific:

   <version name="Version" type="Int32" generated="always" >      

   <column name="ORA_ROWSCN" sql-type="Int32" />

   </version>

NH 2.0 Specifc

<version name="Version" type="Int32" generated="always" column="ORA_ROWSCN"/>

You can find more details on this oracle feature at:

asktom.oracle.com/.../f

JuanCho wrote re: NHibernate Mapping - Concurrency
on 04-29-2009 16:55
bai wrote re: NHibernate Mapping - Concurrency
on 05-23-2009 16:09

Hi there, thanks for shareing.

I have a little problem getting dynamic-update="true" to work on my test project. It has no effect unless I also specify select-before-update="true"

Any ideas why ?

Ayende wrote re: NHibernate Mapping - Concurrency
on 05-23-2009 20:40

No, I don't.

Code + mapping would help, please post it to nh users

nh credit repair wrote nh credit repair
on 12-01-2009 23:13

Wow This place is great. I have to bookmark it and come back here again!

abhaymamid wrote re: NHibernate Mapping - Concurrency
on 02-23-2011 16:38

Hi There,

objNSession = NHibernateHelper.GetCurrentSession();

               objNSession.Lock(item, LockMode.None);

The second code line above throws an exception,

"cannot lock an unsaved transient instance: Boeing.IRSP.BL.BusinessLogic.Admin.DL"

Can you please help me. It's burning.

virusswb wrote re: NHibernate Mapping - Concurrency
on 05-30-2013 23:03

the version node in nh3.3 is not enable, which node do we selected?

thank you.

virusswb wrote re: NHibernate Mapping - Concurrency
on 05-31-2013 0:01

i find the reason.

the version tag must keep up with id tag.

thank you.

Powered by Community Server (Commercial Edition), by Telligent Systems