叶子的家

~●    ~●  ~●          ~●   ~●~●                           ○
    离成功还很远,距离长着叻,Fighting!
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

Castle实践5-NHibernate Facility

Posted on 2005-07-10 14:20  绿叶  阅读(9237)  评论(12编辑  收藏  举报

        大家好,最近项目正式发布挺忙的,所以这篇东西是拖得挺久的啦。好了,废话不多说,进入正题吧。


NHibernate Facility包括有两个项目:
1)Castle.Facilities.NHibernateExtension
        这里有主要有两个Attribute和一个Session管理器。
        SessionFlushAttribute:用于方法指定session的flush方式

[SessionFlush(FlushOption.Auto)]
public virtual Person CreatePerson(string name)
{ }

        UsesAutomaticSessionCreationAttribute:用于类指定使用的session自动管理器使用哪个session factory(配置文件中指定)。

[UsesAutomaticSessionCreation("nhibernate.factory")]
public class PersonDao
{ }

        SessionManager:会话管理器,这是一个PerThread class(LocalDataStoreSlot),用一个堆栈来保存当前会话,在下面介绍的AutomaticSessionInterceptor中会用到这个管理器。

static Stack CurStack
{
    [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配置

<?xml version="1.0" encoding="utf-8" ?>
<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:

[UsesAutomaticSessionCreation("nhibernate.factory.1")]
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;
    }
}

第四步:容器初始化

IWindsorContainer container = new WindsorContainer("../../CastleConfig.xml");
container.AddFacility(
"transaction"new TransactionFacility());
container.AddFacility(
"nhibernate"new NHibernateFacility());
container.AddComponent(
"blog.dao"typeof(BlogDao));

第五步:使用

BlogDao dao = (BlogDao)container["blog.dao"];
dao.CreateBlog(
string.Format("test-{0}"new Random().Next()));

使用过程中,CreateBlog会自动根据指定的session factory新建factory,并调用factory生产session来执行任务,最后根据指定的flush模式来flush。
如果你要使用自动事务,可以这样:

[Transactional]
[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。

// 声明一个服务接口为ISessionFactory的ComponentModel
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执行初始化动作,片断如下:
// 获取session factory配置
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拦截器分析:
// 获取当前的方法使用的数据库的标识id
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~~