SPRNET-1301

Added support for runtime resolution of multiple concurrent NHibernate Session Factories
This commit is contained in:
sbohlen
2010-10-04 21:09:38 +00:00
parent b46a1b9a68
commit 78845eb327
10 changed files with 400 additions and 15 deletions

View File

@@ -1,19 +1,19 @@
#region License
/*
* Copyright <20> 2002-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
/*
* Copyright <20> 2002-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion

View File

@@ -0,0 +1,51 @@
using System.Data;
using System.Collections;
using System.Collections.Generic;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Connection;
using Spring.Data.NHibernate;
namespace Spring.Data.NHibernate
{
///<summary>
/// Delegates to an implementation of ISessionFactory that can select among multiple instances based on
/// thread local storage.
///</summary>
public class DelegatingLocalSessionFactoryObject : LocalSessionFactoryObject
{
/// <summary>
/// Subclasses can override this method to perform custom initialization
/// of the SessionFactory instance, creating it via the given Configuration
/// object that got prepared by this LocalSessionFactoryObject.
/// </summary>
/// <remarks>
/// <p>The default implementation invokes Configuration's BuildSessionFactory.
/// A custom implementation could prepare the instance in a specific way,
/// or use a custom ISessionFactory subclass.
/// </p>
/// </remarks>
/// <returns>The ISessionFactory instance.</returns>
protected override ISessionFactory NewSessionFactory(Configuration config)
{
return new SimpleDelegatingSessionFactory(config);
}
/// <summary>
/// PostProcessConfiguration
/// </summary>
/// <param name="config"></param>
protected override void PostProcessConfiguration(Configuration config)
{
// called before NewSessionFactory
if (!config.Properties.ContainsKey(Environment.ConnectionString))
{
throw new System.ArgumentException("Must specify connection string");
}
}
}
}

View File

@@ -0,0 +1,167 @@
using System.Data;
using System.Collections;
using System.Collections.Generic;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Connection;
using NHibernate.Dialect;
using NHibernate.Engine;
using NHibernate.Metadata;
using NHibernate.Stat;
namespace Spring.Data.NHibernate
{
#pragma warning disable 1591
/// <summary>
/// DelegatingSessionFactory class
/// </summary>
public abstract class DelegatingSessionFactory : ISessionFactory
{
public ICollection<string> DefinedFilterNames
{
get { return TargetSessionFactory.DefinedFilterNames; }
}
public bool IsClosed
{
get { return TargetSessionFactory.IsClosed; }
}
public IStatistics Statistics
{
get { return TargetSessionFactory.Statistics; }
}
public abstract ISessionFactory TargetSessionFactory
{
get;
}
public void Close()
{
TargetSessionFactory.Close();
}
public void Dispose()
{
TargetSessionFactory.Dispose();
}
public void Evict(System.Type persistentClass, object id)
{
TargetSessionFactory.Evict(persistentClass, id);
}
public void Evict(System.Type persistentClass)
{
TargetSessionFactory.Evict(persistentClass);
}
public void EvictCollection(string roleName, object id)
{
TargetSessionFactory.EvictCollection(roleName, id);
}
public void EvictCollection(string roleName)
{
TargetSessionFactory.EvictCollection(roleName);
}
public void EvictEntity(string entityName)
{
TargetSessionFactory.EvictEntity(entityName);
}
public void EvictEntity(string entityName, object id)
{
TargetSessionFactory.EvictEntity(entityName, id);
}
public void EvictQueries(string cacheRegion)
{
TargetSessionFactory.EvictQueries(cacheRegion);
}
public void EvictQueries()
{
TargetSessionFactory.EvictQueries();
}
public IDictionary<string, IClassMetadata> GetAllClassMetadata()
{
return TargetSessionFactory.GetAllClassMetadata();
}
public IDictionary<string, ICollectionMetadata> GetAllCollectionMetadata()
{
return TargetSessionFactory.GetAllCollectionMetadata();
}
public IClassMetadata GetClassMetadata(System.Type persistentType)
{
return TargetSessionFactory.GetClassMetadata(persistentType);
}
public IClassMetadata GetClassMetadata(string entityName)
{
return TargetSessionFactory.GetClassMetadata(entityName);
}
public ICollectionMetadata GetCollectionMetadata(string roleName)
{
return TargetSessionFactory.GetCollectionMetadata(roleName);
}
public ISession GetCurrentSession()
{
return TargetSessionFactory.GetCurrentSession();
}
public FilterDefinition GetFilterDefinition(string filterName)
{
return TargetSessionFactory.GetFilterDefinition(filterName);
}
public ISession OpenSession(IInterceptor interceptor)
{
return TargetSessionFactory.OpenSession(interceptor);
}
public ISession OpenSession()
{
return TargetSessionFactory.OpenSession();
}
public ISession OpenSession(IDbConnection conn, IInterceptor interceptor)
{
return TargetSessionFactory.OpenSession(conn, interceptor);
}
public ISession OpenSession(IDbConnection conn)
{
return TargetSessionFactory.OpenSession(conn);
}
public IStatelessSession OpenStatelessSession()
{
return TargetSessionFactory.OpenStatelessSession();
}
public IStatelessSession OpenStatelessSession(IDbConnection connection)
{
return TargetSessionFactory.OpenStatelessSession(connection);
}
IDictionary<string, IClassMetadata> ISessionFactory.GetAllClassMetadata()
{
return TargetSessionFactory.GetAllClassMetadata();
}
IDictionary<string, ICollectionMetadata> ISessionFactory.GetAllCollectionMetadata()
{
return TargetSessionFactory.GetAllCollectionMetadata();
}
}
}

View File

@@ -0,0 +1,105 @@
using System.Collections;
using NHibernate;
using NHibernate.Cfg;
using NhCfg = NHibernate.Cfg;
using Spring.Collections;
using Spring.Threading;
using Spring.Data.Common;
using Spring.Context.Support;
using NHibernate.Engine;
using System;
namespace Spring.Data.NHibernate
{
/// <summary>
/// SimpleDelegatingSessionFactory class
/// </summary>
public class SimpleDelegatingSessionFactory : DelegatingSessionFactory
{
/// <summary>
/// Connection string config element name
/// </summary>
public const string CONNECTION_STRING = "SimpleDelegatingSessionFactory.ConnectionString";
private Configuration _configuration;
private string _defaultConnectionString;
private object _monitor = new object();
private IDictionary _targetSessionFactories = new SynchronizedHashtable();
/// <summary>
/// public Constructor
/// </summary>
/// <param name="defaultConfiguration"></param>
public SimpleDelegatingSessionFactory(Configuration defaultConfiguration)
{
if (defaultConfiguration == null)
{
throw new ArgumentException("Configuration cannot be null", "defaultConfiguration");
}
_configuration = defaultConfiguration;
if (!_configuration.Properties.ContainsKey(NhCfg.Environment.ConnectionString))
{
throw new ArgumentException("Must specify connection string");
}
_defaultConnectionString = _configuration.Properties[NhCfg.Environment.ConnectionString] as string;
if (_defaultConnectionString == null)
{
throw new ArgumentException("Connection string property must be of type string, not " +
_configuration.Properties[NhCfg.Environment.ConnectionString].GetType().FullName);
}
}
/// <summary>
/// TargetSessionFactory
/// </summary>
public override ISessionFactory TargetSessionFactory
{
get
{
string connectionString = LogicalThreadContext.GetData(CONNECTION_STRING) as string;
System.Diagnostics.Trace.WriteLine(String.Format("{0} = {1}", System.Threading.Thread.CurrentThread.GetHashCode(), connectionString));
if (connectionString == null)
{
connectionString = _defaultConnectionString;
}
lock (_monitor)
{
if (!_targetSessionFactories.Contains(connectionString))
{
System.Diagnostics.Trace.WriteLine(System.Threading.Thread.CurrentThread.GetHashCode().ToString() + " = (created) ");
_configuration.Properties[NhCfg.Environment.ConnectionString] = connectionString;
ISessionFactory sessionFactory = _configuration.BuildSessionFactory();
LocalSessionFactoryObject.DbProviderWrapper dbProviderWrapper = ((ISessionFactoryImplementor)sessionFactory).ConnectionProvider as LocalSessionFactoryObject.DbProviderWrapper;
if (dbProviderWrapper != null)
{
dbProviderWrapper.DbProvider = (IDbProvider)ContextRegistry.GetContext().GetObject("DbProvider");
}
_targetSessionFactories[connectionString] = sessionFactory;
}
else
System.Diagnostics.Trace.WriteLine(System.Threading.Thread.CurrentThread.GetHashCode().ToString() + " = (cached) ");
ISessionFactory factory = _targetSessionFactories[connectionString] as ISessionFactory;
System.Diagnostics.Trace.WriteLine(String.Format("{0} = {1}", System.Threading.Thread.CurrentThread.GetHashCode(), ((ISessionFactoryImplementor)factory).ConnectionProvider.GetConnection().ConnectionString));
return factory;
}
}
}
}
}

View File

@@ -167,6 +167,9 @@
<Compile Include="Data\NHibernate\Bytecode\ReflectionOptimizer.cs" />
<Compile Include="Data\NHibernate\Bytecode\BytecodeProvider.cs" />
<Compile Include="Data\NHibernate\Bytecode\ObjectsFactory.cs" />
<Compile Include="Data\NHibernate\DelegatingLocalSessionFactoryObject.cs" />
<Compile Include="Data\NHibernate\DelegatingSessionFactory.cs" />
<Compile Include="Data\NHibernate\SimpleDelegatingSessionFactory.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Spring.Aop\Spring.Aop.2010.csproj">

View File

@@ -35,6 +35,7 @@
<include name="../Spring.Data.NHibernate12/Data/**/*.cs" />
<include name="../Spring.Data.NHibernate12/Support/**/*.cs" />
<include name="../Spring.Data.NHibernate20/Data/**/*.cs" />
<include name="../Spring.Data.NHibernate21/Data/**/*.cs" />
<exclude name="../Spring.Data.NHibernate/Data/**/HibernateAccessor.cs" />
<exclude name="../Spring.Data.NHibernate/Data/**/SpringSessionSynchronization.cs" />
<exclude name="../Spring.Data.NHibernate/Data/**/LocalSessionFactoryObject.cs" />

View File

@@ -107,6 +107,15 @@
<Compile Include="..\Spring.Data.NHibernate21\Data\NHibernate\Bytecode\ReflectionOptimizer.cs">
<Link>Data\NHibernate\Bytecode\ReflectionOptimizer.cs</Link>
</Compile>
<Compile Include="..\Spring.Data.NHibernate21\Data\NHibernate\DelegatingLocalSessionFactoryObject.cs">
<Link>Data\NHibernate\DelegatingLocalSessionFactoryObject.cs</Link>
</Compile>
<Compile Include="..\Spring.Data.NHibernate21\Data\NHibernate\DelegatingSessionFactory.cs">
<Link>Data\NHibernate\DelegatingSessionFactory.cs</Link>
</Compile>
<Compile Include="..\Spring.Data.NHibernate21\Data\NHibernate\SimpleDelegatingSessionFactory.cs">
<Link>Data\NHibernate\SimpleDelegatingSessionFactory.cs</Link>
</Compile>
<Compile Include="..\Spring.Data.NHibernate\Data\NHibernate\HibernateAdoException.cs">
<Link>Data\NHibernate\HibernateAdoException.cs</Link>
</Compile>

View File

@@ -30,7 +30,6 @@
<include name="../Spring.Data.NHibernate12/Support/**/*.cs" />
<include name="../Spring.Data.NHibernate20/Data/**/*.cs" />
<include name="../Spring.Data.NHibernate21/Data/**/*.cs" />
<include name="../Spring.Data.NHibernate21/Data/**/*.cs" />
<exclude name="../Spring.Data.NHibernate/Data/**/HibernateAccessor.cs" />
<exclude name="../Spring.Data.NHibernate/Data/**/SpringSessionSynchronization.cs" />
<exclude name="../Spring.Data.NHibernate/Data/**/LocalSessionFactoryObject.cs" />

View File

@@ -0,0 +1,49 @@
using System;
using NUnit.Framework;
using NhCfg = NHibernate.Cfg;
using NHibernate;
using System.Collections;
using Spring.Data.Common;
using NHibernate.Dialect;
using Spring.Context.Support;
using NHibernate.Connection;
using NHibernate.Driver;
using Spring.Data.NHibernate.Bytecode;
namespace Spring.Data.NHibernate
{
[TestFixture]
public class DelegatingLocalSessionFactoryObjectTests
{
[Test]
public void CanSetConfigurationAndProperties()
{
IDbProvider dbProvider = DbProviderFactory.GetDbProvider("System.Data.SqlClient");
dbProvider.ConnectionString = "Data Source=(local);Database=Spring;Trusted_Connection=false";
DelegatingLocalSessionFactoryObject lsfo = new DelegatingLocalSessionFactoryObject();
lsfo.DbProvider = dbProvider;
lsfo.ApplicationContext = new StaticApplicationContext();
IDictionary properties = new Hashtable();
properties.Add(NhCfg.Environment.Dialect, typeof(MsSql2000Dialect).AssemblyQualifiedName);
properties.Add(NhCfg.Environment.ConnectionDriver, typeof(SqlClientDriver).AssemblyQualifiedName);
properties.Add(NhCfg.Environment.ConnectionProvider, typeof(DriverConnectionProvider).AssemblyQualifiedName);
properties.Add(NhCfg.Environment.Hbm2ddlKeyWords, "none");
lsfo.HibernateProperties = properties;
lsfo.AfterPropertiesSet();
Assert.IsNotNull(lsfo.Configuration);
Assert.AreEqual(lsfo.Configuration.Properties[NhCfg.Environment.ConnectionProvider], typeof(DriverConnectionProvider).AssemblyQualifiedName);
Assert.AreEqual(lsfo.Configuration.Properties[NhCfg.Environment.ConnectionDriver], typeof(SqlClientDriver).AssemblyQualifiedName);
Assert.AreEqual(lsfo.Configuration.Properties[NhCfg.Environment.Dialect], typeof(MsSql2000Dialect).AssemblyQualifiedName);
Assert.AreEqual(lsfo.Configuration.Properties[NhCfg.Environment.ProxyFactoryFactoryClass], typeof(ProxyFactoryFactory).AssemblyQualifiedName);
}
}
}

View File

@@ -98,6 +98,7 @@
<Link>Data\NHibernate\TestObject.cs</Link>
</Compile>
<Compile Include="AssemblyInfo.cs" />
<Compile Include="Data\NHibernate\DelegatingLocalSessionFactoryObjectTests.cs" />
<Compile Include="HibernateCompilerOptionsTests.cs" />
<Compile Include="HibernateExceptionTests.cs" />
</ItemGroup>