Thursday, 17 June 2010

Learn NHibernate Lesson 4 - Transactions and Concurrency

disclaimer - at the moment, this post is in note format from a workshop I did, I need to it clean up, and add grammar

4. Transactions and Concurrency

nhiberate provides its own ITransaction abstraction

the goal of nhibe is to abstract the choice of databse engine for you. so you dont need to worry about how the db imlements transactions, nhibe will do it for you.
Transaction Pattern

ask the ISession to begin a transaction
do some work
commit or rollback the transaction[csharp]

ITransaction tx = session.BeginTransaction();
//do some work
tx.Commit();

// or tx.Rollback();

[/csharp]


real world is a bit more complex[csharp]

using(ITransaction tx = session.BeginTransaction())
{
try {
//do some work
tx.Commit();
}
catch(NHibernateException) {
tx.Rollback();
throw;
}
}

[/csharp]


when shold you use transactions? Aways! - even on reads (queries)

you have no idea why a db operation may have failed, so instead or worrying, just implement transactions..

before we start we need a refactor of our dataaccess layer. we have a private global session
this is not a great design strategy
the session is not made to be hung on to for a long time
it is non thread safe use once and throw away

the session factory however is much stronger use once
so.. we change the private ISession to an ISessionFactory[csharp]

public class DataAccessProvider
{
private ISessionFactory sessionFactory;

//we now call the getsession in the constructor to the class..

public DataAccessProvider ()
{
sessionFactory = GetSessionFactory();
}

private static ISession GetSession()
{
return sessionFactory.OpenSession();
}

private static ISession GetSessionFactory()
{
return (new Configuration()).Configure().BuildSessionFactory();
}

[/csharp]


now our methods become like:[csharp]

public void AddCustomer(Customer customer)
{
ISession session = GetSession();
session.Save(customer); //save will return an object that represents the primary key of that object in the database
session.Flush();
}

[/csharp]


now each method will use its own copy of the session (not perfect, we will move on again later in the lessons..[csharp]

[test]
[ExpectedException(typeof(NHibernate.HibernateException))]
public void DeleteCustomerCanThrowExceptionOnFail()
{
Customer c = GetFirstCustomerWithOrders();
provider.DeleteCustomer(c);
}

[/csharp]


now, how can we refactor the delete customer method to take advantage of transactions..[csharp]

public void DeleteCustomer(Customer c)
{
using(ISession session = GetSession()) {
using(ITransaction tx = session.BeginTransaction()) {
try {
session.Delete(c);
session.Flush();
tx.Commit();
}
catch(NHibernateException) {
tx.Rollback();
throw;
}
}
}
}

[/csharp]

Concurrency

what does it mean? 2 or more users may be editing hte same data at the same time..

this especially happens in a disconnected-data world

types of concurrency:
Pessimistic concurrency

something in the overall system is responsible for actively blocking operations that could result in data-synchronisation conflicts

Pessimistic (assume something is going to go badly and actively block it from happening)

usually accomplished by exclusive locks approach - Expensive!
Optimsitic Concurrency

assumes no operations that could result in data-synchronisation conflicts will occur and is reposinsible for reacting properly when they do

similar to edit, merge, comit with todays source control, unlike old source control with exclusive locks..
NHibernate Optimistic Concurrency

several approaches
in the approriate hbm.xml mapping file, add a <version> element (implementation of row versioning)
or a <timestamp> element (implementation of timestamp tracking)
and several others..

in general, row versioning is preferred

the version tag must precede any property tags in the mapping file

assuming we have added an integer proeprty called version to customer class[xml]

<version name="Version" column="Version" type="integer" unsaved-value="0" />

[/xml]


now in the database, when you add the version column, no nulls, default value is 1