From: <aye...@us...> - 2009-05-22 22:17:25
|
Revision: 4361 http://nhibernate.svn.sourceforge.net/nhibernate/?rev=4361&view=rev Author: ayenderahien Date: 2009-05-22 22:17:15 +0000 (Fri, 22 May 2009) Log Message: ----------- Second part of transaction factory work, all DTC work is now isolated to the transaction factories. Isolation is now properly handled using the Isolater which delegates to the TransactionFactory to ensure running in a different transaction. Modified Paths: -------------- trunk/nhibernate/src/NHibernate/Engine/Transaction/IIsolatedWork.cs trunk/nhibernate/src/NHibernate/Engine/Transaction/Isolater.cs trunk/nhibernate/src/NHibernate/Engine/TransactionHelper.cs trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Exec/AbstractStatementExecutor.cs trunk/nhibernate/src/NHibernate/Id/Enhanced/TableStructure.cs trunk/nhibernate/src/NHibernate/Id/TableGenerator.cs trunk/nhibernate/src/NHibernate/Transaction/AdoNetTransactionFactory.cs trunk/nhibernate/src/NHibernate/Transaction/AdoNetWithDistrubtedTransactionFactory.cs trunk/nhibernate/src/NHibernate/Transaction/ITransactionFactory.cs trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH1054/DummyTransactionFactory.cs Modified: trunk/nhibernate/src/NHibernate/Engine/Transaction/IIsolatedWork.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Engine/Transaction/IIsolatedWork.cs 2009-05-22 20:42:29 UTC (rev 4360) +++ trunk/nhibernate/src/NHibernate/Engine/Transaction/IIsolatedWork.cs 2009-05-22 22:17:15 UTC (rev 4361) @@ -13,7 +13,7 @@ /// Perform the actual work to be done. /// </summary> /// <param name="connection">The ADP connection to use.</param> - void DoWork(IDbConnection connection); + void DoWork(IDbConnection connection, IDbTransaction transaction); // 2009-05-04 Another time we need a TransactionManager to manage isolated // work for a given connection. Modified: trunk/nhibernate/src/NHibernate/Engine/Transaction/Isolater.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Engine/Transaction/Isolater.cs 2009-05-22 20:42:29 UTC (rev 4360) +++ trunk/nhibernate/src/NHibernate/Engine/Transaction/Isolater.cs 2009-05-22 22:17:15 UTC (rev 4361) @@ -2,6 +2,7 @@ using System.Data; using System.Data.Common; using log4net; +using NHibernate.Dialect; using NHibernate.Exceptions; namespace NHibernate.Engine.Transaction @@ -29,18 +30,6 @@ { private static readonly ILog log = LogManager.GetLogger(typeof(Isolater)); - private static DoWork GetApropieateDelegate() - { - bool isAmbientTransation = System.Transactions.Transaction.Current != null; - if (isAmbientTransation) - { - return AmbientDelegateWork; - } - else - { - return AdoDelegateWork; - } - } /// <summary> /// Ensures that all processing actually performed by the given work will /// occur on a seperate transaction. @@ -49,8 +38,7 @@ /// <param name="session">The session from which this request is originating. </param> public static void DoIsolatedWork(IIsolatedWork work, ISessionImplementor session) { - DoWork worker = GetApropieateDelegate(); - worker(session, work, true); + session.Factory.TransactionFactory.ExecuteWorkInIsolation(session, work, true); } /// <summary> @@ -61,90 +49,7 @@ /// <param name="session">The session from which this request is originating. </param> public static void DoNonTransactedWork(IIsolatedWork work, ISessionImplementor session) { - DoWork worker = GetApropieateDelegate(); - worker(session, work, false); + session.Factory.TransactionFactory.ExecuteWorkInIsolation(session, work, false); } - - private delegate void DoWork(ISessionImplementor session, IIsolatedWork work, bool transacted); - - private static void AdoDelegateWork(ISessionImplementor session, IIsolatedWork work, bool transacted) - { - IDbConnection connection = null; - IDbTransaction trans = null; - // bool wasAutoCommit = false; - try - { - connection = session.Factory.ConnectionProvider.GetConnection(); - - if (transacted) - { - trans = connection.BeginTransaction(); - // TODO NH: a way to read the autocommit state is needed - //if (TransactionManager.GetAutoCommit(connection)) - //{ - // wasAutoCommit = true; - // TransactionManager.SetAutoCommit(connection, false); - //} - } - - work.DoWork(connection); - - if (transacted) - { - trans.Commit(); - //TransactionManager.Commit(connection); - } - } - catch (Exception t) - { - try - { - if (transacted && connection != null && !(connection.State == ConnectionState.Closed)) - { - trans.Rollback(); - // TransactionManager.RollBack(connection); - } - } - catch (Exception ignore) - { - log.Debug("unable to release connection on exception [" + ignore + "]"); - } - - if (t is HibernateException) - { - throw; - } - else if (t is DbException) - { - throw ADOExceptionHelper.Convert(session.Factory.SQLExceptionConverter, t, "error performing isolated work"); - } - else - { - throw new HibernateException("error performing isolated work", t); - } - } - finally - { - //if (transacted && wasAutoCommit) - //{ - // try - // { - // // TODO NH: reset autocommit - // // TransactionManager.SetAutoCommit(connection, true); - // } - // catch (Exception) - // { - // log.Debug("was unable to reset connection back to auto-commit"); - // } - //} - session.Factory.ConnectionProvider.CloseConnection(connection); - } - } - - private static void AmbientDelegateWork(ISessionImplementor session, IIsolatedWork work, bool transacted) - { - throw new NotSupportedException("Not supported yet."); - } - } } \ No newline at end of file Modified: trunk/nhibernate/src/NHibernate/Engine/TransactionHelper.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Engine/TransactionHelper.cs 2009-05-22 20:42:29 UTC (rev 4360) +++ trunk/nhibernate/src/NHibernate/Engine/TransactionHelper.cs 2009-05-22 22:17:15 UTC (rev 4361) @@ -25,11 +25,11 @@ #region Implementation of IIsolatedWork - public void DoWork(IDbConnection connection) + public void DoWork(IDbConnection connection, IDbTransaction transaction) { try { - generatedValue = owner.DoWorkInCurrentTransaction(connection, null); + generatedValue = owner.DoWorkInCurrentTransaction(session, connection, transaction); } catch (DbException sqle) { @@ -41,7 +41,7 @@ } /// <summary> The work to be done</summary> - public abstract object DoWorkInCurrentTransaction(IDbConnection conn, string sql); + public abstract object DoWorkInCurrentTransaction(ISessionImplementor session, IDbConnection conn, IDbTransaction transaction); /// <summary> Suspend the current transaction and perform work in a new transaction</summary> public virtual object DoWorkInNewTransaction(ISessionImplementor session) Modified: trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Exec/AbstractStatementExecutor.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Exec/AbstractStatementExecutor.cs 2009-05-22 20:42:29 UTC (rev 4360) +++ trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Exec/AbstractStatementExecutor.cs 2009-05-22 22:17:15 UTC (rev 4361) @@ -137,7 +137,7 @@ } else { - work.DoWork(session.ConnectionManager.GetConnection()); + work.DoWork(session.ConnectionManager.GetConnection(), null); session.ConnectionManager.AfterStatement(); } } @@ -149,10 +149,7 @@ { return dialectVote.Value; } - else - { - return Factory.Settings.IsDataDefinitionImplicitCommit; - } + return Factory.Settings.IsDataDefinitionImplicitCommit; } protected virtual void DropTemporaryTableIfNecessary(IQueryable persister, ISessionImplementor session) @@ -174,7 +171,7 @@ } else { - work.DoWork(session.ConnectionManager.GetConnection()); + work.DoWork(session.ConnectionManager.GetConnection(), null); session.ConnectionManager.AfterStatement(); } } @@ -222,7 +219,7 @@ this.session = session; } - public void DoWork(IDbConnection connection) + public void DoWork(IDbConnection connection, IDbTransaction transaction) { IDbCommand stmnt = null; try @@ -266,7 +263,7 @@ private readonly ILog log; private readonly ISessionImplementor session; - public void DoWork(IDbConnection connection) + public void DoWork(IDbConnection connection, IDbTransaction transaction) { IDbCommand stmnt = null; try Modified: trunk/nhibernate/src/NHibernate/Id/Enhanced/TableStructure.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Id/Enhanced/TableStructure.cs 2009-05-22 20:42:29 UTC (rev 4360) +++ trunk/nhibernate/src/NHibernate/Id/Enhanced/TableStructure.cs 2009-05-22 22:17:15 UTC (rev 4361) @@ -99,7 +99,7 @@ #region Overrides of TransactionHelper - public override object DoWorkInCurrentTransaction(IDbConnection conn, string sql) + public override object DoWorkInCurrentTransaction(ISessionImplementor session, IDbConnection conn, IDbTransaction transaction) { long result; int rows; @@ -136,7 +136,7 @@ } query = update.ToString(); - SqlLog.Debug(sql); + IDbCommand ups = conn.CreateCommand(); ups.CommandType = CommandType.Text; ups.CommandText = query; Modified: trunk/nhibernate/src/NHibernate/Id/TableGenerator.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Id/TableGenerator.cs 2009-05-22 20:42:29 UTC (rev 4360) +++ trunk/nhibernate/src/NHibernate/Id/TableGenerator.cs 2009-05-22 22:17:15 UTC (rev 4361) @@ -6,6 +6,7 @@ using log4net; using NHibernate.Dialect; using NHibernate.Engine; +using NHibernate.Engine.Transaction; using NHibernate.SqlCommand; using NHibernate.SqlTypes; using NHibernate.Type; @@ -35,7 +36,7 @@ /// The mapping parameters <c>table</c> and <c>column</c> are required. /// </p> /// </remarks> - public class TableGenerator : IPersistentIdentifierGenerator, IConfigurable + public class TableGenerator : TransactionHelper, IPersistentIdentifierGenerator, IConfigurable { private static readonly ILog log = LogManager.GetLogger(typeof(TableGenerator)); /// <summary> @@ -155,117 +156,7 @@ // This has to be done using a different connection to the containing // transaction becase the new hi value must remain valid even if the // containing transaction rolls back. - // - // We make an exception for SQLite and use the session's connection, - // since SQLite only allows one connection to the database. - - bool isSQLite = session.Factory.Dialect is SQLiteDialect; - IDbConnection conn; - TransactionScope dtcTrans = null; - if (isSQLite) - { - conn = session.Connection; - } - else - { - // existing dtc transaction, creating a new one - // to override the ambient one - if (Transaction.Current != null) - dtcTrans = new TransactionScope(TransactionScopeOption.RequiresNew); - conn = session.Factory.ConnectionProvider.GetConnection(); - } - - IDbTransaction trans = null; - try - { - if (!isSQLite) - { - if(dtcTrans==null) - trans = conn.BeginTransaction(); - } - - long result; - int rows; - do - { - //the loop ensure atomicitiy of the - //select + uspdate even for no transaction - //or read committed isolation level (needed for .net?) - - IDbCommand qps = conn.CreateCommand(); - IDataReader rs = null; - qps.CommandText = query; - qps.CommandType = CommandType.Text; - qps.Transaction = trans; - PersistentIdGeneratorParmsNames.SqlStatementLogger.LogCommand("Reading high value:", qps, FormatStyle.Basic); - try - { - rs = qps.ExecuteReader(); - if (!rs.Read()) - { - string err = "could not read a hi value - you need to populate the table: " + tableName; - log.Error(err); - throw new IdentifierGenerationException(err); - } - result = Convert.ToInt64(columnType.Get(rs, 0)); - } - // TODO: change to SqlException - catch (Exception e) - { - log.Error("could not read a hi value", e); - throw; - } - finally - { - if (rs != null) rs.Close(); - qps.Dispose(); - } - - IDbCommand ups = - session.Factory.ConnectionProvider.Driver.GenerateCommand(CommandType.Text, updateSql, parameterTypes); - ups.Connection = conn; - ups.Transaction = trans; - PersistentIdGeneratorParmsNames.SqlStatementLogger.LogCommand("Updating high value:", ups, FormatStyle.Basic); - - try - { - columnType.Set(ups, result + 1, 0); - columnType.Set(ups, result, 1); - - rows = ups.ExecuteNonQuery(); - } - // TODO: change to SqlException - catch (Exception e) - { - log.Error("could not update hi value in: " + tableName, e); - throw; - } - finally - { - ups.Dispose(); - } - } while (rows == 0); - - if (!isSQLite) - { - if (dtcTrans != null) - dtcTrans.Complete(); - else - trans.Commit(); - } - - return result; - } - // TODO: Shouldn't we have a Catch with a rollback here? - finally - { - if (!isSQLite) - { - if(dtcTrans!=null) - dtcTrans.Dispose(); - session.Factory.ConnectionProvider.CloseConnection(conn); - } - } + return DoWorkInNewTransaction(session); } #endregion @@ -317,5 +208,70 @@ } #endregion + + public override object DoWorkInCurrentTransaction(ISessionImplementor session, IDbConnection conn, IDbTransaction transaction) + { + long result; + int rows; + do + { + //the loop ensure atomicitiy of the + //select + uspdate even for no transaction + //or read committed isolation level (needed for .net?) + + IDbCommand qps = conn.CreateCommand(); + IDataReader rs = null; + qps.CommandText = query; + qps.CommandType = CommandType.Text; + qps.Transaction = transaction; + PersistentIdGeneratorParmsNames.SqlStatementLogger.LogCommand("Reading high value:", qps, FormatStyle.Basic); + try + { + rs = qps.ExecuteReader(); + if (!rs.Read()) + { + string err = "could not read a hi value - you need to populate the table: " + tableName; + log.Error(err); + throw new IdentifierGenerationException(err); + } + result = Convert.ToInt64(columnType.Get(rs, 0)); + } + catch (Exception e) + { + log.Error("could not read a hi value", e); + throw; + } + finally + { + if (rs != null) rs.Close(); + qps.Dispose(); + } + + IDbCommand ups = + session.Factory.ConnectionProvider.Driver.GenerateCommand(CommandType.Text, updateSql, parameterTypes); + ups.Connection = conn; + ups.Transaction = transaction; + PersistentIdGeneratorParmsNames.SqlStatementLogger.LogCommand("Updating high value:", ups, FormatStyle.Basic); + + try + { + columnType.Set(ups, result + 1, 0); + columnType.Set(ups, result, 1); + + rows = ups.ExecuteNonQuery(); + } + catch (Exception e) + { + log.Error("could not update hi value in: " + tableName, e); + throw; + } + finally + { + ups.Dispose(); + } + } while (rows == 0); + + return result; + } } } Modified: trunk/nhibernate/src/NHibernate/Transaction/AdoNetTransactionFactory.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Transaction/AdoNetTransactionFactory.cs 2009-05-22 20:42:29 UTC (rev 4360) +++ trunk/nhibernate/src/NHibernate/Transaction/AdoNetTransactionFactory.cs 2009-05-22 22:17:15 UTC (rev 4361) @@ -1,10 +1,19 @@ +using System; using System.Collections; +using System.Data; +using System.Data.Common; +using log4net; +using NHibernate.Dialect; using NHibernate.Engine; +using NHibernate.Engine.Transaction; +using NHibernate.Exceptions; namespace NHibernate.Transaction { public class AdoNetTransactionFactory : ITransactionFactory { + private readonly ILog isolaterLog = LogManager.GetLogger(typeof(Isolater)); + public ITransaction CreateTransaction(ISessionImplementor session) { return new AdoTransaction(session); @@ -20,6 +29,85 @@ return false; } + public void ExecuteWorkInIsolation(ISessionImplementor session, IIsolatedWork work, bool transacted) + { + IDbConnection connection = null; + IDbTransaction trans = null; + // bool wasAutoCommit = false; + try + { + // We make an exception for SQLite and use the session's connection, + // since SQLite only allows one connection to the database. + if (session.Factory.Dialect is SQLiteDialect) + connection = session.Connection; + else + connection = session.Factory.ConnectionProvider.GetConnection(); + + if (transacted) + { + trans = connection.BeginTransaction(); + // TODO NH: a way to read the autocommit state is needed + //if (TransactionManager.GetAutoCommit(connection)) + //{ + // wasAutoCommit = true; + // TransactionManager.SetAutoCommit(connection, false); + //} + } + + work.DoWork(connection, trans); + + if (transacted) + { + trans.Commit(); + //TransactionManager.Commit(connection); + } + } + catch (Exception t) + { + try + { + if (trans != null && connection.State != ConnectionState.Closed) + { + trans.Rollback(); + } + } + catch (Exception ignore) + { + isolaterLog.Debug("unable to release connection on exception [" + ignore + "]"); + } + + if (t is HibernateException) + { + throw; + } + else if (t is DbException) + { + throw ADOExceptionHelper.Convert(session.Factory.SQLExceptionConverter, t, "error performing isolated work"); + } + else + { + throw new HibernateException("error performing isolated work", t); + } + } + finally + { + //if (transacted && wasAutoCommit) + //{ + // try + // { + // // TODO NH: reset autocommit + // // TransactionManager.SetAutoCommit(connection, true); + // } + // catch (Exception) + // { + // log.Debug("was unable to reset connection back to auto-commit"); + // } + //} + if (session.Factory.Dialect is SQLiteDialect == false) + session.Factory.ConnectionProvider.CloseConnection(connection); + } + } + public void Configure(IDictionary props) { } Modified: trunk/nhibernate/src/NHibernate/Transaction/AdoNetWithDistrubtedTransactionFactory.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Transaction/AdoNetWithDistrubtedTransactionFactory.cs 2009-05-22 20:42:29 UTC (rev 4360) +++ trunk/nhibernate/src/NHibernate/Transaction/AdoNetWithDistrubtedTransactionFactory.cs 2009-05-22 22:17:15 UTC (rev 4361) @@ -3,6 +3,7 @@ using System.Transactions; using log4net; using NHibernate.Engine; +using NHibernate.Engine.Transaction; using NHibernate.Impl; namespace NHibernate.Transaction @@ -10,6 +11,8 @@ public class AdoNetWithDistrubtedTransactionFactory : ITransactionFactory { private static readonly ILog logger = LogManager.GetLogger(typeof (AbstractSessionImpl)); + private readonly AdoNetTransactionFactory adoNetTransactionFactory = new AdoNetTransactionFactory(); + public void Configure(IDictionary props) { @@ -60,6 +63,17 @@ distributedTransactionContext.IsInActiveTransaction; } + public void ExecuteWorkInIsolation(ISessionImplementor session, IIsolatedWork work, bool transacted) + { + using(var tx = new TransactionScope(TransactionScopeOption.Suppress)) + { + // instead of duplicating the logic, we suppress the DTC transaction and create + // our own transaction instead + adoNetTransactionFactory.ExecuteWorkInIsolation(session, work, transacted); + tx.Complete(); + } + } + public class DistributedTransactionContext : ITransactionContext, IEnlistmentNotification { public System.Transactions.Transaction AmbientTransation { get; set; } Modified: trunk/nhibernate/src/NHibernate/Transaction/ITransactionFactory.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Transaction/ITransactionFactory.cs 2009-05-22 20:42:29 UTC (rev 4360) +++ trunk/nhibernate/src/NHibernate/Transaction/ITransactionFactory.cs 2009-05-22 22:17:15 UTC (rev 4361) @@ -3,6 +3,7 @@ using NHibernate; using NHibernate.AdoNet; using NHibernate.Engine; +using NHibernate.Engine.Transaction; namespace NHibernate.Transaction { @@ -30,5 +31,7 @@ void EnlistInDistributedTransactionIfNeeded(ISessionImplementor session); bool IsInDistributedActiveTransaction(ISessionImplementor session); + + void ExecuteWorkInIsolation(ISessionImplementor session, IIsolatedWork work, bool transacted); } } \ No newline at end of file Modified: trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH1054/DummyTransactionFactory.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH1054/DummyTransactionFactory.cs 2009-05-22 20:42:29 UTC (rev 4360) +++ trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH1054/DummyTransactionFactory.cs 2009-05-22 22:17:15 UTC (rev 4361) @@ -2,6 +2,7 @@ using System.Collections; using NHibernate.AdoNet; using NHibernate.Engine; +using NHibernate.Engine.Transaction; using NHibernate.Transaction; namespace NHibernate.Test.NHSpecificTest.NH1054 @@ -26,5 +27,10 @@ { return false; } + + public void ExecuteWorkInIsolation(ISessionImplementor session, IIsolatedWork work, bool transacted) + { + throw new NotImplementedException(); + } } } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |