最近在学习Spring.NET,对Spring.NET对NHibernate的封装用起来感觉很舒服,于是乎自己查看了Spring.NET的源代码,看看其秘密。
用过NHibernate的童靴应该知道,我们想对数据库的进行CRUD操作,必须有Session,而Session又是由SessionFactory创造的,而SessionFactory可以由下面的代码创建。
var cfg = new NHibernate.Cfg.Configuration().Configure("Config/hibernate.cfg.xml");//hibernate.cfg.xml为配置文件
using (ISessionFactory sessionFactory = cfg.BuildSessionFactory())
{ //创建Session
ISession session=sessionFactory.OpenSession()
//CRUD操作
}
那在Spring.NET中如何操作呢? 先来看这段代码(Spring.NET源代码)
namespace Spring.Data.NHibernate.Generic.Support
{
public abstract class HibernateDaoSupport : DaoSupport
{
public HibernateTemplate HibernateTemplate { get; set; }
public ISessionFactory SessionFactory { get; set; }
public ISession Session { get; }
protected virtual HibernateTemplate CreateHibernateTemplate(ISessionFactory sessionFactory);
protected override void CheckDaoConfig();
protected ISession DoGetSession(bool allowCreate);
protected DataAccessException ConvertHibernateAccessException(HibernateException ex);
protected void ReleaseSession(ISession session);
}
}
这是一个抽象类,其中有一些重要属性:HibernateTemplate 、SessionFactory 、Session 。我们首先可以继承这个抽象类,具体如下
public abstract class RepositoryBase<T> : HibernateDaoSupport, IRepository<T> where T : class
{
/// <summary>
/// 保存
/// </summary>
/// <param name="entity">实体</param>
/// <returns></returns>
public virtual object Save(T entity)
{
return this.HibernateTemplate.Save(entity);
}
/// <summary>
/// 获取所有记录
/// </summary>
/// <returns></returns>
public virtual IQueryable<T> LoadAll()
{
var result = Session.Query<T>();
return result;
}
//...省略
}
那上面的HibernateTemplate 、Session 从哪来呢?我们可以通过属性注入(参照刘冬的博客)的方式来设置。以下是配置文件:
<!--SessionFactory对象-->
<object id="NHibernateSessionFactory" type="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate30">
<property name="DbProvider" ref="DbProvider"/>
<property name="MappingAssemblies">
<list>
<value>DomainModel</value>
</list>
</property>
<property name="HibernateProperties">
<dictionary>
<entry key="hibernate.current_session_context_class"
value="Spring.Data.NHibernate.SpringSessionContext, Spring.Data.NHibernate30"/>
<entry key="hibernate.connection.provider"
value="NHibernate.Connection.DriverConnectionProvider"/>
<!--数据库方言-->
<entry key="dialect" value="${dialect}"/>
<!--数据库驱动-->
<entry key="connection.driver_class" value="${driver_class}"/>
<entry key="use_outer_join" value="true"/>
<entry key="show_sql" value="false"/>
<!--自动建表(反向映射)-->
<entry key="hbm2ddl.auto" value="${hbm2ddl.auto}"/>
<!--超时时间-->
<entry key="command_timeout" value="60"/>
<entry key="query.substitutions" value="true 1, false 0, yes 'Y', no 'N'"/>
<entry key="proxyfactory.factory_class"
value="NHibernate.ByteCode.LinFu.ProxyFactoryFactory, NHibernate.ByteCode.LinFu"/>
</dictionary>
</property>
<property name="ExposeTransactionAwareSessionFactory" value="true" />
</object>
<!--HibernateTemplate模板-->
<object id="HibernateTemplate" type="Spring.Data.NHibernate.Generic.HibernateTemplate">
<property name="SessionFactory" ref="NHibernateSessionFactory" />
<property name="TemplateFlushMode" value="Auto" />
<property name="CacheQueries" value="true" />
</object>
这样一来我们就有了HibernateTemplate和SessionFactory,有童靴就问了但是没有Session啊。是的,但是Session是不是可以由SessionFactory创建。我们具体再来看HibernateDaoSupport类下的Session属性(Spring.NET源代码):
public ISession Session
{
get
{
return DoGetSession(HibernateTemplate.AllowCreate);
}
}
再转到DoGetSession方法:
protected ISession DoGetSession(bool allowCreate)
{
return (!allowCreate ?
SessionFactoryUtils.GetSession(SessionFactory, false) :
SessionFactoryUtils.GetSession(
SessionFactory,
this.hibernateTemplate.EntityInterceptor,
this.hibernateTemplate.AdoExceptionTranslator));
}
再转到SessionFactoryUtils.GetSession()方法,(注:下面的方法为重载方法)
private static ISession GetSession(
ISessionFactory sessionFactory, IInterceptor entityInterceptor,
IAdoExceptionTranslator adoExceptionTranslator, bool allowCreate)
{
try
{
return DoGetSession(sessionFactory, entityInterceptor, adoExceptionTranslator, allowCreate);
}
catch (HibernateException ex)
{
throw new DataAccessResourceFailureException("Could not open Hibernate Session", ex);
}
}
继续转到SessionFactoryUtils.DoGetSession()方法,注意和上面的 DoGetSession()方法区分
private static ISession DoGetSession(
ISessionFactory sessionFactory, IInterceptor entityInterceptor,
IAdoExceptionTranslator adoExceptionTranslator, bool allowCreate)
{
AssertUtils.ArgumentNotNull(sessionFactory, "sessionFactory", "SessionFactory can not be null");
SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.GetResource(sessionFactory);
if (sessionHolder != null && !sessionHolder.IsEmpty)
{
// pre-bound Hibernate Session
ISession session = null;
if (TransactionSynchronizationManager.SynchronizationActive &&
sessionHolder.DoesNotHoldNonDefaultSession)
{
// Spring transaction management is active ->
// register pre-bound Session with it for transactional flushing.
session = sessionHolder.ValidatedSession;
if (session != null && !sessionHolder.SynchronizedWithTransaction)
{
log.Debug("Registering Spring transaction synchronization for existing Hibernate Session");
TransactionSynchronizationManager.RegisterSynchronization(
new SpringSessionSynchronization(sessionHolder, sessionFactory, adoExceptionTranslator, false));
sessionHolder.SynchronizedWithTransaction = true;
// Switch to FlushMode.AUTO if we're not within a read-only transaction.
FlushMode flushMode = session.FlushMode;
if (FlushMode.Never == flushMode &&
!TransactionSynchronizationManager.CurrentTransactionReadOnly)
{
session.FlushMode = FlushMode.Auto;
sessionHolder.PreviousFlushMode = flushMode;
}
}
}
else
{
// No Spring transaction management active -> simply return default thread-bound Session, if any
// (possibly from OpenSessionInViewModule)
session = sessionHolder.ValidatedSession;
}
if (session != null)
{
return session;
}
}
ISession sess = OpenSession(sessionFactory, entityInterceptor);
// Set Session to FlushMode.Never if we're within a read-only transaction.
// Use same Session for further Hibernate actions within the transaction.
// Thread object will get removed by synchronization at transaction completion.
if (TransactionSynchronizationManager.SynchronizationActive)
{
log.Debug("Registering Spring transaction synchronization for new Hibernate Session");
SessionHolder holderToUse = sessionHolder;
if (holderToUse == null)
{
holderToUse = new SessionHolder(sess);
}
else
{
holderToUse.AddSession(sess);
}
if (TransactionSynchronizationManager.CurrentTransactionReadOnly)
{
sess.FlushMode = FlushMode.Never;
}
TransactionSynchronizationManager.RegisterSynchronization(
new SpringSessionSynchronization(holderToUse, sessionFactory, adoExceptionTranslator, true));
holderToUse.SynchronizedWithTransaction = true;
if (holderToUse != sessionHolder)
{
TransactionSynchronizationManager.BindResource(sessionFactory, holderToUse);
}
}
// Check whether we are allowed to return the Session.
if (!allowCreate && !IsSessionTransactional(sess, sessionFactory))
{
CloseSession(sess);
throw new InvalidOperationException ("No Hibernate Session bound to thread, " +
"and configuration does not allow creation of non-transactional one here");
}
return sess;
}
到此终于把Session找出来了。
现在又有问题了,HibernateTemplate怎么有Save()、Load()等方法呢,它又是如何操作数据的(根据NHibernate的经验,它最后肯定和Session有关系)。通过看源代码会发现所有的方法(Save()、Load()等方法)都是调用public object Execute(IHibernateCallback action, bool exposeNativeSession)这个方法,这个方法下有这一句:
Object result = action.DoInHibernate(sessionToExpose);
这里用到了一个设计模式--策略模式:
GetByTypeHibernateCallback、SaveObjectHibernateCallback等等都是继承自IHibernateCallback ,这里我们举SaveObjectHibernateCallback.DoInHibernate这个例子
public object DoInHibernate(ISession session)
{
outer.CheckWriteOperationAllowed(session);
return session.Save(entity);
}
看看果然用到了Session,这下原理也就明白了。