These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 4 posts ] 
Author Message
 Post subject: SequenceStyleGenerator causes sql exception on mssql 2005
PostPosted: Fri Oct 30, 2009 11:53 am 
Newbie

Joined: Mon Jun 30, 2008 11:43 am
Posts: 7
Is there any documentation on the new NHibernate.Id.Enhanced.SequenceStyleGenerator that has sneaked in at some point?

Namely is it ready for use as on MSSQL 2005 I get the following exception:

Code:
System.InvalidOperationException: SqlConnection does not support parallel transactions.
at System.Data.SqlClient.SqlInternalConnection.BeginSqlTransaction(IsolationLevel iso, String transactionName)
at System.Data.SqlClient.SqlInternalConnection.BeginTransaction(IsolationLevel iso)
at System.Data.SqlClient.SqlConnection.BeginDbTransaction(IsolationLevel isolationLevel)
at System.Data.Common.DbConnection.System.Data.IDbConnection.BeginTransaction()
at NHibernate.Id.Enhanced.TableStructure.DoWorkInCurrentTransaction(ISessionImplementor session, IDbConnection conn, IDbTransaction transaction)
at NHibernate.Engine.TransactionHelper.Work.DoWork(IDbConnection connection, IDbTransaction transaction)
at NHibernate.Transaction.AdoNetTransactionFactory.ExecuteWorkInIsolation(ISessionImplementor session, IIsolatedWork work, Boolean transacted)

NHibernate.HibernateException: error performing isolated work
at NHibernate.Transaction.AdoNetTransactionFactory.ExecuteWorkInIsolation(ISessionImplementor session, IIsolatedWork work, Boolean transacted)
at NHibernate.Transaction.AdoNetWithDistrubtedTransactionFactory.ExecuteWorkInIsolation(ISessionImplementor session, IIsolatedWork work, Boolean transacted)
at NHibernate.Engine.Transaction.Isolater.DoIsolatedWork(IIsolatedWork work, ISessionImplementor session)
at NHibernate.Engine.TransactionHelper.DoWorkInNewTransaction(ISessionImplementor session)
at NHibernate.Id.Enhanced.TableStructure.TableAccessCallback.get_NextValue()
at NHibernate.Id.Enhanced.OptimizerFactory.NoopOptimizer.Generate(IAccessCallback callback)
at NHibernate.Id.Enhanced.SequenceStyleGenerator.Generate(ISessionImplementor session, Object obj)
at NHibernate.Event.Default.AbstractSaveEventListener.SaveWithGeneratedId(Object entity, String entityName, Object anything, IEventSource source, Boolean requiresImmediateIdAccess)
at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.SaveWithGeneratedOrRequestedId(SaveOrUpdateEvent event)
at NHibernate.Event.Default.DefaultSaveEventListener.SaveWithGeneratedOrRequestedId(SaveOrUpdateEvent event)
at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.EntityIsTransient(SaveOrUpdateEvent event)
at NHibernate.Event.Default.DefaultSaveEventListener.PerformSaveOrUpdate(SaveOrUpdateEvent event)
at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.OnSaveOrUpdate(SaveOrUpdateEvent event)
at NHibernate.Impl.SessionImpl.FireSave(SaveOrUpdateEvent event)
at NHibernate.Impl.SessionImpl.Save(Object obj)


Top
 Profile  
 
 Post subject: Re: SequenceStyleGenerator causes sql exception on mssql 2005
PostPosted: Fri Oct 30, 2009 12:44 pm 
Newbie

Joined: Mon Jun 30, 2008 11:43 am
Posts: 7
The following unit test can be used to recreate the problem when run under mssql server 2005 (unsure about any other dialects).

Code:
using System.Collections;
using NUnit.Framework;

namespace NHibernate.Test.SqlTest.Custom.MsSQL
{
    [TestFixture]
    public class MonkeyTests : TestCase
    {
        protected override string MappingsAssembly
        {
            get
            {
                return "NHibernate.Test";
            }
        }

        protected override IList Mappings
        {
            get { return new[] {"SqlTest.Custom.MsSQL.Monkey.hbm.xml"}; }
        }

        [Test]
        public void CanGenerateIdWithTransactionOpen()
        {
            using (var session = OpenSession())
            using (var transaction = session.BeginTransaction())
            {
                var monkey = new Monkey();
                session.Save(monkey);
                session.Flush();
                transaction.Rollback();
            }
        }
    }

    public class Monkey
    {
        public virtual int Id { get; set; }
    }
}


Code:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
               assembly="NHibernate.Test"
               namespace="NHibernate.Test.SqlTest.Custom.MsSQL">

   <class name="Monkey">
      <id name="Id" column="id">
         <generator class="NHibernate.Id.Enhanced.SequenceStyleGenerator, NHibernate" />
      </id>
   </class>
</hibernate-mapping>


Top
 Profile  
 
 Post subject: Re: SequenceStyleGenerator causes sql exception on mssql 2005
PostPosted: Fri Oct 30, 2009 1:07 pm 
Newbie

Joined: Mon Jun 30, 2008 11:43 am
Posts: 7
Ah located it, NHibernate is creating 2 transactions on the same connection (line numbers according to trunk@r4808).

NHibernate.Transaction.AdoNetTransactionFactory.ExecuteWorkInIsolation, line 48
NHibernate.Id.Enhanced.TableStructure.DoWorkInCurrentTransaction, line 114

The following patch fixes the transaction issue I believe, but does create a new problem at line 148 as the command "ups" hasn't actually got any parameters so an IndexOutOfRangeException is raised.

Code:
Index: TableStructure.cs
===================================================================
--- TableStructure.cs   (revision 4808)
+++ TableStructure.cs   (working copy)
@@ -111,7 +111,7 @@
            IDataReader rs = null;
            qps.CommandText = query;
            qps.CommandType = CommandType.Text;
-            qps.Transaction = conn.BeginTransaction();
+            qps.Transaction = transaction;
            try
            {
               rs = qps.ExecuteReader();
@@ -141,7 +141,7 @@
            ups.CommandType = CommandType.Text;
            ups.CommandText = query;
            ups.Connection = conn;
-            ups.Transaction = conn.BeginTransaction();
+            ups.Transaction = transaction;
            try
            {
               int increment = applyIncrementSizeToSourceValues ? incrementSize : 1;


Top
 Profile  
 
 Post subject: Re: SequenceStyleGenerator causes sql exception on mssql 2005
PostPosted: Fri Oct 30, 2009 2:04 pm 
Newbie

Joined: Mon Jun 30, 2008 11:43 am
Posts: 7
This seems to work fine, at least the basic test now passes.

Code:
Index: src/NHibernate.Test/NHibernate.Test.csproj
===================================================================
--- src/NHibernate.Test/NHibernate.Test.csproj   (revision 4808)
+++ src/NHibernate.Test/NHibernate.Test.csproj   (working copy)
@@ -1296,6 +1296,7 @@
     <Compile Include="SqlTest\Custom\CustomStoredProcSupportTest.cs" />
     <Compile Include="SqlTest\Custom\MySQL\MySQLTest.cs" />
     <Compile Include="SqlTest\Custom\Oracle\OracleCustomSQLFixture.cs" />
+    <Compile Include="SqlTest\Custom\MsSQL\Monkey.cs" />
     <Compile Include="SqlTest\SqlTypeFactoryFixture.cs" />
     <Compile Include="Stateless\Naturalness.cs" />
     <Compile Include="Stateless\StatelessWithRelationsFixture.cs" />
@@ -2052,6 +2053,7 @@
     <EmbeddedResource Include="CfgTest\Loquacious\EntityToCache.hbm.xml" />
     <EmbeddedResource Include="DriverTest\SqlServerCeEntity.hbm.xml" />
     <Content Include="DynamicEntity\package.html" />
+    <EmbeddedResource Include="SqlTest\Custom\MsSQL\Monkey.hbm.xml" />
     <EmbeddedResource Include="NHSpecificTest\NH2003\Mappings.hbm.xml" />
     <EmbeddedResource Include="NHSpecificTest\NH1356\MappingsBag.hbm.xml" />
     <EmbeddedResource Include="NHSpecificTest\NH1356\MappingsList.hbm.xml" />
Index: src/NHibernate/Id/Enhanced/TableStructure.cs
===================================================================
--- src/NHibernate/Id/Enhanced/TableStructure.cs   (revision 4808)
+++ src/NHibernate/Id/Enhanced/TableStructure.cs   (working copy)
@@ -102,72 +102,88 @@
      public override object DoWorkInCurrentTransaction(ISessionImplementor session, IDbConnection conn, IDbTransaction transaction)
      {
         long result;
-         int rows;
+         bool updated;
+
         do
         {
-            string query = select.ToString();
-            SqlLog.Debug(query);
-            IDbCommand qps = conn.CreateCommand();
-            IDataReader rs = null;
-            qps.CommandText = query;
-            qps.CommandType = CommandType.Text;
-            qps.Transaction = conn.BeginTransaction();
-            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 = rs.GetInt64(0);
-               rs.Close();
-            }
-            catch (Exception sqle)
-            {
-               log.Error("could not read a hi value", sqle);
-               throw;
-            }
-            finally
-            {
-               if (rs != null) rs.Close();
-               qps.Dispose();
-            }
+             result = QueryCurrentHi(session, conn, transaction);
+             updated = UpdateNextHi(session, conn, transaction, result);
+         } while (!updated);

-            query = update.ToString();
-            
-            IDbCommand ups = conn.CreateCommand();
-            ups.CommandType = CommandType.Text;
-            ups.CommandText = query;
-            ups.Connection = conn;
-            ups.Transaction = conn.BeginTransaction();
-            try
-            {
-               int increment = applyIncrementSizeToSourceValues ? incrementSize : 1;
-               ((IDataParameter) ups.Parameters[0]).Value = result + increment;
-               ((IDataParameter)ups.Parameters[1]).Value = result;
-               rows = ups.ExecuteNonQuery();
-            }
-            catch (Exception sqle)
-            {
-               log.Error("could not update hi value in: " + tableName, sqle);
-               throw;
-            }
-            finally
-            {
-               ups.Dispose();
-            }
-         }
-         while (rows == 0);
-
         accessCounter++;

         return result;
      }

-      #endregion
+       private long QueryCurrentHi(ISessionImplementor session, IDbConnection conn, IDbTransaction transaction)
+       {
+           long result;

+           IDbCommand qps = session.Factory.ConnectionProvider.Driver.GenerateCommand(CommandType.Text, select, SqlTypeFactory.NoTypes);
+           IDataReader rs = null;
+           qps.Connection = conn;
+           qps.Transaction = transaction;
+
+            SqlLog.Debug(qps.CommandText);
+
+           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 = rs.GetInt64(0);
+               rs.Close();
+           }
+           catch (Exception sqle)
+           {
+               log.Error("could not read a hi value", sqle);
+               throw;
+           }
+           finally
+           {
+               if (rs != null) rs.Close();
+               qps.Dispose();
+           }
+
+           return result;
+       }
+
+       private bool UpdateNextHi(ISessionImplementor session, IDbConnection conn, IDbTransaction transaction, long result)
+       {
+           bool updated;
+
+           IDbCommand ups = session.Factory.ConnectionProvider.Driver.GenerateCommand(CommandType.Text, update, new[] {SqlTypeFactory.Int64, SqlTypeFactory.Int64});
+           ups.Connection = conn;
+           ups.Transaction = transaction;
+
+            SqlLog.Debug(ups.CommandText);
+
+           try
+           {
+               int increment = applyIncrementSizeToSourceValues ? incrementSize : 1;
+               ((IDataParameter)ups.Parameters[0]).Value = result + increment;
+               ((IDataParameter)ups.Parameters[1]).Value = result;
+               updated = ups.ExecuteNonQuery() != 0;
+           }
+           catch (Exception sqle)
+           {
+               log.Error("could not update hi value in: " + tableName, sqle);
+               throw;
+           }
+           finally
+           {
+               ups.Dispose();
+           }
+
+           return updated;
+       }
+
+       #endregion
+
      #region Nested type: TableAccessCallback

      private class TableAccessCallback : IAccessCallback


Code:
using System.Collections;
using NUnit.Framework;

namespace NHibernate.Test.SqlTest.Custom.MsSQL
{
    [TestFixture]
    public class MonkeyTests : TestCase
    {
        protected override string MappingsAssembly
        {
            get
            {
                return "NHibernate.Test";
            }
        }

        protected override IList Mappings
        {
            get { return new[] {"SqlTest.Custom.MsSQL.Monkey.hbm.xml"}; }
        }

        [Test]
        public void CanGenerateIdWithTransactionOpen()
        {
            using (var session = OpenSession())
            using (var transaction = session.BeginTransaction())
            {
                var monkey1 = new Monkey();
                var monkey2 = new Monkey();
               
                Assert.AreEqual(0, monkey1.Id);
                Assert.AreEqual(0, monkey1.Id);

                session.Save(monkey1);
                session.Save(monkey2);
               
                Assert.AreEqual(1, monkey1.Id);
                Assert.AreEqual(2, monkey2.Id);

                session.Flush();
                transaction.Rollback();
            }

            using (var session = OpenSession())
            using (var transaction = session.BeginTransaction())
            {
                var monkey = new Monkey();
               
                Assert.AreEqual(0, monkey.Id);

                session.Save(monkey);
               
                Assert.AreEqual(3, monkey.Id);

                session.Flush();
                transaction.Rollback();
            }
        }
    }

    public class Monkey
    {
        public virtual int Id { get; set; }
    }
}


Code:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
               assembly="NHibernate.Test"
               namespace="NHibernate.Test.SqlTest.Custom.MsSQL">

   <class name="Monkey">
      <id name="Id" column="id">
         <generator class="NHibernate.Id.Enhanced.SequenceStyleGenerator, NHibernate" />
         <!--<generator class="hilo" />-->
      </id>
   </class>
</hibernate-mapping>


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 4 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.