zoukankan      html  css  js  c++  java
  • Spring.NET实用技巧5——WCF环境下的NHibernate分布式事务

      

      之前实现的NHibernate分布式事务,在WCF环境下遇到的一个难点,是NHibernate的Session管理。然而在我查看log4net生成的调试日志时候惊奇的发现,原来NHibernate的Session不一定需要SessionScope来管理。在遇到事务的时候能自动创建一个Session,在事务关闭的时候能自动关闭Session。SessionScope仅仅是把自动创建的Session合并为一个。就例如,在第一次调用服务层方法的时候会产生一个新的Session,在第二次调用的时候也会产生一个新的Session,在没有使用SessionScope把这两个请求“包围”起来的时候,由于是不同的Session,所以经常报“two session”这样的错。由此可见,作为分布式事务,会自动打开Session的,在分布式事务结束以后会关闭这个Session。同理,NHibernate延迟加载、持久化等机制也能很好的管理。这种Spring.NET给我们提供的Session管理机制就能很好的实现NHibernate分布式事务。

      

      让我们看一下Spring.NET提供的几种事务管理器

      

    名称

    作用

    AdoPlatformTransactionManager

    基于本地ADO.NET的事务

    ServiceDomainPlatformTransactionManager

    由企业服务提供的分布式事务管理器

    TxScopePlatformTransactionManager

    System.Transactions提供的本地/分布式的事务管理器

    HibernateTransactionManager

    基于NHibernate本地事务

      我们选择ServiceDomainPlatformTransactionManager或者TxScopePlatformTransactionManager即可以实现基于WCF环境下NHibernate的分布式事务。

      让我们模拟“银行转账”的场景:有两个银行,北京银行和上海银行。每个银行各有1个账户。先给北京银行的账户初始化1000元钱,然后北京银行的账户再向上海银行的账户转入1000元。接下来,向上海银行转入1000元钱,这时由于北京银行的账户余额不足,所以不能转入。

      我们为了能够测试分布式事务的效果,先将转入的账户加上转入的金额,然后再扣除转出账户的金额。当转出账户的金额不足时,转入账户的金额将会自动回滚。

      让我们看一下Demo。

      

      一、Domain

      

    AccountInfo
        [DataContract]
        
    public class AccountInfo
        {
            [DataMember]
            
    public virtual int? ID { getset; }

            [DataMember]
            
    public virtual string Name { getset; }

            [DataMember]
            
    public virtual decimal Money { getset; }
        }
    AccountInfo.hbm.xml
    <?xml version="1.0" encoding="utf-8" ?>

    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="BeiJing.Bank.Domain" namespace="BeiJing.Bank.Domain">
      
    <class name="AccountInfo" table="T_Account" lazy="true" >

        
    <id name="ID" column="ID" type="Int32" >
          
    <generator class="native" />
        
    </id>

        
    <property name="Name" type="string">
          
    <column name="Name" length="50"/>
        
    </property>

        
    <property name="Money" type="decimal">
          
    <column name="Money" precision="16" scale="2"/>
        
    </property>

      
    </class>
    </hibernate-mapping>

      二、Dao

         

    AccountDao
        public interface IAccountDao
        {
            AccountInfo Get(
    object id);

            
    object Save(AccountInfo entity);

            
    void Update(AccountInfo entity);
        }

        
    public class AccountDao : HibernateDaoSupport, IAccountDao
        {
            
    public virtual object Save(AccountInfo entity)
            {
                
    return this.HibernateTemplate.Save(entity);
            }

            
    public virtual AccountInfo Get(object id)
            {
                
    return this.HibernateTemplate.Get<AccountInfo>(id);
            }

            
    public void Update(AccountInfo entity)
            {
                
    this.HibernateTemplate.Update(entity);
            }
        }
    Dao.xml
    <?xml version="1.0" encoding="utf-8" ?>
    <objects xmlns="http://www.springframework.net"
             xmlns:db
    ="http://www.springframework.net/database">
     
      
    <object type="Spring.Objects.Factory.Config.PropertyPlaceholderConfigurer, Spring.Core">
        
    <property name="ConfigSections" value="databaseSettings"/>
      
    </object>

      
    <db:provider id="DbProvider" provider="SqlServer-2.0"
                   connectionString
    ="Server=.;database=BeiJing;uid=sa;pwd=;"/>

      
    <object id="NHibernateSessionFactory" type="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate21">
        
    <property name="DbProvider" ref="DbProvider"/>
        
    <property name="MappingAssemblies">
          
    <list>
            
    <value>BeiJing.Bank.Domain</value>
          
    </list>
        
    </property>
        
    <property name="HibernateProperties">
          
    <dictionary>
            
    <entry key="hibernate.connection.provider" value="NHibernate.Connection.DriverConnectionProvider"/>
            
    <!--SqlServer连接-->
            
    <entry key="dialect" value="NHibernate.Dialect.MsSql2000Dialect"/>
            
    <entry key="hibernate.connection.driver_class" value="NHibernate.Driver.SqlClientDriver"/>

            
    <entry key="use_outer_join" value="true"/>
            
    <entry key="show_sql" value="true"/>
            
    <!--自动建表(反向映射)-->
            
    <entry key="hbm2ddl.auto" value="update"/>
            
    <!--批量更新-->
            
    <entry key="adonet.batch_size" value="0"/>
            
    <!--超时时间-->
            
    <entry key="command_timeout" value="60"/>
            
    <!--启用二级缓存-->
            
    <entry key="cache.use_second_level_cache" value="false"/>
            
    <!--启动查询缓存-->
            
    <entry key="cache.use_query_cache" value="false"/>
            
    <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>

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

      
    <!-- Dao -->
      
    <object id="AccountDao" type="BeiJing.Bank.Dao.Implement.AccountDao,BeiJing.Bank.Dao">
        
    <property name="HibernateTemplate" ref="HibernateTemplate"/>
      
    </object>

    </objects>

      三、Service

    AccountManager
        public interface IAccountManager
        {
            AccountInfo Get(
    object id);

            
    object Save(AccountInfo entity);

            
    void Update(AccountInfo entity);
        }

        
    public class AccountManager : IAccountManager
        {
            
    public IAccountDao Dao { getset; }

            
    public AccountInfo Get(object id)
            {
                
    return Dao.Get(id);
            }

            
    public object Save(AccountInfo entity)
            {
                
    return Dao.Save(entity);
            }

            
    public void Update(AccountInfo entity)
            {
                
    if (entity.Money < 0)
                {
                    
    throw new Exception("账户金额不足");
                }
                Dao.Update(entity);
            }
        }
    Service.xml
    <?xml version="1.0" encoding="utf-8" ?>
    <objects xmlns="http://www.springframework.net"
             xmlns:tx
    ="http://www.springframework.net/tx">

      
    <object id="transactionManager"
          type
    ="Spring.Data.Core.TxScopeTransactionManager, Spring.Data">
      
    </object>
      
      
    <object id="transactionInterceptor" 
              type
    ="Spring.Transaction.Interceptor.TransactionInterceptor, Spring.Data">
        
    <property name="TransactionManager" ref="transactionManager"/>
        
    <property name="TransactionAttributeSource">
          
    <object type="Spring.Transaction.Interceptor.AttributesTransactionAttributeSource, Spring.Data"/>
        
    </property>
      
    </object>

      
    <object id="BaseTransactionManager"  
              type
    ="Spring.Transaction.Interceptor.TransactionProxyFactoryObject, Spring.Data" abstract="true">
        
    <property name="PlatformTransactionManager" ref="transactionManager"/>
        
    <property name="TransactionAttributes">
          
    <name-values>  
            
    <add key="*" value="PROPAGATION_REQUIRED"/>
          
    </name-values>
        
    </property>
      
    </object>

      
    <object id="AccountManager" parent="BaseTransactionManager">
        
    <property name="Target">
          
    <object type="BeiJing.Bank.Service.Implement.AccountManager, BeiJing.Bank.Service">
            
    <property name="Dao" ref="AccountDao"/>
          
    </object>
        
    </property>
      
    </object>
      
    </objects>

      四、Host

      

    Contract
        [ServiceContract(SessionMode = SessionMode.Required)]
        
    public interface IContract
        {
            [OperationContract]
            [TransactionFlow(TransactionFlowOption.Allowed)]
            AccountInfo Get(
    object id);

            [OperationContract]
            [TransactionFlow(TransactionFlowOption.Allowed)]
            
    object Save(AccountInfo entity);

            [OperationContract]
            [TransactionFlow(TransactionFlowOption.Allowed)]
            
    void Update(AccountInfo entity);
        }

        [AspNetCompatibilityRequirements(RequirementsMode 
    = AspNetCompatibilityRequirementsMode.Required)]
        
    public class BankServer : IContract
        {
            
    public IAccountManager Manager { getset; }

            [OperationBehavior(TransactionScopeRequired 
    = true)]
            
    public AccountInfo Get(object id)
            {
                
    return Manager.Get(id);
            }

            [OperationBehavior(TransactionScopeRequired 
    = true)]
            
    public object Save(AccountInfo entity)
            {
                
    return Manager.Save(entity);
            }

            [OperationBehavior(TransactionScopeRequired 
    = true)]
            
    public void Update(AccountInfo entity)
            {
                Manager.Update(entity);
            }
        }
    <%@ ServiceHost Language="C#" Debug="true" Service="Host" Factory="Spring.ServiceModel.Activation.ServiceHostFactory"%>
    web.config
      <configSections>

        
    <sectionGroup name="spring">
          
    <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/>
          
    <section name="parsers" type="Spring.Context.Support.NamespaceParsersSectionHandler, Spring.Core"/>
          
    <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core"/>
        
    </sectionGroup>


      
    </configSections>


    ......


    <spring xmlns="http://www.springframework.net">
        
    <parsers>
          
    <parser type="Spring.Data.Config.DatabaseNamespaceParser, Spring.Data" />
          
    <parser type="Spring.Transaction.Config.TxNamespaceParser, Spring.Data" />
        
    </parsers>
        
    <context>
          
    <resource uri="config://spring/objects" />

          
    <!--Dao-->
          
    <resource uri="assembly://BeiJing.Bank.Dao/BeiJing.Bank.Dao.Config/Dao.xml" />
          
    <!--Service-->
          
    <resource uri="assembly://BeiJing.Bank.Service/BeiJing.Bank.Service.Config/Service.xml" />

        
    </context>
        
    <objects xmlns="http://www.springframework.net">

          
    <object id="Host" type="BeiJing.Bank.Host.Implement.BankServer, BeiJing.Bank.Host">
            
    <property name="Manager" ref="AccountManager" />
          
    </object>

        
    </objects>
      
    </spring>


    .......

    <system.serviceModel>
        
    <services>
          
    <service name="Host">
            
    <endpoint address="" binding="wsHttpBinding" bindingConfiguration="ServerBinding" contract="BeiJing.Bank.Host.IContract"/>
            
    <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
          
    </service>
        
    </services>
        
    <bindings>
          
    <wsHttpBinding >
            
    <binding name="ServerBinding" transactionFlow="true">
            
    </binding>
          
    </wsHttpBinding>
        
    </bindings>
        
    <behaviors>
          
    <serviceBehaviors>
            
    <behavior>
              
    <!-- 为避免泄漏元数据信息,请在部署前将以下值设置为 false 并删除上面的元数据终结点 -->
              
    <serviceMetadata httpGetEnabled="true"/>
              
    <!-- 要接收故障异常详细信息以进行调试,请将以下值设置为 true。在部署前设置为 false 以避免泄漏异常信息 -->
              
    <serviceDebug includeExceptionDetailInFaults="true"/>
            
    </behavior>
          
    </serviceBehaviors>
        
    </behaviors>
        
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" aspNetCompatibilityEnabled="true"/>
      
    </system.serviceModel>

      五、Test

      

    TransactionTest
        [TestFixture]
        
    public class TransactionTest
        {
            
    private BeiJingProxy.ContractClient beiJingProxy;

            
    private ShangHaiProxy.ContractClient shangHaiProxy;

            [SetUp]
            
    public void Init()
            {
                beiJingProxy 
    = new BeiJingProxy.ContractClient();
                shangHaiProxy 
    = new ShangHaiProxy.ContractClient();
            }

            [Test]
            
    public void InitData()
            {
                beiJingProxy.Save(
    new BeiJingProxy.AccountInfo
                {
                    Name 
    = "刘冬",
                    Money 
    = 1000
                });

                shangHaiProxy.Save(
    new ShangHaiProxy.AccountInfo
                {
                    Name 
    = "冬冬",
                    Money 
    = 0
                });
            }
            
            [Test]
            
    public void CompleteTest()
            {
                
    using (TransactionScope scope = new TransactionScope())
                {
                    var beiJingAccount 
    = beiJingProxy.Get(1);
                    var shangHaiAccount 
    = shangHaiProxy.Get(1);

                    beiJingAccount.Money 
    -= 1000;
                    shangHaiAccount.Money 
    += 1000;

                    shangHaiProxy.Update(shangHaiAccount);
                    beiJingProxy.Update(beiJingAccount);

                    scope.Complete();
                }
            }

      六、运行效果

      1.初始化数据

      

      2.第一次转账:北京账户转入上海账户1000元

      

      3.第二次转账:北京账户转入上海账户1000元,由于北京账户余额不足,所以上海账户增加的1000元回滚。

      

      

      好了,基于WCF环境下的NHibernate分布式事务就完美的实现了。

      

      代码下载

      出处:http://www.cnblogs.com/GoodHelper/archive/2010/08/04/SpringNetWcfDistributedTransaction.html

      欢迎转载,但需保留版权。

  • 相关阅读:
    saltstack高效运维
    python与RPC服务
    01-08-01【Nhibernate (版本3.3.1.4000) 出入江湖】NHibernate中的一级缓存
    01-08-01【Nhibernate (版本3.3.1.4000) 出入江湖】NHibernate中的三种状态
    01-07-01【Nhibernate (版本3.3.1.4000) 出入江湖】并发控制
    01-06-01【Nhibernate (版本3.3.1.4000) 出入江湖】事务
    01-05-01-2【Nhibernate (版本3.3.1.4000) 出入江湖】立即加载实现--NHibernateUtil.Initialize()和添加fetch关键字的HQL查询
    01-05-01-1【Nhibernate (版本3.3.1.4000) 出入江湖】延迟加载及其class和集合(set、bag等)的Lazy属性配置组合对Get和Load方法的影响
    01-01-01【Nhibernate (版本3.3.1.4000) 出入江湖】配置文件
    【log4net】配置文件
  • 原文地址:https://www.cnblogs.com/GoodHelper/p/SpringNetWcfDistributedTransaction.html
Copyright © 2011-2022 走看看