diff --git a/src/NHibernate.Test/ConnectionTest/BatcherFixture.cs b/src/NHibernate.Test/ConnectionTest/BatcherFixture.cs new file mode 100644 index 00000000000..d96a7c205d5 --- /dev/null +++ b/src/NHibernate.Test/ConnectionTest/BatcherFixture.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using NHibernate.Cfg; +using NHibernate.Util; +using NUnit.Framework; +using Environment = NHibernate.Cfg.Environment; + +namespace NHibernate.Test.ConnectionTest +{ + [TestFixture] + public class BatcherFixture : ConnectionManagementTestCase + { + protected override void Configure(Configuration config) + { + base.Configure(config); + config.SetProperty(Environment.BatchSize, "10"); + } + + protected override ISession GetSessionUnderTest() + => OpenSession(); + + protected override void OnTearDown() + { + using (var s = OpenSession()) + { + s.CreateQuery("delete from System.Object").ExecuteUpdate(); + } + } + + [Test] + public void CanCloseCommandsAndUseBatcher() + { + using (var s = OpenSession()) + { + // Need a generator strategy not causing insert at save. + var silly = new YetAnother { Name = "Silly" }; + s.Save(silly); + s.GetSessionImplementation().ConnectionManager.Batcher.CloseCommands(); + + Assert.DoesNotThrow(s.Flush, "Flush failure after closing commands."); + } + } + } +} \ No newline at end of file diff --git a/src/NHibernate.Test/ConnectionTest/Silly.hbm.xml b/src/NHibernate.Test/ConnectionTest/Silly.hbm.xml index bc233c62ead..b36c66f911a 100644 --- a/src/NHibernate.Test/ConnectionTest/Silly.hbm.xml +++ b/src/NHibernate.Test/ConnectionTest/Silly.hbm.xml @@ -16,4 +16,11 @@ + + + + + + + \ No newline at end of file diff --git a/src/NHibernate.Test/ConnectionTest/YetAnother.cs b/src/NHibernate.Test/ConnectionTest/YetAnother.cs new file mode 100644 index 00000000000..398a9a39542 --- /dev/null +++ b/src/NHibernate.Test/ConnectionTest/YetAnother.cs @@ -0,0 +1,12 @@ +using System; + +namespace NHibernate.Test.ConnectionTest +{ + [Serializable] + public class YetAnother + { + public virtual long Id { get; set; } + + public virtual string Name { get; set; } + } +} \ No newline at end of file diff --git a/src/NHibernate.Test/NHibernate.Test.csproj b/src/NHibernate.Test/NHibernate.Test.csproj index fbd40cd73ec..1ec3a9086c4 100644 --- a/src/NHibernate.Test/NHibernate.Test.csproj +++ b/src/NHibernate.Test/NHibernate.Test.csproj @@ -202,8 +202,10 @@ + + diff --git a/src/NHibernate/AdoNet/MySqlClientBatchingBatcher.cs b/src/NHibernate/AdoNet/MySqlClientBatchingBatcher.cs index 59904a1b74e..0e8dc1ff1b2 100644 --- a/src/NHibernate/AdoNet/MySqlClientBatchingBatcher.cs +++ b/src/NHibernate/AdoNet/MySqlClientBatchingBatcher.cs @@ -69,34 +69,48 @@ public override void AddToBatch(IExpectation expectation) protected override void DoExecuteBatch(DbCommand ps) { - Log.DebugFormat("Executing batch"); - CheckReaders(); - if (Factory.Settings.SqlStatementLogger.IsDebugEnabled) - { - Factory.Settings.SqlStatementLogger.LogBatchCommand(currentBatchCommandsLog.ToString()); - currentBatchCommandsLog = new StringBuilder().AppendLine("Batch commands:"); - } - - int rowsAffected; try { - rowsAffected = currentBatch.ExecuteNonQuery(); + Log.DebugFormat("Executing batch"); + CheckReaders(); + if (Factory.Settings.SqlStatementLogger.IsDebugEnabled) + { + Factory.Settings.SqlStatementLogger.LogBatchCommand(currentBatchCommandsLog.ToString()); + } + + int rowsAffected; + try + { + rowsAffected = currentBatch.ExecuteNonQuery(); + } + catch (DbException e) + { + throw ADOExceptionHelper.Convert(Factory.SQLExceptionConverter, e, "could not execute batch command."); + } + + Expectations.VerifyOutcomeBatched(totalExpectedRowsAffected, rowsAffected); } - catch (DbException e) + finally { - throw ADOExceptionHelper.Convert(Factory.SQLExceptionConverter, e, "could not execute batch command."); + ClearCurrentBatch(); } + } - Expectations.VerifyOutcomeBatched(totalExpectedRowsAffected, rowsAffected); + private MySqlClientSqlCommandSet CreateConfiguredBatch() + { + return new MySqlClientSqlCommandSet(batchSize); + } + private void ClearCurrentBatch() + { currentBatch.Dispose(); totalExpectedRowsAffected = 0; currentBatch = CreateConfiguredBatch(); - } - private MySqlClientSqlCommandSet CreateConfiguredBatch() - { - return new MySqlClientSqlCommandSet(batchSize); + if (Factory.Settings.SqlStatementLogger.IsDebugEnabled) + { + currentBatchCommandsLog = new StringBuilder().AppendLine("Batch commands:"); + } } public override void CloseCommands() @@ -105,12 +119,27 @@ public override void CloseCommands() try { - currentBatch.Dispose(); + ClearCurrentBatch(); } catch (Exception e) { - // Prevent exceptions when closing the batch from hiding any original exception + // Prevent exceptions when clearing the batch from hiding any original exception // (We do not know here if this batch closing occurs after a failure or not.) + Log.Warn("Exception clearing batch", e); + } + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + // Prevent exceptions when closing the batch from hiding any original exception + // (We do not know here if this batch closing occurs after a failure or not.) + try + { + currentBatch.Dispose(); + } + catch (Exception e) + { Log.Warn("Exception closing batcher", e); } } diff --git a/src/NHibernate/AdoNet/SqlClientBatchingBatcher.cs b/src/NHibernate/AdoNet/SqlClientBatchingBatcher.cs index 9c8196a97d7..94cd00112c7 100644 --- a/src/NHibernate/AdoNet/SqlClientBatchingBatcher.cs +++ b/src/NHibernate/AdoNet/SqlClientBatchingBatcher.cs @@ -61,7 +61,7 @@ public override void AddToBatch(IExpectation expectation) { Log.Debug("Adding to batch:" + lineWithParameters); } - _currentBatch.Append((System.Data.SqlClient.SqlCommand) batchUpdate); + _currentBatch.Append((System.Data.SqlClient.SqlCommand)batchUpdate); if (_currentBatch.CountOfCommands >= _batchSize) { @@ -71,30 +71,31 @@ public override void AddToBatch(IExpectation expectation) protected override void DoExecuteBatch(DbCommand ps) { - Log.DebugFormat("Executing batch"); - CheckReaders(); - Prepare(_currentBatch.BatchCommand); - if (Factory.Settings.SqlStatementLogger.IsDebugEnabled) - { - Factory.Settings.SqlStatementLogger.LogBatchCommand(_currentBatchCommandsLog.ToString()); - _currentBatchCommandsLog = new StringBuilder().AppendLine("Batch commands:"); - } - - int rowsAffected; try { - rowsAffected = _currentBatch.ExecuteNonQuery(); + Log.DebugFormat("Executing batch"); + CheckReaders(); + Prepare(_currentBatch.BatchCommand); + if (Factory.Settings.SqlStatementLogger.IsDebugEnabled) + { + Factory.Settings.SqlStatementLogger.LogBatchCommand(_currentBatchCommandsLog.ToString()); + } + int rowsAffected; + try + { + rowsAffected = _currentBatch.ExecuteNonQuery(); + } + catch (DbException e) + { + throw ADOExceptionHelper.Convert(Factory.SQLExceptionConverter, e, "could not execute batch command."); + } + + Expectations.VerifyOutcomeBatched(_totalExpectedRowsAffected, rowsAffected); } - catch (DbException e) + finally { - throw ADOExceptionHelper.Convert(Factory.SQLExceptionConverter, e, "could not execute batch command."); + ClearCurrentBatch(); } - - Expectations.VerifyOutcomeBatched(_totalExpectedRowsAffected, rowsAffected); - - _currentBatch.Dispose(); - _totalExpectedRowsAffected = 0; - _currentBatch = CreateConfiguredBatch(); } private SqlClientSqlCommandSet CreateConfiguredBatch() @@ -118,18 +119,45 @@ private SqlClientSqlCommandSet CreateConfiguredBatch() return result; } + private void ClearCurrentBatch() + { + _currentBatch.Dispose(); + _totalExpectedRowsAffected = 0; + _currentBatch = CreateConfiguredBatch(); + + if (Factory.Settings.SqlStatementLogger.IsDebugEnabled) + { + _currentBatchCommandsLog = new StringBuilder().AppendLine("Batch commands:"); + } + } + public override void CloseCommands() { base.CloseCommands(); + // Prevent exceptions when closing the batch from hiding any original exception + // (We do not know here if this batch closing occurs after a failure or not.) + try + { + ClearCurrentBatch(); + } + catch (Exception e) + { + Log.Warn("Exception clearing batch", e); + } + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + // Prevent exceptions when closing the batch from hiding any original exception + // (We do not know here if this batch closing occurs after a failure or not.) try { _currentBatch.Dispose(); } catch (Exception e) { - // Prevent exceptions when closing the batch from hiding any original exception - // (We do not know here if this batch closing occurs after a failure or not.) Log.Warn("Exception closing batcher", e); } }