大家好,最近项目正式发布挺忙的,所以这篇东西是拖得挺久的啦。好了,废话不多说,进入正题吧。
NHibernate Facility包括有两个项目:
1)Castle.Facilities.NHibernateExtension
这里有主要有两个Attribute和一个Session管理器。
SessionFlushAttribute:用于方法指定session的flush方式
public virtual Person CreatePerson(string name)
{ }
UsesAutomaticSessionCreationAttribute:用于类指定使用的session自动管理器使用哪个session factory(配置文件中指定)。
public class PersonDao
{ }
SessionManager:会话管理器,这是一个PerThread class(LocalDataStoreSlot),用一个堆栈来保存当前会话,在下面介绍的AutomaticSessionInterceptor中会用到这个管理器。
{
[MethodImpl(MethodImplOptions.Synchronized)]
get
{
// _slot是一个“内存槽”,这个槽是用来装一个Stack的,
//这样做用来确保每个线程在_slot只共享一个Stack,他用来
//储存Session
Stack stack = (Stack) Thread.GetData(_slot);
if (stack == null)
{
stack = new Stack();
Thread.SetData(_slot, stack);
}
return stack;
}
}
2)Castle.Facilities.NHibernateIntegration
NHibernate Facility的核心在这里,这里用到了前面介绍的Castle.Services.Transaction(实践3)和Castle.Facilities.AutomaticTransactionManagement(实践4)。首先,先不说原理,我们来看一下怎么使用。
【使用篇】
第一步:NHibernate Facility配置
<configuration>
<facilities>
<facility id="nhibernate">
<!-- 数据库一 -->
<factory id="nhibernate.factory.1">
<settings>
<item key="hibernate.connection.provider">NHibernate.Connection.DriverConnectionProvider</item>
<item key="hibernate.connection.driver_class">NHibernate.Driver.SqlClientDriver</item>
<item key="hibernate.connection.connection_string">Server=localhost;Database=Test;Uid=sa;Pwd=123</item>
<item key="hibernate.dialect">NHibernate.Dialect.MsSql2000Dialect</item>
</settings>
<resources>
<resource assembly="Demo" name="Demo.Entities.Blog.hbm.xml" />
<resource assembly="Demo" name="Demo.Entities.BlogItem.hbm.xml" />
</resources>
</factory>
<!-- 数据库二 -->
<factory id="nhibernate.factory.2">
<settings>
<item key="hibernate.connection.provider">NHibernate.Connection.DriverConnectionProvider</item>
<item key="hibernate.connection.driver_class">NHibernate.Driver.SqlClientDriver</item>
<item key="hibernate.connection.connection_string">Server=localhost;Database=Test2;Uid=sa;Pwd=123</item>
<item key="hibernate.dialect">NHibernate.Dialect.MsSql2000Dialect</item>
</settings>
<resources>
<resource assembly="Demo" name="Demo.Entities.Order.hbm.xml" />
</resources>
</factory>
</facility>
</facilities>
</configuration>
第二步:建立好NHibernate的映射和持久类文件。这里和普通使用nhb没什么不同的地方。
第三步:在DAO中使用上面介绍的UsesAutomaticSessionCreationAttribute:
public class BlogDao
{
public BlogDao() {}
[SessionFlush(FlushOption.Auto)]
public virtual Blog CreateBlog( String name )
{
ISession session = SessionManager.CurrentSession;
Blog blog = new Blog();
blog.Name = name;
blog.Items = new ArrayList();
session.Save(blog);
return blog;
}
}
第四步:容器初始化
container.AddFacility("transaction", new TransactionFacility());
container.AddFacility("nhibernate", new NHibernateFacility());
container.AddComponent("blog.dao", typeof(BlogDao));
第五步:使用
dao.CreateBlog(string.Format("test-{0}", new Random().Next()));
使用过程中,CreateBlog会自动根据指定的session factory新建factory,并调用factory生产session来执行任务,最后根据指定的flush模式来flush。
如果你要使用自动事务,可以这样:
[UsesAutomaticSessionCreation("nhibernate.factory.1")]
public class BlogDao
{
[Transaction(TransactionMode.Requires)]
[SessionFlush(FlushOption.Auto)]
public virtual Blog CreateBlog( String name )
{
ISession session = SessionManager.CurrentSession;
Blog blog = new Blog();
blog.Name = name;
blog.Items = new ArrayList();
session.Save(blog);
return blog;
}
}
事务管理也是自动的,会自动根据是否发生异常来Commit或者Rollback。
这样使用之后,你完全不用写一句OpenSession()或者BeginTrans()又或者是Commit()等等,因为这都被容器自动化管理了。而且可以很方便的使用多个数据库连接,只需要改变一下UsesAutomaticSessionCreationAttribute中的factory id就可以了。Cool吗?
【原理篇】
1、SessionKeeper : Castle.Services.Transaction.ISynchronization
事务的同步化对象,这里用于关闭相应的ISession。
2、ResourceSessionAdapter : Castle.Services.Transaction.IResource
这里Castle.Services.Transaction.ITransaction包装了一个NHibernate.ITransaction作为资源,他是一个“对象适配器”,把NHibernate.ITransaction适配为Castle.Services.Transaction.IResource,用于NHibernate.ITransaction提交和回滚。
上面两个对象都是Castle.Services.Transaction(实践3有详细介绍)里面的,NHibernate Facility用他们协助管理、同步事务。在拦截器AutomaticSessionInterceptor中,如果方法体声明了事务特性,他们将被使用到。
3、SessionFactoryActivator : Castle.MicroKernel.ComponentActivator.AbstractComponentActivator
NHibernate Facility在配置ISessionFactory的时候,根据xml配置文件初始化一个NHibernate.Cfg.Configuration,并把这个Configuration作为ISessionFactory组件的一个ExtendedProperties,当请求ISessionFactory的时候,Activator会把ExtendedProperties里面的Configuration取出来,并调用BuildSessionFactory来生产出一个ISessionFactory。这样就可以根据不同的ID(配置时候指定)来请求不同的ISessionFactory了,上面说使用多数据库的时候就是改变一下id,这下明白了吧。当然ISessionFactory的请求是Facility本身做的事,不用你管,要是你想直接用ISessionFactory,那也可以直接向容器索取,同样的你只需要知道一个ID。
ComponentModel model = new ComponentModel(id, typeof(ISessionFactory), null);
// 把配置对象作为组件的扩张属性
model.ExtendedProperties.Add(ConfiguredObject, cfg );
// 组件生存方式为单例
model.LifestyleType = LifestyleType.Singleton;
// 指定Activator
model.CustomComponentActivator = typeof( SessionFactoryActivator );
// 加入到容器
Kernel.AddCustomComponent( model );
4、NHibernateTransactionManager : Castle.Services.Transaction.DefaultTransactionManager
这是一个事务管理器,在TransactionInterceptor中用管理器创建Castle.Services.Transaction.ITransaction并加入管理器,在AutomaticSessionInterceptor 中检测到有事务打开,则执行上面提到的1、2点包装一个NHibernate.ITransaction,并开始事务。
5、TransactionInterceptor 与 AutomaticSessionInterceptor
这两个拦截器是NHibernate Facility的核心,下面详细分析。
我们从注册一个NHibernate Facility开始,看他是如何实现这种自动化管理的。
注册Facility的时候,我们不但向容器注册了NHibernate Facility,而且也注册了Transaction Facility。当使用了Transactional和UsesAutomaticSessionCreation特性,这两个facility会同时发生作用。
NHibernate Facility执行初始化动作,片断如下:
IConfiguration factoriesConfig = FacilityConfig.Children["factory"];
if (factoriesConfig == null)
{
throw new ConfigurationException(
"You need to configure at least one factory to use the NHibernateFacility");
}
// 加入一个组件初始化处理流,所有声明了UsesAutomaticSessionCreationAttribute的都植入拦截器
Kernel.ComponentModelBuilder.AddContributor( new AutomaticSessionInspector() );
// 上面植入的拦截器
Kernel.AddComponent( "nhibernate.session.interceptor", typeof(AutomaticSessionInterceptor) );
// 加入事务管理器
Kernel.AddComponent( "nhibernate.transaction.manager", typeof(ITransactionManager), typeof(NHibernateTransactionManager) );
foreach(IConfiguration factoryConfig in FacilityConfig.Children)
{
if (!"factory".Equals(factoryConfig.Name))
{
throw new ConfigurationException("Unexpected node " + factoryConfig.Name);
}
// 配置session factory,配置过程请看上面第三点
ConfigureFactories(factoryConfig);
}
TransactionFacility会在所有使用了事务特性的方法上面植入拦截器,上面提到的NHibernateTransactionManager 就是在这里发挥作用的。拦截器片断如下:
ITransaction transaction = _manager.CreateTransaction( transactionAtt.TransactionMode, transactionAtt.IsolationMode );
if (transaction == null)
{
return invocation.Proceed(args);
}
object value = null;
// 开始事务
transaction.Begin();
try
{
// 执行方法体处理,这里会被AutomaticSessionInterceptor拦截
value = invocation.Proceed(args);
// 正常则提交事务
transaction.Commit();
}
catch(Exception ex)
{
// 发生异常回滚事务
transaction.Rollback();
throw ex;
}
finally
{
// 释放事务资源
_manager.Dispose(transaction);
}
AutomaticSessionInterceptor拦截器分析:
String key = ObtainSessionFactoryKeyFor(invocation.InvocationTarget);
// 根据标识id判断当前处理是否和session管理器的当前活动数据库相同
if (SessionManager.IsCurrentSessionCompatible(key))
{
// 执行处理
return invocation.Proceed(args);
}
// 获取session factory,上面讲的SessionFactoryActivator会发挥作用
ISessionFactory sessionFactory = ObtainSessionFactoryFor(key);
// 打开一个session
ISession session = sessionFactory.OpenSession();
// 把session和key标识加入管理器
SessionManager.Push(session, key);
// 获取session的flush模式
FlushOption flushOption = ExtractFlushOption(invocation.MethodInvocationTarget);
ConfigureFlushMode(flushOption, session);
// 当前是否有活动事务,如果有则开始事务
if (EnlistSessionIfHasTransactionActive(key, session))
{
try
{
// 执行处理
return invocation.Proceed(args);
}
finally
{
// 从管理器从移除sesion
SessionManager.Pop(key);
}
}
try
{
// 不带事务的处理
return invocation.Proceed(args);
}
finally
{
// flush
if (flushOption == FlushOption.Force)
{
session.Flush();
}
// 关闭session
session.Close();
// 移除sesion
SessionManager.Pop(key);
}
好了,分析就到此为止吧。是不是有点混乱。因为这个Facility和其他好几个Facility结合一起用,关系比较复杂啦。建议大家如果想了解原理的话,还是结合我的分析自己跟踪一下源码的处理吧。
因为是实践性的东西,我希望多以代码的方式剖析给大家看。哪里讲得不对的,希望大家多提宝贵意见,下次实践我们再见咯~~3q~~