Spring.NET实用技巧3——NHibernate分布式事务(上)

长平狐 发布于 2012/06/11 11:54
阅读 929
收藏 0

在使用NHibernate作为持久层框架时,多数据库操作是一个比较难解决的问题。并且很多网友在给我发的eamil中经常谈到此问题。由于NHibernate是一种框架,不能像ADO.NET那样直接用SQL语句操作数据库,在动态改变DbConnection时比较麻烦,而且NHibernate目前并不完全支持多数据库,所以实现多数据库的操作是个棘手的问题。

回想一下,在使用ADO.NET实现多数据库的时候,无非是增加多个DbConnection,以后在每次事务结束后提交事务。所以说多数据库的实现难点在于实现分布式事务。那么,什么是分布式事务?分布式事务是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。为了实现分布式事务,需要使用下面将介绍的两阶段提交协议。 阶段一:开始向事务涉及到的全部资源发送提交前信息。此时,事务涉及到的资源还有最后一次机会来异常结束事务。如果任意一个资源决定异常结束事务,则整个事务取消,不会进行资源的更新。否则,事务将正常执行,除非发生灾难性的失败。为了防止会发生灾难性的失败,所有资源的更新都会写入到日志中。这些日志是永久性的,因此,这些日志会幸免遇难并且在失败之后可以重新对所有资源进行更新。  阶段二:只在阶段一没有异常结束的时候才会发生。此时,所有能被定位和单独控制的资源管理器都将开始执行真正的数据更新。 在分布式事务两阶段提交协议中,有一个主事务管理器负责充当分布式事务协调器的角色。事务协调器负责整个事务并使之与网络中的其他事务管理器协同工作。 为了实现分布式事务,必须使用一种协议在分布式事务的各个参与者之间传递事务上下文信息,IIOP便是这种协议。这就要求不同开发商开发的事务参与者必须支持一种标准协议,才能实现分布式的事务。

由于Spring.NET框架的出现,便很好的解决了这一点——分布式事务处理(查询此博客)。TxScopePlatformTransactionManagerSystem.Transactions提供的本地/分布式的事务管理器。我们配置TxScopePlatformTransactionManager 则能够实现分布式事务处理。

下面,我建立两个数据库:一个数据库为Customer,用于存放客户资料数据;另一个数据库为Order,用于存放客户的订单数据。当某个客户增加订单成功时,则更新该客户的订金余额。我们做一个业务判断,该客户的余额不能超过3000,当超过3000时则抛出异常。

实现步骤如下:

一、启动MSDTC服务。运行->cmd输入->net start msdtc

 

 

  二、代码实现

 

1.Domain

 

①.Customer

public   class  CustomerInfo
    {
        
public   virtual   int ?  ID {  get set ; }

        
public   virtual   string  Name {  get set ; }

        
public   virtual   decimal  Money {  get set ; }
    }

 

 

 

CustomerInfo.hbm.xml
<? xml version="1.0" encoding="utf-8"  ?>

< hibernate-mapping  xmlns ="urn:nhibernate-mapping-2.2"  assembly ="Customer.Domain"  namespace ="Customer.Domain" >
  
< class  name ="CustomerInfo"  table ="T_Customer"  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"  cision ="16"  scale ="2" />
    
</ property >

  
</ class >
</ hibernate-mapping >

 

 

②.Order

 

OrderInfo
public   class  OrderInfo
    {
        
public   virtual   int ?  ID {  get set ; }

        
public   virtual   int  CustomerId {  get set ; }

        
public   virtual  DateTime OrderDate {  get set ; }

        
public   virtual   string  Address {  get set ; }
    }

 

 

 

OrderInfo.hbm.xml
<? xml version="1.0" encoding="utf-8"  ?>

< hibernate-mapping  xmlns ="urn:nhibernate-mapping-2.2"  assembly ="Order.Domain"  namespace ="Order.Domain" >
  
< class  name ="OrderInfo"  table ="T_Order"  lazy ="true"   >

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

    
< property  name ="CustomerId"  type ="int" >
      
< column  name ="CustomerId" />
    
</ property >

    
< property  name ="OrderDate"  type ="DateTime" >
      
< column  name ="OrderDate" />
    
</ property >

    
< property  name ="Address"  type ="string" >
      
< column  name ="Address"  length ="200" />
    
</ property >

  
</ class >
</ hibernate-mapping >

 

 

  2.Dao

①.Customer

 

CustomerDao
public   interface  ICustomerDao
    {
        CustomerInfo Get(
object  id);

        
object  Save(CustomerInfo entity);

        
void  Update(CustomerInfo entity);
    }

public   class  CustomerDao : HibernateDaoSupport, ICustomerDao
    {
        
public   virtual   object  Save(CustomerInfo entity)
        {
            
return   this .HibernateTemplate.Save(entity);
        }

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


        
public   void  Update(CustomerInfo entity)
        {
            
this .HibernateTemplate.Update(entity);
        }
    }

 

 

 

 

CustomerDao.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 ="Customer.DbProvider"  provider ="SqlServer-2.0"
               connectionString
="Server=.;database=Customer;uid=sa;pwd=;" />

  
< object  id ="Customer.NHibernateSessionFactory"  type ="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate21" >
    
< property  name ="DbProvider"  ref ="Customer.DbProvider" />
    
< property  name ="MappingAssemblies" >
      
< list >
        
< value > Customer.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 ="Customer.HibernateTemplate"  type ="Spring.Data.NHibernate.Generic.HibernateTemplate" >
    
< property  name ="SessionFactory"  ref ="Customer.NHibernateSessionFactory"   />
    
< property  name ="TemplateFlushMode"  value ="Auto"   />
    
< property  name ="CacheQueries"  value ="true"   />
  
</ object >


  
<!--  Dao  -->
  
< object  id ="Customer.CustomerDao"  type ="Customer.Dao.Implement.CustomerDao,Customer.Dao" >
    
< property  name ="HibernateTemplate"  ref ="Customer.HibernateTemplate" />
  
</ object >

</ objects >

 

 

②.Order

 

OrderDao
public   interface  IOrderDao
    {
        
object  Save(OrderInfo entity);
    }

 
public   class  OrderDao : HibernateDaoSupport, IOrderDao
    {
        
public   virtual   object  Save(OrderInfo entity)
        {
            
return   this .HibernateTemplate.Save(entity);
        }
    }

 

 

 

 

Order.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 ="Order.DbProvider"  provider ="SqlServer-2.0"
               connectionString
="Server=.;database=Order;uid=sa;pwd=;" />

  
< object  id ="Order.NHibernateSessionFactory"  type ="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate21" >
    
< property  name ="DbProvider"  ref ="Order.DbProvider" />
    
< property  name ="MappingAssemblies" >
      
< list >
        
< value > Order.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 ="Order.HibernateTemplate"  type ="Spring.Data.NHibernate.Generic.HibernateTemplate" >
    
< property  name ="SessionFactory"  ref ="Order.NHibernateSessionFactory"   />
    
< property  name ="TemplateFlushMode"  value ="Auto"   />
    
< property  name ="CacheQueries"  value ="true"   />
  
</ object >


  
<!--  Dao  -->
  
< object  id ="Order.OrderDao"  type ="Order.Dao.Implement.OrderDao,Order.Dao" >
    
< property  name ="HibernateTemplate"  ref ="Order.HibernateTemplate" />
  
</ object >

</ objects >

 

 

三、Service

OrderManager
     public   interface  IOrderManager
    {
        
void  CreateOrder(OrderInfo order,  decimal  money);

        
object  SaveCustomer(CustomerInfo customer);
    }

public   class  OrderManager : IOrderManager
    {
        
public  ICustomerDao CustomerDao {  get set ; }

        
public  IOrderDao OrderDao {  get set ; }

        [Transaction]
        
public   void  CreateOrder(OrderInfo order, decimal  money)
        {
            CustomerInfo customer 
=  CustomerDao.Get(order.CustomerId);
            OrderDao.Save(order);
            

            
if  (customer.Money  >=   3000 )
            {
                
throw   new  Exception( " 订金额度上限 " );
            }
customer.Money += money;
            CustomerDao.Update(customer);
        }

        [Transaction]
        
public   object  SaveCustomer(CustomerInfo customer)
        {
            
return  CustomerDao.Save(customer);
        }
    }

 

 

 

Service.xml
<? xml version="1.0" encoding="utf-8"  ?>
< objects  xmlns ="http://www.springframework.net"
         xmlns:db
="http://www.springframework.net/database"
         xmlns:tx
="http://www.springframework.net/tx" >

  
< object  id ="transactionManager"
          type
="Spring.Data.Core.TxScopeTransactionManager, Spring.Data" >
  
</ object >

  
<!--  Service  -->
  
< object  id ="Service.OrderManager"  type ="Service.Implement.OrderManager,Service" >
    
< property  name ="CustomerDao"  ref ="Customer.CustomerDao" />
    
< property  name ="OrderDao"  ref ="Order.OrderDao" />
  
</ object >

  
< tx:attribute-driven />

</ objects >

 

 

四、Test

 

Test
 [TestFixture]
    
public   class  ServiceTest
    {
        
private  IApplicationContext applicationContext;
        
        [SetUp]
        
public   void  Init()
        {
            log4net.Config.XmlConfigurator.Configure();
            applicationContext 
=  ContextRegistry.GetContext();
        }

        [Test]
        
public   void  InitData()
        {
            CustomerInfo customer 
=   new  CustomerInfo
            {
                Name 
=   " 刘冬 "
            };
            IOrderManager manager 
=  (IOrderManager)applicationContext.GetObject( " Service.OrderManager " );
            manager.SaveCustomer(customer);
        }

        [Test]
        
public   void  CreateOrderTest()
        {
            IOrderManager manager 
=  (IOrderManager)applicationContext.GetObject( " Service.OrderManager " );
            manager.CreateOrder(
new  OrderInfo
            {
                Address 
=   " 中国北京 " ,
                CustomerId 
=   1 ,
                OrderDate 
=  DateTime.Now
            }, 
1000 );
        }
    }

 

 

 

App.config
<? xml version="1.0" ?>
< configuration >
  
< 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 >

    
< section  name ="log4net"  type ="log4net.Config.Log4NetConfigurationSectionHandler, log4net"   />
    
< section  name ="databaseSettings"  type ="System.Configuration.NameValueSectionHandler"   />
  
</ configSections >


  
<!-- log4net配置 -->
  
< log4net  debug ="true" >
    
< appender  name ="LogFileAppender"  type ="log4net.Appender.FileAppender" >
      
< param  name ="File"  value ="Logs\Log.log"   />
      
< param  name ="datePattern"  value ="MM-dd HH:mm"   />
      
< param  name ="AppendToFile"  value ="true"   />
      
< layout  type ="log4net.Layout.PatternLayout" >
        
< param  name ="ConversionPattern"  value ="%d [%t] %-5p %c [%x] - %m%n"   />
      
</ layout >
    
</ appender >
    
< appender  name ="HttpTraceAppender"  type ="log4net.Appender.ASPNetTraceAppender" >
      
< layout  type ="log4net.Layout.PatternLayout" >
        
< param  name ="ConversionPattern"  value ="%d [%t] %-5p %c [%x] - %m%n"   />
      
</ layout >
    
</ appender >
    
< appender  name ="EventLogAppender"  type ="log4net.Appender.EventLogAppender" >
      
< layout  type ="log4net.Layout.PatternLayout" >
        
< param  name ="ConversionPattern"  value ="%d [%t] %-5p %c [%x] - %m%n"   />
      
</ layout >
    
</ appender >
    
< appender  name ="RollingLogFileAppender"  type ="log4net.Appender.RollingFileAppender" >
      
< param  name ="File"  value ="Logs/Log.log"   />
      
< param  name ="AppendToFile"  value ="true"   />
      
< param  name ="MaxSizeRollBackups"  value ="10"   />
      
< param  name ="MaximumFileSize"  value ="100K"   />
      
< param  name ="RollingStyle"  value ="Size"   />
      
< param  name ="StaticLogFileName"  value ="true"   />
      
< layout  type ="log4net.Layout.PatternLayout" >
        
< param  name ="ConversionPattern"  value ="%d [%t] %-5p %c [%x] - %m%n"   />
      
</ layout >
    
</ appender >
    
< root >
      
< level  value ="ALL"   />
      
< appender-ref  ref ="RollingLogFileAppender"   />
    
</ root >
  
</ log4net >

  
<!-- spring配置 -->
  
< 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://Customer.Dao/Customer.Dao.Config/Dao.xml"   />
      
< resource  uri ="assembly://Order.Dao/Order.Dao.Config/Dao.xml"   />
      
<!-- Service -->
      
< resource  uri ="assembly://Service/Service.Config/Service.xml"   />

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

  
< startup >< supportedRuntime  version ="v4.0"  sku =".NETFramework,Version=v4.0" /></ startup ></ configuration >

 

 

 

运行结果如下:

一、初始化数据:

 

 

二、建立第一个订单(插入第一次数据),订金小于3000

 

三、建立第一个订单(插入第二次数据),订金小于3000

 

四、建立第一个订单(插入第三次数据),订金等于3000

 

五、建立第一个订单(插入第四次数据),订金超过3000

 

 

从运行结果上看到,当我们创建第四张订单时,由于订金超过3000后抛出异常,数据实现回滚。这样分布式事务便实现了。

 

代码下载

出处:http://www.cnblogs.com/GoodHelper/archive/2010/07/29/SpringNetDistributedTransaction1.html

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


原文链接:http://www.cnblogs.com/GoodHelper/archive/2010/07/29/SpringNetDistributedTransaction1.html
加载中
返回顶部
顶部