• Castle实践5-NHibernate Facility


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


    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~~

  • 相关阅读:
    Android 表格布局
    Python 字符串操作分类
    设置Safari禁止访问某个网站
    java判断路径是文件夹还是文件
    java上下分页窗口流动布局
    Python获取网页html代码
    一次失败的java Box居中尝试
    装饰器进阶和迭代器
    函数对象补充,包函数与装饰器
    函数对象和名称空间
  • 原文地址:https://www.cnblogs.com/wj/p/189924.html
Copyright © 2020-2023  润新知