SPRNET-1380

Cleaned up tests around HibernateTxScopeTransactionManager tests and added a few null-checks around the sessionHolder member to address situations where there's not (yet) a session but ambient (outer) transactions are underway
This commit is contained in:
sbohlen
2010-10-11 21:18:17 +00:00
parent b3cc1b1034
commit 8911336d19
4 changed files with 107 additions and 89 deletions

View File

@@ -977,7 +977,10 @@ namespace Spring.Data.NHibernate
public void SetRollbackOnly()
{
SessionHolder.RollbackOnly = true;
if (SessionHolder != null)
{
SessionHolder.RollbackOnly = true;
}
if (ConnectionHolder != null)
{
ConnectionHolder.RollbackOnly = true;
@@ -993,8 +996,8 @@ namespace Spring.Data.NHibernate
{
get
{
return SessionHolder.RollbackOnly ||
(ConnectionHolder != null && ConnectionHolder.RollbackOnly);
return ((SessionHolder != null && SessionHolder.RollbackOnly) ||
(ConnectionHolder != null && ConnectionHolder.RollbackOnly));
}
}
}

View File

@@ -34,6 +34,7 @@ using Spring.Transaction;
using Spring.Transaction.Support;
using Spring.Transaction.Interceptor;
using System.Transactions;
using System.Collections.Generic;
#endregion
@@ -46,26 +47,46 @@ namespace Spring.Data.NHibernate
[TestFixture]
public class HibernateTxScopeTransactionManagerTests
{
#region Fields
private IDbProvider dbProvider;
private IPlatformTransactionManager transactionManager;
private IApplicationContext ctx;
#endregion
#region Constants
/// <summary>
/// The shared <see cref="log4net.ILog"/> instance for this class (and derived classes).
/// </summary>
protected static readonly ILog log =
LogManager.GetLogger(typeof(TemplateTests));
LogManager.GetLogger(typeof(HibernateTxScopeTransactionManagerTests));
//// force Spring.Data.NHibernate to be preloaded by runtime
//private Type TLocalSessionFactoryObject = typeof(LocalSessionFactoryObject);
private IApplicationContext ctx;
#endregion
private IDbProvider dbProvider;
private IPlatformTransactionManager transactionManager;
[Test]
public void CanProperlyReleaseConnectionsWhenTransactionsAreRolledBack()
{
for (int i = 0; i < 200; i++)
{
try
{
DoSave(true);
}
catch (Exception)
{
}
}
}
[Test]
[Ignore("Test harness left in place to demonstrate the leaking connection issue present in NH2.1 and later")]
public void DoesNotLeakConnection()
{
DoPreventConnectionLeaksPreventionPattern();
}
[Test]
[Ignore("Test harness left in place to demonstrate the leaking connection issue present in NH2.1 and later")]
public void LeaksConnection()
{
DoConnectionLeakingAntiPattern();
}
[SetUp]
public void SetUp()
@@ -92,14 +113,66 @@ namespace Spring.Data.NHibernate
}
}
private static void ExecuteSql(IDbConnection conn, string sql)
/// <summary>
/// this method will fail the test as it follows the anti-pattern of failing to use the (mandatory) NH Transaction to wrap the session call
/// </summary>
private void DoConnectionLeakingAntiPattern()
{
IDbCommand cmd;
cmd = conn.CreateCommand();
cmd.CommandText = sql;
cmd.ExecuteNonQuery();
//this counter must be larger than the 'Max Pool Size' setting in the connection string for this test to demonstrate the issue
int counter = 200;
for (int i = 0; i < counter; i++)
{
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
{
using (ISession session = ((ISessionFactory)ctx["SessionFactory"]).OpenSession())
{
IList<TestObject> to = session.CreateCriteria<TestObject>().List<TestObject>();
}
//because scope.Complete() is never called, the Transaction is rolled back and this results in the orpahned connections
}
}
}
/// <summary>
/// this method demonstrates the proper pattern to follow w NH 2.1 and later, mandating the use of an NH transaction wrapping the session
/// </summary>
private void DoPreventConnectionLeaksPreventionPattern()
{
//this counter must be larger than the 'Max Pool Size' setting in the connection string for this test to demonstrate the issue
int counter = 200;
for (int i = 0; i < counter; i++)
{
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
{
using (ISession session = ((ISessionFactory)ctx["SessionFactory"]).OpenSession())
{
using (ITransaction tx = session.BeginTransaction())
{
try
{
IList<TestObject> to = session.CreateCriteria<TestObject>().List<TestObject>();
throw new Exception("this exception simulates something going wrong!");
}
catch (Exception)
{
tx.Rollback();
}
}
}
//because scope.Complete() is never called, the Transaction is rolled back but the presence of the NH transaction and its
// associated Rollback() call will enable NH to properly release the connection
}
}
}
[Transaction]
private void DoSave(bool simulateException)
@@ -118,22 +191,16 @@ namespace Spring.Data.NHibernate
tx.Complete();
}
}
[Test]
public void CanProperlyReleaseConnectionsWhenTransactionsAreRolledBack()
private static void ExecuteSql(IDbConnection conn, string sql)
{
for (int i = 0; i < 200; i++)
{
try
{
DoSave(true);
}
catch (Exception)
{
}
}
IDbCommand cmd;
cmd = conn.CreateCommand();
cmd.CommandText = sql;
cmd.ExecuteNonQuery();
}
}
}

View File

@@ -3,13 +3,12 @@
xmlns:db="http://www.springframework.net/database"
xmlns:tx="http://www.springframework.net/tx">
<!-- NOTE: Critical that the Max Pool Size parameter is present in the connection string to limit conn pool size for these tests-->
<db:provider id="DbProvider"
provider="SqlServer-2.0"
connectionString="Data Source=SPRINGQA;Database=Spring;User ID=springqa;Password=springqa;Trusted_Connection=False;Max Pool Size=10;"/>
<object id="SessionFactory" type="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate21">
<!-- TODO Provide dedicated NHibernate Schema -->
<property name="DbProvider" ref="DbProvider"/>
<property name="MappingResources">
<list>
@@ -32,57 +31,5 @@
</object>
<tx:attribute-driven />
<!--<object id="nativeNHTestObjectDao" type="Spring.Data.NHibernate.NativeNHTestObjectDao, Spring.Data.NHibernate21.Integration.Tests">
<property name="SessionFactory" ref="SessionFactory"/>
</object>
<object id="NHTestObjectDao" type="Spring.Data.NHibernate.NHTestObjectDao, Spring.Data.NHibernate21.Integration.Tests">
<property name="SessionFactory" ref="SessionFactory"/>
</object>-->
<!-- Transactional Proxy for TestObjectDao using transaction attributes -->
<!--<object id="testObjectDaoViaTxAttributes"
type="Spring.Transaction.Interceptor.TransactionProxyFactoryObject, Spring.Data">
<property name="PlatformTransactionManager" ref="hibernateTransactionManager"/>
<property name="Target">
<object type="Spring.Data.NHibernate.NHTestObjectDao">
<property name="SessionFactory" ref="SessionFactory"/>
</object>
</property>
<property name="ProxyInterfaces" value="Spring.Data.NHibernate.ITestObjectDao"/>
<property name="TransactionAttributeSource">
<object type="Spring.Transaction.Interceptor.AttributesTransactionAttributeSource" />
</property>
</object>
--><!-- Transactional Proxy for TestObjectDao using the TransactionProxyFactory --><!--
<object id="testObjectDaoTransProxy"
type="Spring.Transaction.Interceptor.TransactionProxyFactoryObject, Spring.Data">
<property name="PlatformTransactionManager" ref="hibernateTransactionManager"/>
<property name="Target">
<object type="Spring.Data.NHibernate.NHTestObjectDao, Spring.Data.NHibernate21.Integration.Tests">
<property name="SessionFactory" ref="SessionFactory"/>
</object>
</property>
<property name="ProxyInterfaces" value="Spring.Data.NHibernate.ITestObjectDao"/>
<property name="TransactionAttributes">
<name-values>
<add key="Create*" value="PROPAGATION_REQUIRED"/>
<add key="Delete*" value="PROPAGATION_REQUIRED"/>
<add key="Update*" value="PROPAGATION_REQUIRED"/>
<add key="Find*" value="PROPAGATION_REQUIRED"/>
</name-values>
</property>
</object>-->
</objects>

View File

@@ -25,6 +25,7 @@
<DefineConstants>TRACE;DEBUG;NET_2_0,NH_2_0,NH_2_1</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>