nHibernate transactions and rollbacks
Introduction
The same rollback considerations that apply to normal SQL transactions also apply to nHibernate transactions.
Rollback Scenario
Suppose you have two SQL Statements – the first one tries to insert (and runs into a primary key violation). The second one tries to update the same record as the first one.
SInce the first one failed, the second one should not proceed with its update. However, if you have the two statements within a transaction, the second one will proceed even though the first one has failed. This is because, on failure of the first statement, no one ENDED the transaction.The transaction still proceeds and executes the second statement.
Here is a code example – sql1 INSERTS a new author (and runs into a constraint), sql2 tries to update the same row:
1: using (var transaction = session.BeginTransaction(System.Data.IsolationLevel.ReadCommitted))
2: {
3:
4: var sql1 = "INSERT INTO [authors]([au_id], [au_lname], [au_fname]) VALUES (1, 'Gates', 'Bill');"
5: session.CreateSQLQuery(sql1);
6:
7: var sql2 = "UPDATE authors SET au_fname = 'anuj' WHERE au_id = 1"
8: session.CreateSQLQuery(sql2);
9:
10: transaction.commit();
11: }
The workaround
In case of an exception (caught within a try-catch), rollback the transaction.
1: try{
2: using (var transaction = session.BeginTransaction(System.Data.IsolationLevel.ReadCommitted))
3: {
4:
5: var sql1 = "INSERT INTO [authors]([au_id], [au_lname], [au_fname]) VALUES (1, 'Gates', 'Bill');"
6: session.CreateSQLQuery(sql1);
7:
8: var sql2 = "UPDATE authors SET au_fname = 'anuj' WHERE au_id = 1"
9: session.CreateSQLQuery(sql2);
10:
11: transaction.commit();
12: } // close using
13: } // close try
14: catch(Exception e)
15: {
16: transaction.Rollback();
17: session.Clear();
18: }
1: try{
2: using (var transaction = session.BeginTransaction(System.Data.IsolationLevel.ReadCommitted))
3: {
4:
5: var sql1 = "INSERT INTO [authors]([au_id], [au_lname], [au_fname]) VALUES (1, 'Gates', 'Bill');"
6: session.CreateSQLQuery(sql1);
7:
8: var sql2 = "UPDATE authors SET au_fname = 'anuj' WHERE au_id = 1"
9: session.CreateSQLQuery(sql2);
10:
11: transaction.commit();
12: } // close using
13: } // close try
14: catch(Exception e)
15: {
16: transaction.Rollback();
17: session.Clear();
18: }
Recommended Pattern for nHibernate Transactions
As per nHibernate’s documentation (and Hibernating Rhino’s), as long as you stick to the usings as shown below, the cleanup will automatically happen during the dispose() (for the transaction and the session). The rollbacks will occur as part of the cleanup – so there isn’t a reason for explicitly rolling back – as long as you use the pattern below.
1: using(var session = sessionFactory.OpenSession())
2: using(var tx = session.BeginTransaction())
3: {
4: // execute code that uses the session
5: tx.Commit();
6: }
Summary
nHibernate transactions should ideally be surrounded by a try-catch statement – and rolled back in case of exception. In addition, the transaction should always be started with a using( statement – to ensure correct disposal.
Leave a Reply