#region License
/*
* Copyright 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
#region Imports
using System;
using System.Data;
using System.Reflection;
using NHibernate;
using NHibernate.Transaction;
using Spring.Core.TypeResolution;
using Spring.Dao;
using Spring.Data.Common;
using Spring.Data.Support;
using Spring.Objects.Factory;
using Spring.Transaction;
using Spring.Transaction.Support;
using Spring.Util;
using HibernateTransactionExceptionNHibernate.TransactionException;
#endregion
namespace Spring.Data.NHibernate{
/// <summary>
/// PlatformTransactionManager implementation for a single Hibernate SessionFactory.
/// Binds a Hibernate Session from the specified factory to the thread, potentially
/// allowing for one thread Session per factory
/// </summary>
/// <remarks>
/// SessionFactoryUtils and HibernateTemplate are aware of thread-bound Sessions and participate in such
/// transactions automatically. Using either of those is required for Hibernate
/// access code that needs to support this transaction handling mechanism.
/// <para>
/// Supports custom isolation levels at the start of the transaction
/// , and timeouts that get applied as appropriate
/// Hibernate query timeouts. To support the latter, application code must either use
/// <code>HibernateTemplate</code> (which by default applies the timeouts) or call
/// <code>SessionFactoryUtils.applyTransactionTimeout</code> for each created
/// Hibernate Query object.
/// </para>
/// <para>Note that you can specify a Spring IDbProvider instance which if shared with
/// a corresponding instance of AdoTemplate will allow for mixing ADO.NET/NHibernate
/// operations within a single transaction.</para>
/// </remarks>
/// <author>Mark Pollack (.NET)</author>
public class HibernateTransactionManager : AbstractPlatformTransactionManager, IResourceTransactionManager, IObjectFactoryAware, IInitializingObject
{
#region Fields
private ISessionFactory sessionFactory;
private IDbProvider dbProvider;
private bool autodetectDbProvider = true;
private Object entityInterceptor;
private IAdoExceptionTranslator adoExceptionTranslator;
private IAdoExceptionTranslator defaultExceptionTranslator;
/// <summary>
/// Just needed for entityInterceptorBeanName.
/// </summary>
private IObjectFactory objectFactory;
#endregion
#region Constructor (s)
/// <summary>
/// Initializes a new instance of the <see cref="HibernateTransactionManager"/> class.
/// </summary>
public HibernateTransactionManager()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="HibernateTransactionManager"/> class.
/// </summary>
/// <param name="sessionFactory">The session factory.</param>
public HibernateTransactionManager(ISessionFactory sessionFactory)
{
this.sessionFactory = sessionFactory;
AfterPropertiesSet();
}
#endregion
#region Properties
/// <summary>
/// Gets or sets the db provider.
/// </summary>
/// <value>The db provider.</value>
public IDbProvider DbProvider
{
get { return dbProvider; }
set { dbProvider = value; }
}
/// <summary>
/// Gets or sets a Hibernate entity interceptor that allows to inspect and change
/// property values before writing to and reading from the database.
/// When getting, return the current Hibernate entity interceptor, or <code>null</code> if none.
/// </summary>
/// <value>The entity interceptor.</value>
/// <remarks>
/// Resolves an entity interceptor object name via the object factory,
/// if necessary.
/// Will get applied to any new Session created by this transaction manager.
/// Such an interceptor can either be set at the SessionFactory level,
/// i.e. on LocalSessionFactoryObject, or at the Session level, i.e. on
/// HibernateTemplate, HibernateInterceptor, and HibernateTransactionManager.
/// It's preferable to set it on LocalSessionFactoryObject or HibernateTransactionManager
/// to avoid repeated configuration and guarantee consistent behavior in transactions.
/// </remarks>
/// <exception cref="InvalidOperationException">If object factory is null and need to get entity interceptor via object name.</exception>
public IInterceptor EntityInterceptor
{
get
{
if (this.entityInterceptor is IInterceptor)
{
return (IInterceptor) entityInterceptor;
}
else if (this.entityInterceptor is string)
{
if (this.objectFactory == null)
{
throw new InvalidOperationException ("Cannot get entity interceptor via object name if no object factory set");
}
String objectName = (String) this.entityInterceptor;
return (IInterceptor) this.objectFactory.GetObject(objectName, typeof(IInterceptor));
}
else
{
return null;
}
}
set
{
entityInterceptor = value;
}
}
/// <summary>
/// Sets the object name of a Hibernate entity interceptor that
/// allows to inspect and change property values before writing to and reading from the database.
/// </summary>
/// <value>The name of the entity interceptor object.</value>
/// <remarks>
/// Will get applied to any new Session created by this transaction manager.
/// <p>Requires the object factory to be known, to be able to resolve the object
/// name to an interceptor instance on session creation. Typically used for
/// prototype interceptors, i.e. a new interceptor instance per session.
/// </p>
/// <p>Can also be used for shared interceptor instances, but it is recommended
/// to set the interceptor reference directly in such a scenario.
/// </p>
/// </remarks>
public string EntityInterceptorObjectName
{
set
{
entityInterceptor = value;
}
}
/// <summary>
/// Gets or sets the ADO.NET exception translator for this transaction manager.
/// </summary>
/// <remarks>
/// Applied to ADO.NET Exceptions (wrapped by Hibernate's ADOException)
/// </remarks>
/// <value>The ADO exception translator.</value>
public IAdoExceptionTranslator AdoExceptionTranslator
{
get { return adoExceptionTranslator; }
set { adoExceptionTranslator = value; }
}
/// <summary>
/// Gets the default IAdoException translator, lazily creating it if nece
/// </summary>
/// <value>The default IAdoException translator.</value>
public IAdoExceptionTranslator DefaultAdoExceptionTranslator
{
get
{ lock(this)
{
if (defaultExceptionTranslator == null)
{
if (dbProvider != null)
{
defaultExceptionTranslator = new ErrorCodeExceptionTranslator(dbProvider);
}
else
{
defaultExceptionTranslator = SessionFactoryUtils.NewAdoExceptionTranslator(SessionFactory);
}
}
return defaultExceptionTranslator;
}
}
}
/// <summary>
/// Gets or sets the SessionFactory that this instance should manage transactions for.
/// </summary>
/// <value>The session factory.</value>
public ISessionFactory SessionFactory
{
get { return sessionFactory; }
set { sessionFactory = value; }
}
/// <summary>
/// Gets the resource factory that this transaction manager operates on,
/// For the HibenratePlatformTransactionManager this the SessionFactory
/// </summary>
/// <value>The SessionFactory.</value>
public object ResourceFactory
{
get { return sessionFactory; }
}
/// <summary>
/// Set whether to autodetect a ADO.NET connection used by the Hibernate SessionFactory,
/// if set via LocalSessionFactoryObject's <code>DbProvider</code>. Default is "true".
/// </summary>
/// <value>
/// <c>true</c> if [autodetect data source]; otherwise, <c>false</c>.
/// </value>
/// <remarks>
/// <p>Can be turned off to deliberately ignore an available IDbProvider,
/// to not expose Hibernate transactions as ADO.NET transactions for that IDbProvider.
/// </p>
/// </remarks>
public bool AutodetectDbProvider
{
set { autodetectDbProvider = value; }
}
#endregion
#region Methods
#endregion
/// <summary>
/// The object factory just needs to be known for resolving entity interceptor
/// It does not need to be set for any other mode of operation.
/// </summary>
/// <value>
/// Owning <see cref="T:Spring.Objects.Factory.IObjectFactory"/>
/// (may not be <see langword="null"/>). The object can immediately
/// call methods on the factory.
/// </value>
public IObjectFactory ObjectFactory
{
set
{
objectFactory = value;
}
}
/// <summary>
/// Return the current transaction object.
/// </summary>
/// <returns>The current transaction object.</returns>
/// <exception cref="Spring.Transaction.CannotCreateTransactionException">
/// If transaction support is not available.
/// </exception>
/// <exception cref="Spring.Transaction.TransactionException">
/// In the case of lookup or system errors.
/// </exception>
protected override object DoGetTransaction()
{
HibernateTransactionObject txObject = new HibernateTransactionObject();
txObject.SavepointAllowed = NestedTransactionsAllowed;
if (TransactionSynchronizationManager.HasResource(SessionFactory))
{
SessionHolder sessionHolder =
(SessionHolder) TransactionSynchronizationManager.GetResource(SessionFactory);
if (log.IsDebugEnabled)
{
log.Debug("Found thread-bound Session [" + sessionHolder.Session +
"] for Hibernate transaction");
}
txObject.SetSessionHolder(sessionHolder, false);
if (DbProvider != null)
{
ConnectionHolder conHolder = (ConnectionHolder)
TransactionSynchronizationManager.GetResource(DbProvider);
txObject.ConnectionHolder = conHolder;
}
}
return txObject;
}
/// <summary>
/// Check if the given transaction object indicates an existing,
/// i.e. already begun, transaction.
/// </summary>
/// <param name="transaction">
/// Transaction object returned by
/// <see cref="Spring.Transaction.Support.AbstractPlatformTransactionManager.DoGetTransaction"/>.
/// </param>
/// <returns>True if there is an existing transaction.</returns>
/// <exception cref="Spring.Transaction.TransactionException">
/// In the case of system errors.
/// </exception>
protected override bool IsExistingTransaction(object transaction)
{
return ((HibernateTransactionObject) transaction).HasTransaction();
}
/// <summary>
/// Begin a new transaction with the given transaction definition.
/// </summary>
/// <param name="transaction">
/// Transaction object returned by
/// <see cref="Spring.Transaction.Support.AbstractPlatformTransactionManager.DoGetTransaction"/>.
/// </param>
/// <param name="definition">
/// <see cref="Spring.Transaction.ITransactionDefinition"/> instance, describing
/// propagation behavior, isolation level, timeout etc.
/// </param>
/// <remarks>
/// Does not have to care about applying the propagation behavior,
/// as this has already been handled by this abstract manager.
/// </remarks>
/// <exception cref="Spring.Transaction.TransactionException">
/// In the case of creation or system errors.
/// </exception>
protected override void DoBegin(object transaction, ITransactionDefinition definition)
{
HibernateTransactionObject txObject = (HibernateTransactionObject) transaction;
if (DbProvider != null && TransactionSynchronizationManager.HasResource(DbProvider)
&& !txObject.ConnectionHolder.SynchronizedWithTransaction)
{
throw new IllegalTransactionStateException(
"Pre-bound ADO.NET Connection found - HibernateTransactionManager does not support " +
"running within AdoTransactionManager if told to manage the DbProvider itself. " +
"It is recommended to use a single HibernateTransactionManager for all transactions " +
"on a single DbProvider, no matter whether Hibernate or ADO.NET access.");
}
ISession session = null;
try
{
if (txObject.SessionHolder == null || txObject.SessionHolder.SynchronizedWithTransaction)
{
IInterceptor interceptor = EntityInterceptor;
ISession newSession = (interceptor != null ?
SessionFactory.OpenSession(interceptor) : SessionFactory.OpenSession());
if (log.IsDebugEnabled)
{
log.Debug("Opened new Session [" + newSession + "] for Hibernate transaction");
}
txObject.SetSessionHolder(new SessionHolder(newSession), true);
}
txObject.SessionHolder.SynchronizedWithTransaction = true;
session = txObject.SessionHolder.Session;
IDbConnection con = session.Connection;
//TODO isolation level mgmt
//IsolationLevel previousIsolationLevel =
if (definition.ReadOnly && txObject.NewSessionHolder)
{
// Just set to NEVER in case of a new Session for this transaction.
session.FlushMode = FlushMode.Never;
}
if (!definition.ReadOnly && !txObject.NewSessionHolder)
{
// We need AUTO or COMMIT for a non-read-only transaction.
FlushMode flushMode = session.FlushMode;
if (FlushMode.Never == flushMode)
{
session.FlushMode = FlushMode.Auto;
txObject.SessionHolder.PreviousFlushMode = flushMode;
}
}
// Add the Hibernate transaction to the session holder.
// for now pass in tx options isolation level.
ITransaction hibernateTx = session.BeginTransaction(definition.TransactionIsolationLevel);
IDbTransaction adoTx = GetIDbTransaction(hibernateTx);
// Add the Hibernate transaction to the session holder.
txObject.SessionHolder.Transaction = hibernateTx;
// Register transaction timeout.
int timeout = DetermineTimeout(definition);
if (timeout != DefaultTransactionDefinition.TIMEOUT_DEFAULT)
{
txObject.SessionHolder.TimeoutInSeconds = timeout;
}
// Register the Hibernate Session's ADO.NET Connection/TX pair for the DbProvider, if set.
if (DbProvider != null)
{
//investigate passing null for tx.
ConnectionHolder conHolder = new ConnectionHolder(con, adoTx);
if (timeout != DefaultTransactionDefinition.TIMEOUT_DEFAULT)
{
conHolder.TimeoutInMillis = definition.TransactionTimeout;
}
if (log.IsDebugEnabled)
{
log.Debug("Exposing Hibernate transaction as ADO transaction [" + con + "]");
}
TransactionSynchronizationManager.BindResource(DbProvider, conHolder);
txObject.ConnectionHolder = conHolder;
}
// Bind the session holder to the thread.
if (txObject.NewSessionHolder)
{
TransactionSynchronizationManager.BindResource(SessionFactory, txObject.SessionHolder);
}
} catch (Exception ex)
{
SessionFactoryUtils.CloseSession(session);
throw new CannotCreateTransactionException("Could not open Hibernate Session for transaction", ex);
}
}
/// <summary>
/// Suspend the resources of the current transaction.
/// </summary>
/// <param name="transaction">
/// Transaction object returned by
/// <see cref="Spring.Transaction.Support.AbstractPlatformTransactionManager.DoGetTransaction"/>.
/// </param>
/// <returns>
/// An object that holds suspended resources (will be kept unexamined for passing it into
/// <see cref="Spring.Transaction.Support.AbstractPlatformTransactionManager.DoResume"/>.)
/// </returns>
/// <remarks>
/// Transaction synchronization will already have been suspended.
/// </remarks>
/// <exception cref="Spring.Transaction.IllegalTransactionStateException">
/// If suspending is not supported by the transaction manager implementation.
/// </exception>
/// <exception cref="Spring.Transaction.TransactionException">
/// in case of system errors.
/// </exception>
protected override object DoSuspend(object transaction)
{
HibernateTransactionObject txObject = (HibernateTransactionObject) transaction;
txObject.SetSessionHolder(null, false);
SessionHolder sessionHolder =
(SessionHolder) TransactionSynchronizationManager.UnbindResource(SessionFactory);
ConnectionHolder connectionHolder = null;
if (DbProvider != null)
{
connectionHolder = (ConnectionHolder) TransactionSynchronizationManager.UnbindResource(DbProvider);
}
return new SuspendedResourcesHolder(sessionHolder, connectionHolder);
}
/// <summary>
/// Resume the resources of the current transaction.
/// </summary>
/// <param name="transaction">
/// Transaction object returned by
/// <see cref="Spring.Transaction.Support.AbstractPlatformTransactionManager.DoGetTransaction"/>.
/// </param>
/// <param name="suspendedResources">
/// The object that holds suspended resources as returned by
/// <see cref="Spring.Transaction.Support.AbstractPlatformTransactionManager.DoSuspend"/>.
/// </param>
/// <remarks>
/// Transaction synchronization will be resumed afterwards.
/// </remarks>
/// <exception cref="Spring.Transaction.IllegalTransactionStateException">
/// If suspending is not supported by the transaction manager implementation.
/// </exception>
/// <exception cref="Spring.Transaction.TransactionException">
/// In the case of system errors.
/// </exception>
protected override void DoResume(object transaction, object suspendedResources)
{
SuspendedResourcesHolder resourcesHolder = (SuspendedResourcesHolder) suspendedResources;
if (TransactionSynchronizationManager.HasResource(SessionFactory))
{
// From non-transactional code running in active transaction synchronization
// -> can be safely removed, will be closed on transaction completion.
TransactionSynchronizationManager.UnbindResource(SessionFactory);
}
TransactionSynchronizationManager.BindResource(SessionFactory, resourcesHolder.SessionHolder);
if (DbProvider != null)
{
TransactionSynchronizationManager.BindResource(DbProvider, resourcesHolder.ConnectionHolder);
}
}
/// <summary>
/// Perform an actual commit on the given transaction.
/// </summary>
/// <param name="status">The status representation of the transaction.</param>
/// <remarks>
/// <p>
/// An implementation does not need to check the rollback-only flag.
/// </p>
/// </remarks>
/// <exception cref="Spring.Transaction.TransactionException">
/// In the case of system errors.
/// </exception>
protected override void DoCommit(DefaultTransactionStatus status)
{
HibernateTransactionObject txObject = (HibernateTransactionObject) status.Transaction;
if (status.Debug)
{
log.Debug("Committing Hibernate transaction on Session [" +
txObject.SessionHolder.Session + "]");
}
try
{
txObject.SessionHolder.Transaction.Commit();
}
// Note, unfortunate collision of namespaces/classname for NHibernate.TransactionException
// and Spring.Data.NHibernate requires this wierd construct.
catch (Exception ex)
{
Type nhibTxExceptiontype = TypeResolutionUtils.ResolveType("NHibernate.TransactionException, NHibernate");
if (ex.GetType().Equals(nhibTxExceptiontype))
{
// assumably from commit call to the underlying ADO.NET connection
throw new TransactionSystemException("Could not commit Hibernate transaction", ex);
}
HibernateException hibEx = ex as HibernateException;
if (hibEx != null)
{
// assumably failed to flush changes to database
throw ConvertHibernateAccessException(hibEx);
}
throw;
}
}
/// <summary>
/// Perform an actual rollback on the given transaction.
/// </summary>
/// <param name="status">The status representation of the transaction.</param>
/// <remarks>
/// An implementation does not need to check the new transaction flag.
/// </remarks>
/// <exception cref="Spring.Transaction.TransactionException">
/// In the case of system errors.
/// </exception>
protected override void DoRollback(DefaultTransactionStatus status)
{
HibernateTransactionObject txObject = (HibernateTransactionObject) status.Transaction;
if (status.Debug)
{
log.Debug("Rolling back Hibernate transaction on Session [" +
txObject.SessionHolder.Session + "]");
}
try
{
txObject.SessionHolder.Transaction.Rollback();
}
catch (HibernateTransactionException ex)
{
throw new TransactionSystemException("Could not roll back Hibernate transaction", ex);
}
catch (HibernateException ex)
{
// Shouldn't really happen, as a rollback doesn't cause a flush.
throw ConvertHibernateAccessException(ex);
}
finally
{
if (!txObject.NewSessionHolder)
{
// Clear all pending inserts/updates/deletes in the Session.
// Necessary for pre-bound Sessions, to avoid inconsistent state.
txObject.SessionHolder.Session.Clear();
}
}
}
/// <summary>
/// Set the given transaction rollback-only. Only called on rollback
/// if the current transaction takes part in an existing one.
/// </summary>
/// <param name="status">The status representation of the transaction.</param>
/// <exception cref="Spring.Transaction.TransactionException">
/// In the case of system errors.
/// </exception>
protected override void DoSetRollbackOnly(DefaultTransactionStatus status)
{
HibernateTransactionObject txObject = (HibernateTransactionObject) status.Transaction;
if (status.Debug)
{
log.Debug("Setting Hibernate transaction on Session [" +
txObject.SessionHolder.Session + "] rollback-only");
}
txObject.SetRollbackOnly();
}
/// <summary>
/// Gets the ADO.NET IDbTransaction object from the NHibernate ITransaction object.
/// </summary>
/// <param name="hibernateTx">The hibernate transaction.</param>
/// <returns>The ADO.NET transaction. Null if could not get the transaction. Warning
/// messages will be logged in that case.</returns>
protected IDbTransaction GetIDbTransaction(ITransaction hibernateTx)
{
AdoTransaction hibernateAdoTx = hibernateTx as AdoTransaction;
IDbTransaction adoTransaction = null;
if (hibernateAdoTx != null)
{
try
{
FieldInfo fi = hibernateAdoTx.GetType().GetField("trans", BindingFlags.Instance | BindingFlags.NonPublic);
adoTransaction = fi.GetValue(hibernateAdoTx) as IDbTransaction;
}
catch (Exception e)
{
log.Warn("Could not extract IDbTransaction from Hibernate AdoTransaction using field name trans.", e);
}
}
else
{
log.Warn("Hibernate ITransaction not of expected type AdoTransaction. Could not extract IDbTransaction from Hibernate AdoTransaction.");
}
return adoTransaction;
}
/// <summary>
/// Convert the given HibernateException to an appropriate exception from
/// the Spring.Dao hierarchy. Can be overridden in subclasses.
/// </summary>
/// <param name="ex">The HibernateException that occured.</param>
/// <returns>The corresponding DataAccessException instance</returns>
protected virtual DataAccessException ConvertHibernateAccessException(HibernateException ex)
{
if (AdoExceptionTranslator != null && ex is ADOException)
{
return ConvertAdoAccessException((ADOException) ex, AdoExceptionTranslator);
}
else if (ex is ADOException)
{
return ConvertAdoAccessException((ADOException)ex, DefaultAdoExceptionTranslator);
}
return SessionFactoryUtils.ConvertHibernateAccessException(ex);
}
/// <summary>
/// Convert the given ADOException to an appropriate exception from the
/// the Spring.Dao hierarchy. Can be overridden in subclasses.
/// </summary>
/// <param name="ex">The ADOException that occured, wrapping the underlying
/// ADO.NET thrown exception.</param>
/// <param name="translator">The translator to convert hibernate ADOExceptions.</param>
/// <returns>
/// The corresponding DataAccessException instance
/// </returns>
protected virtual DataAccessException ConvertAdoAccessException(ADOException ex, IAdoExceptionTranslator translator)
{
return translator.Translate("Hibernate flusing: " + ex.Message, null, ex.InnerException);
}
/// <summary>
/// Cleanup resources after transaction completion.
/// </summary>
/// <param name="transaction">Transaction object returned by
/// <see cref="M:Spring.Transaction.Support.AbstractPlatformTransactionManager.DoGetTransaction"/>.</param>
/// <remarks>
/// <note>
/// This implemenation unbinds the SessionFactory and
/// DbProvider from thread local storage and closes the
/// ISession.
/// </note>
/// <p>
/// Called after <see cref="M:Spring.Transaction.Support.AbstractPlatformTransactionManager.DoCommit(Spring.Transaction.Support.DefaultTransactionStatus)"/>
/// and
/// <see cref="M:Spring.Transaction.Support.AbstractPlatformTransactionManager.DoRollback(Spring.Transaction.Support.DefaultTransactionStatus)"/>
/// execution on any outcome.
/// </p>
/// <p>
/// Should not throw any exceptions but just issue warnings on errors.
/// </p>
/// </remarks>
protected override void DoCleanupAfterCompletion( object transaction )
{
HibernateTransactionObject txObject = (HibernateTransactionObject) transaction;
// Remove the session holder from the thread.
if (txObject.NewSessionHolder)
{
TransactionSynchronizationManager.UnbindResource(SessionFactory);
}
// Remove the ADO.NET connection holder from the thread, if exposed.
if (DbProvider != null)
{
TransactionSynchronizationManager.UnbindResource(DbProvider);
}
/*
try
{
//TODO investigate isolation level settings...
//IDbConnection con = txObject.SessionHolder.Session.Connection;
//AdoUtils.ResetConnectionAfterTransaction(con, txObject.PreviousIsolationLevel);
}
catch (HibernateException ex)
{
log.Info("Could not access ADO.NET IDbConnection of Hibernate Session", ex);
}
*/
ISession session = txObject.SessionHolder.Session;
if (txObject.NewSessionHolder)
{
if (log.IsDebugEnabled)
{
log.Debug("Closing Hibernate Session [" + session + "] after transaction");
}
SessionFactoryUtils.CloseSessionOrRegisterDeferredClose(session, SessionFactory);
}
else
{
if (log.IsDebugEnabled)
{
log.Debug("Not closing pre-bound Hibernate Session [" + session + "] after transaction");
}
if (txObject.SessionHolder.AssignedPreviousFlushMode)
{
session.FlushMode = txObject.SessionHolder.PreviousFlushMode;
}
}
txObject.SessionHolder.Clear();
}
private class HibernateTransactionObject : AdoTransactionObjectSupport
{
private SessionHolder sessionHolder;
private bool newSessionHolder;
public void SetSessionHolder(SessionHolder sessionHolder, bool newSessionHolder)
{
this.sessionHolder = sessionHolder;
this.newSessionHolder = newSessionHolder;
}
public SessionHolder SessionHolder
{
get
{
return sessionHolder;
}
}
public bool NewSessionHolder
{
get
{
return newSessionHolder;
}
}
public bool HasTransaction()
{
return (this.sessionHolder != null && this.sessionHolder.Transaction != null);
}
public void SetRollbackOnly()
{
SessionHolder.RollbackOnly = true;
if (ConnectionHolder != null)
{
ConnectionHolder.RollbackOnly = true;
}
}
/// <summary>
/// Return whether the transaction is internally marked as rollback-only.
/// </summary>
/// <value></value>
/// <returns>True of the transaction is marked as rollback-only.</returns>
public override bool RollbackOnly
{
get
{
return SessionHolder.RollbackOnly ||
(ConnectionHolder != null && ConnectionHolder.RollbackOnly);
}
}
}
private class SuspendedResourcesHolder
{
private readonly SessionHolder sessionHolder;
private readonly ConnectionHolder connectionHolder;
public SuspendedResourcesHolder(SessionHolder sessionHolder, ConnectionHolder conHolder)
{
this.sessionHolder = sessionHolder;
this.connectionHolder = conHolder;
}
public SessionHolder SessionHolder
{
get
{
return sessionHolder;
}
}
public ConnectionHolder ConnectionHolder
{
get
{
return connectionHolder;
}
}
}
/// <summary>
/// Invoked by an <see cref="Spring.Objects.Factory.IObjectFactory"/>
/// after it has injected all of an object's dependencies.
/// </summary>
/// <remarks>
/// <p>
/// This method allows the object instance to perform the kind of
/// initialization only possible when all of it's dependencies have
/// been injected (set), and to throw an appropriate exception in the
/// event of misconfiguration.
/// </p>
/// <p>
/// Please do consult the class level documentation for the
/// <see cref="Spring.Objects.Factory.IObjectFactory"/> interface for a
/// description of exactly <i>when</i> this method is invoked. In
/// particular, it is worth noting that the
/// <see cref="Spring.Objects.Factory.IObjectFactoryAware"/>
/// and <see cref="Spring.Context.IApplicationContextAware"/>
/// callbacks will have been invoked <i>prior</i> to this method being
/// called.
/// </p>
/// </remarks>
/// <exception cref="System.ArgumentException">
/// In the event of misconfiguration (such as the failure to set a
/// required property) or if initialization fails.
/// </exception>
public void AfterPropertiesSet()
{
if (SessionFactory == null) {
throw new ArgumentException("sessionFactory is required");
}
if (this.entityInterceptor is string && this.objectFactory == null) {
throw new ArgumentException ("objectFactory is required for entityInterceptorBeanName");
}
// Try to derive a DbProvider given the SessionFactory.
if (this.autodetectDbProvider && DbProvider == null) {
IDbProvider sfDbProvider = SessionFactoryUtils.GetDbProvider(SessionFactory);
if (sfDbProvider != null) {
// Use the SessionFactory's DataSource for exposing transactions to ADO.NET code.
if (log.IsInfoEnabled) {
log.Info("Derived DbProvider [" + sfDbProvider.DbMetadata.ProductName +
"] of Hibernate SessionFactory for HibernateTransactionManager");
}
DbProvider = sfDbProvider;
}
else
{
log.Info("Could not auto detect DbProvider from SessionFactory configuration");
}
}
}
}
}
|