HibernateAccessor.cs :  » Inversion-of-Control-Dependency-Injection » Spring.net » Spring » Data » NHibernate » C# / CSharp Open Source

Home
C# / CSharp Open Source
1.2.6.4 mono .net core
2.2.6.4 mono core
3.Aspect Oriented Frameworks
4.Bloggers
5.Build Systems
6.Business Application
7.Charting Reporting Tools
8.Chat Servers
9.Code Coverage Tools
10.Content Management Systems CMS
11.CRM ERP
12.Database
13.Development
14.Email
15.Forum
16.Game
17.GIS
18.GUI
19.IDEs
20.Installers Generators
21.Inversion of Control Dependency Injection
22.Issue Tracking
23.Logging Tools
24.Message
25.Mobile
26.Network Clients
27.Network Servers
28.Office
29.PDF
30.Persistence Frameworks
31.Portals
32.Profilers
33.Project Management
34.RSS RDF
35.Rule Engines
36.Script
37.Search Engines
38.Sound Audio
39.Source Control
40.SQL Clients
41.Template Engines
42.Testing
43.UML
44.Web Frameworks
45.Web Service
46.Web Testing
47.Wiki Engines
48.Windows Presentation Foundation
49.Workflows
50.XML Parsers
C# / C Sharp
C# / C Sharp by API
C# / CSharp Tutorial
C# / CSharp Open Source » Inversion of Control Dependency Injection » Spring.net 
Spring.net » Spring » Data » NHibernate » HibernateAccessor.cs
#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.Collections;
using System.Reflection;
using AopAlliance.Intercept;
using Common.Logging;
using NHibernate;
using NHibernate.Impl;
using NHibernate.Type;
using Spring.Core.TypeResolution;
using Spring.Dao;
using Spring.Data.Support;
using Spring.Objects.Factory;
using IInterceptorNHibernate.IInterceptor;
using ICriteriaNHibernate.ICriteria;

#endregion

namespace Spring.Data.NHibernate{
  /// <summary>
    /// Base class for HibernateTemplate defining common
    /// properties like SessionFactory and flushing behavior.
  /// </summary>
  /// <remarks>
  /// <p>Not intended to be used directly. See HibernateTemplate.
  /// </p>
  /// </remarks>
  /// <author>Mark Pollack (.NET)</author>
  public abstract class HibernateAccessor : IInitializingObject, IObjectFactoryAware
  {

        private Type criteriaType;
      
      #region Constants

    /// <summary>
    /// The <see cref="ILog"/> instance for this class. 
    /// </summary>
    private readonly ILog log = LogManager.GetLogger(typeof (HibernateAccessor));
      
      #endregion

    #region Constructor (s)

      /// <summary>
    /// Initializes a new instance of the <see cref="HibernateAccessor"/> class.
        /// </summary>
    public HibernateAccessor()
    {
          
    }

    #endregion

      
        #region Properties

        /// <summary>
        /// Gets or sets if a new Session should be created when no transactional Session
        /// can be found for the current thread.
        /// </summary>
        /// <value>
        ///   <c>true</c> if allowed to create non-transaction session;
        /// otherwise, <c>false</c>.
        /// </value>
        /// <remarks>
        ///   <p>HibernateTemplate is aware of a corresponding Session bound to the
        /// current thread, for example when using HibernateTransactionManager.
        /// If allowCreate is true, a new non-transactional Session will be created
        /// if none found, which needs to be closed at the end of the operation.
        /// If false, an InvalidOperationException will get thrown in this case.
        /// </p>
        /// </remarks>
        public abstract bool AllowCreate
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets a value indicating whether to always
        /// use a new Hibernate Session for this template.
        /// </summary>
        /// <value><c>true</c> if always use new session; otherwise, <c>false</c>.</value>
        /// <remarks>
        ///   <p>
        /// Default is "false"; if activated, all operations on this template will
        /// work on a new NHibernate ISession even in case of a pre-bound ISession
        /// (for example, within a transaction).
        /// </p>
        ///   <p>Within a transaction, a new NHibernate ISession used by this template
        /// will participate in the transaction through using the same ADO.NET
        /// Connection. In such a scenario, multiple Sessions will participate
        /// in the same database transaction.
        /// </p>
        ///   <p>Turn this on for operations that are supposed to always execute
        /// independently, without side effects caused by a shared NHibernate ISession.
        /// </p>
        /// </remarks>
        public abstract bool AlwaysUseNewSession
        {
            get;
            set;
        }

        /// <summary>
        /// Set whether to expose the native Hibernate Session to IHibernateCallback
        /// code. Default is "false": a Session proxy will be returned,
        /// suppressing <code>close</code> calls and automatically applying
        /// query cache settings and transaction timeouts.
        /// </summary>
        /// <value><c>true</c> if expose native session; otherwise, <c>false</c>.</value>
        public abstract bool ExposeNativeSession
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the template flush mode.
        /// </summary>
        /// <remarks>
        /// Default is Auto. Will get applied to any <b>new</b> ISession
        /// created by the template.
        /// </remarks>
        /// <value>The template flush mode.</value>
        public abstract TemplateFlushMode TemplateFlushMode
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the entity interceptor that allows to inspect and change
        /// property values before writing to and reading from the database.
        /// </summary>
        /// <remarks>
        /// Will get applied to any <b>new</b> ISession created by this object.
        /// <p>Such an interceptor can either be set at the ISessionFactory level,
        /// i.e. on LocalSessionFactoryObject, or at the ISession 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.
        /// </p>
        /// </remarks>
        /// <value>The interceptor.</value>
        public abstract IInterceptor EntityInterceptor
        {
            get;
            set;
        }

        /// <summary>
        /// Set 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>
        /// <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>
        /// <value>The name of the entity interceptor in the object factory/application context.</value>
        public abstract string EntityInterceptorObjectName
        {
            set;
        }

        /// <summary>
        /// Gets or sets the session factory that should be used to create
        /// NHibernate ISessions.
        /// </summary>
        /// <value>The session factory.</value>
        public abstract ISessionFactory SessionFactory
        {
            get;
            set;
        }

        /// <summary>
        /// Set the object factory instance.
        /// </summary>
        /// <value>The object factory instance.</value>
        public abstract IObjectFactory ObjectFactory
        {
            set;
        }

        /// <summary>
        /// Gets or sets a value indicating whether to 
        /// cache all queries executed by this template.
        /// </summary>
        /// <remarks>
        /// If this is true, all IQuery and ICriteria objects created by
        /// this template will be marked as cacheable (including all
        /// queries through find methods).
        /// <p>To specify the query region to be used for queries cached
        /// by this template, set the QueryCacheRegion property.
        /// </p>
        /// </remarks>
        /// <value><c>true</c> if cache queries; otherwise, <c>false</c>.</value>
        public abstract bool CacheQueries
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the name of the cache region for queries executed by this template.
        /// </summary>
        /// <remarks>
        /// If this is specified, it will be applied to all IQuery and ICriteria objects
        /// created by this template (including all queries through find methods).
        /// <p>The cache region will not take effect unless queries created by this
        /// template are configured to be cached via the CacheQueries property.
        /// </p>
        /// </remarks>
        /// <value>The query cache region.</value>
        public abstract string QueryCacheRegion
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the fetch size for this HibernateTemplate.
        /// </summary>
        /// <value>The size of the fetch.</value>
        /// <remarks>This is important for processing
        /// large result sets: Setting this higher than the default value will increase
        /// processing speed at the cost of memory consumption; setting this lower can
        /// avoid transferring row data that will never be read by the application.
        /// <p>Default is 0, indicating to use the driver's default.</p>
        /// </remarks>
        public abstract int FetchSize
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the maximum number of rows for this HibernateTemplate.
        /// </summary>
        /// <value>The max results.</value>
        /// <remarks>
        /// This is important
        /// for processing subsets of large result sets, avoiding to read and hold
        /// the entire result set in the database or in the ADO.NET driver if we're
        /// never interested in the entire result in the first place (for example,
        /// when performing searches that might return a large number of matches).
        /// <p>Default is 0, indicating to use the driver's default.</p>
        /// </remarks>
        public abstract int MaxResults
        {
            get;
            set;
        }

        /// <summary>
        /// Set the ADO.NET exception translator for this instance.
        /// Applied to System.Data.Common.DbException (or provider specific exception type
        /// in .NET 1.1) thrown by callback code, be it direct
        /// DbException or wrapped Hibernate ADOExceptions.
        /// <p>The default exception translator is either a ErrorCodeExceptionTranslator
        /// if a DbProvider is available, or a FalbackExceptionTranslator otherwise
        /// </p>
        /// </summary>
        /// <value>The ADO exception translator.</value>
        public abstract IAdoExceptionTranslator AdoExceptionTranslator
        {
            set;
            get;
        }
      
        /// <summary>
        /// Gets a Session for use by this template.
        /// </summary>
        /// <value>The session.</value>
        /// <remarks>
        /// - Returns a new Session in case of "alwaysUseNewSession" (using the same ADO.NET connection as a transaction Session, if applicable)
        /// - a pre-bound Session in case of "AllowCreate" is set to false (not the default) 
        /// - or a pre-bound Session or new Session if no transactional or other pre-bound Session exists.
        /// </remarks>
        protected ISession Session
        {
            get
            {
                if (AlwaysUseNewSession)
                {
                    return SessionFactoryUtils.GetNewSession(SessionFactory, EntityInterceptor);
                }
                else if (!AllowCreate)
                {
                    return SessionFactoryUtils.GetSession(SessionFactory, false);
                }
                else
                {
                    return SessionFactoryUtils.GetSession(
                        SessionFactory, EntityInterceptor, AdoExceptionTranslator);
                }

            }

        }
      
      #endregion

    #region Methods

        /// <summary>
        /// Apply the flush mode that's been specified for this accessor
        /// to the given Session.
        /// </summary>
        /// <param name="session">The current Hibernate Session.</param>
        /// <param name="existingTransaction">if set to <c>true</c>
        /// if executing within an existing transaction.</param>
        /// <returns>
        /// the previous flush mode to restore after the operation,
        /// or <code>null</code> if none
        /// </returns>
        protected FlushModeHolder ApplyFlushMode(ISession session, bool existingTransaction) 
        {
            if (TemplateFlushMode == TemplateFlushMode.Never) 
            {
                if (existingTransaction) 
                {
                    FlushMode previousFlushMode = session.FlushMode;
                    if (previousFlushMode != FlushMode.Never) 
                    {
                        session.FlushMode = FlushMode.Never;
                        return new FlushModeHolder(previousFlushMode);
                    }
                }
                else 
                {
                    session.FlushMode = FlushMode.Never;
                }
            }
            else if (TemplateFlushMode == TemplateFlushMode.Eager) 
            {
                if (existingTransaction) 
                {
                    FlushMode previousFlushMode = session.FlushMode;
                    if (previousFlushMode != FlushMode.Auto) 
                    {
                        session.FlushMode = FlushMode.Auto;
                        return new FlushModeHolder(previousFlushMode);
                    }
                }
                else 
                {
                    // rely on default FlushMode.AUTO
                }
            }
            else if (TemplateFlushMode == TemplateFlushMode.Commit) 
            {
                if (existingTransaction) 
                {
                    FlushMode previousFlushMode = session.FlushMode;
                    if (previousFlushMode == FlushMode.Auto) 
                    {
                        session.FlushMode = FlushMode.Commit;
                        return new FlushModeHolder(previousFlushMode);
                    }
                }
                else 
                {
                    session.FlushMode = FlushMode.Commit;
                }
            }
            return new FlushModeHolder();
        }

  
        /// <summary>
        ///  Flush the given Hibernate Session if necessary.
        /// </summary>
        /// <param name="session">The current Hibernate Session.</param>
        /// <param name="existingTransaction">if set to <c>true</c> 
        /// if executing within an existing transaction.</param>
        protected void FlushIfNecessary(ISession session, bool existingTransaction)
        {
            if (TemplateFlushMode == TemplateFlushMode.Eager ||
                (!existingTransaction && TemplateFlushMode != TemplateFlushMode.Never)) 
            {
                log.Debug("Eagerly flushing Hibernate session");
                session.Flush();
            }
        }

        /// <summary>
        /// Convert the given HibernateException to an appropriate exception from the
        /// <code>org.springframework.dao</code> hierarchy. Will automatically detect
        /// wrapped ADO.NET Exceptions and convert them accordingly.
        /// </summary>
        /// <param name="ex">HibernateException that occured.</param>
        /// <returns>
        /// The corresponding DataAccessException instance
        /// </returns>
        /// <remarks>
        /// The default implementation delegates to SessionFactoryUtils
        /// and convertAdoAccessException. Can be overridden in subclasses.
        /// </remarks>
        public virtual DataAccessException ConvertHibernateAccessException(HibernateException ex)
        {
            if (ex is ADOException)
            {
                return ConvertAdoAccessException((ADOException) ex);
            }   
            return SessionFactoryUtils.ConvertHibernateAccessException(ex);
        }

        /// <summary>
        /// Converts the ADO.NET access exception to an appropriate exception from the
        /// <code>org.springframework.dao</code> hierarchy. Can be overridden in subclasses.
        /// </summary>
        /// <param name="ex">ADOException that occured, wrapping underlying ADO.NET exception.</param>
        /// <returns>
        /// the corresponding DataAccessException instance
        /// </returns>
        protected virtual DataAccessException ConvertAdoAccessException(ADOException ex) 
        {

            string sqlString = (ex.SqlString != null)
                ? ex.SqlString.ToString()
                : string.Empty;
                return AdoExceptionTranslator.Translate(
                    "Hibernate operation: " + ex.Message, sqlString, ex.InnerException);            

        }

        /// <summary>
        /// Converts the ADO.NET access exception to an appropriate exception from the
        /// <code>org.springframework.dao</code> hierarchy. Can be overridden in subclasses.
        /// </summary>
        /// <remarks>
        /// <note>
        /// Note that a direct SQLException can just occur when callback code
        /// performs direct ADO.NET access via <code>ISession.Connection()</code>.
        /// </note>
        /// </remarks>
        /// <param name="ex">The ADO.NET exception.</param>
        /// <returns>The corresponding DataAccessException instance</returns>
        protected virtual DataAccessException ConvertAdoAccessException(Exception ex) 
        {           
            return AdoExceptionTranslator.Translate("Hibernate operation", null, ex);
        }

      
        /// <summary>
        /// Prepare the given IQuery object, applying cache settings and/or
        /// a transaction timeout.
        /// </summary>
        /// <param name="queryObject">The query object to prepare.</param>
        public virtual void PrepareQuery(IQuery queryObject)
        {
            if (CacheQueries)
            {
                queryObject.SetCacheable(true);
                if (QueryCacheRegion != null)
                {
                    queryObject.SetCacheRegion(QueryCacheRegion);
                }
            }
                      
            if (FetchSize > 0)
            {
                AbstractQueryImpl queryImpl = queryObject as AbstractQueryImpl;
                if (queryImpl != null)
                {
                    queryImpl.SetFetchSize(FetchSize);
                }
                else
                {
                    log.Warn("Could not set FetchSize for IQuery.  Expected Implemention to be of type AbstractQueryImpl");
                }
            }
            
            if (MaxResults > 0)
            {
                queryObject.SetMaxResults(MaxResults);
            }

            SessionFactoryUtils.ApplyTransactionTimeout(queryObject, SessionFactory);

        }

        /// <summary>
        /// Apply the given name parameter to the given Query object.
        /// </summary>
        /// <param name="queryObject">The query object.</param>
        /// <param name="paramName">Name of the parameter</param>
        /// <param name="value">The value of the parameter</param>
        /// <param name="type">The NHibernate type of the parameter (or <code>null</code> if none specified)</param>
        public virtual void ApplyNamedParameterToQuery(IQuery queryObject, string paramName, object value, IType type)
        {

            if (value is ICollection)
            {
                if (type != null)
                {
                    queryObject.SetParameterList(paramName, (ICollection)value, type);
                }
                else
                {
                    queryObject.SetParameterList(paramName, (ICollection)value);
                }
            }
            else if (value is Object[])
            {

                //TODO investigate support for this conversion.
                if (type != null)
                {
                    queryObject.SetParameterList(paramName, (Object[])value, type);
                }
                else
                {
                    queryObject.SetParameterList(paramName, (Object[])value);
                }
            }
            else
            {
                if (type != null)
                {
                    queryObject.SetParameter(paramName, value, type);
                }
                else
                {
                    queryObject.SetParameter(paramName, value);
                }
            }
        }

        /// <summary>
        /// Prepare the given Criteria object, applying cache settings and/or
        /// a transaction timeout.
        /// </summary>
        /// <remarks>
        /// <para>Note that for NHibernate 1.2 this only works if the
        /// implementation is of the type CriteriaImpl, which should generally
        /// be the case.  The SetFetchSize method is not available on the
        /// ICriteria interface
        /// </para>
        /// <para>This is a no-op for NHibernate 1.0.x since
        /// the SetFetchSize method is not on the ICriteria interface and
        /// the implementation class is has internal access.
        /// </para>
        /// <para>To remove the method completely for Spring's NHibernate 1.0 
        /// support while reusing code for NHibernate 1.2 would not be 
        /// possible.  So now this ineffectual operation is left in tact for
        /// NHibernate 1.0.2 support.</para>
        /// </remarks>
        /// <param name="criteria">The criteria object to prepare</param>
        public void PrepareCriteria(ICriteria criteria)
        {
            if (CacheQueries)
            {
                criteria.SetCacheable(true);
                if (QueryCacheRegion != null)
                {
                    criteria.SetCacheRegion(QueryCacheRegion);
                }
            }



            if (FetchSize > 0)
            {
                //TODO see if we can optimize performance.
                //CriteriaImpl is internal in NH 1.0.x
                object[] args = new object[] { FetchSize };
                try
                {
                    Type t = TypeResolutionUtils.ResolveType("NHibernate.Impl.CriteriaImpl, NHibernate");
                    if (t != null)
                    {
                        t.InvokeMember("SetFetchSize", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
                                                       | BindingFlags.InvokeMethod,
                                       null, criteria, args);
                    }
                }
                catch (TypeLoadException e)
                {
                    log.Warn("Can't set FetchSize for ICriteria", e);
                }
            }
            
            if (MaxResults > 0)
            {
                criteria.SetMaxResults(MaxResults);
            }

            SessionFactoryUtils.ApplyTransactionTimeout(criteria, SessionFactory);
        }
      
      private void InitCriteriaType()
      {
                      
            try
            {
                criteriaType = TypeResolutionUtils.ResolveType("Hibernate.Impl.CriteriaImpl, NHibernate");
            }
            catch (TypeLoadException e)
            {
                log.Warn("CriteriaImpl not available. FetchSize can not be set on ICriteria objects", e);
            }
      }
      
    #endregion

        /// <summary>
        /// Ensure SessionFactory is not null
        /// </summary>
        /// <exception cref="ArgumentException">If SessionFactory property is null.</exception>
      public virtual void AfterPropertiesSet()
      {
            if (SessionFactory == null) 
            {
                throw new ArgumentException("sessionFactory is required");
            }
      }

        #region Helper Classes
        /// <summary>
        /// Helper class to determine if the FlushMode enumeration
        /// was changed from its default value
        /// </summary>
        protected class FlushModeHolder
        {
            /// <summary>
            /// Gets or sets a value indicating whether the FlushMode
            /// property was set..
            /// </summary>
            /// <value><c>true</c> if FlushMode was set; otherwise, <c>false</c>.</value>
            public bool ModeWasSet
            {
                get { return modeWasSet; }
                set { modeWasSet = value; }
            }

            /// <summary>
            /// Gets or sets the FlushMode.
            /// </summary>
            /// <value>The FlushMode.</value>
            public FlushMode Mode
            {
                get { return flushMode; }
                set { flushMode = value; }
            }

            private bool modeWasSet = false;
            private FlushMode flushMode;

            /// <summary>
            /// Initializes a new instance of the <see cref="FlushModeHolder"/> class.
            /// </summary>
            public FlushModeHolder()
            {                
            }

            /// <summary>
            /// Initializes a new instance of the <see cref="FlushModeHolder"/> class.
            /// </summary>
            /// <param name="mode">The flush mode.</param>
            public FlushModeHolder(FlushMode mode)
            {
                Mode = mode;
                ModeWasSet = true;
            }

        }
      

        #endregion
      

  }

    internal class CloseSuppressingMethodInterceptor : IMethodInterceptor
    {
        private HibernateAccessor hibernateAccessor;

        public CloseSuppressingMethodInterceptor(HibernateAccessor accessor)
        {
            hibernateAccessor = accessor;
        }

        public object Invoke(IMethodInvocation invocation)
        {
            //anything special for equals/hashcode?
            if (invocation.Method.Name.Equals("Close"))
            {
                return null;
            }
            else
            {
                object retValue = invocation.Proceed();
                if (retValue is IQuery)
                {
                    hibernateAccessor.PrepareQuery((IQuery)retValue);
                }
                if (retValue is ICriteria)
                {
                    hibernateAccessor.PrepareCriteria((ICriteria)retValue);
                }
                return retValue;
            }
        }
    }
}
www.java2v.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.