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:
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user