實踐篇(3)--關於事務處理的一點細節

 很早就注意到了微軟的MTS COM+,但是卻很少使用,主要原因是因爲它的第一次執行太慢,耗費的資源比較多。在服務器配置比較樂觀的項目裏,比如某移動公司和某知名跨國日化生產商的系統裏,恰當地使用COM+反而使得整體負載和程序的框架變得很清晰。

    在castle中集成了NHibernateIntegration對NHibernate進行了集成,對Session和Transaction進行了很好的封裝。但是在對發佈的beta版進行了最基本的測試後,我發現這個開源的東西還比較簡陋。於是通過SVN獲取了最新的開發版本,發現已經完全重寫了。所以我就一個經典的轉賬來看看他們各有怎樣的表現.


測試場景:
  我們假設一個娛樂中心要通過銀行把錢轉給在本中心娛樂的那些玩家中的獲獎者,娛樂中心的賬號設在bank1;而獲獎者的賬號都在bank2.  以下是轉賬的規則:

1) 娛樂中心沒有錢肯定不能轉帳;
2) 一次轉賬額不能超過5000;
3) 銀行賬號不能有重複;

    在很多的經典例子中,都太簡單,這裏我們模擬在每次都轉到新開賬號上,如果對第二次轉帳到同一玩家,您講看到一個異常。我們看看這兩個容器是否能經受考驗!

以下就是針對COM+ 的程序片斷:

using System;
using System.Data;

using System.Reflection;

using System.EnterpriseServices;
using System.Runtime.InteropServices;
using Dao;


[assembly: ApplicationName(
"COM+ demo")]
[assembly: ApplicationActivation(ActivationOption.Library)]

namespace Services {
    [Transaction(TransactionOption.Required)] 
    [ObjectPooling(Enabled
=true,MinPoolSize=2,MaxPoolSize=4)]
    
public class MyService:ServicedComponent {
    

        
public MyService() {
            Console.WriteLine(Assembly.GetExecutingAssembly().FullName);
        }

        [AutoComplete]
        
public virtual void Transfer( string from_BankTag,string from_uid,
            
string to_BankTag ,string to_uid,
            
int money) {
            
if(money<0return ;

                
            Withdraw(from_BankTag,from_uid,money);
            CreateNewAccount(to_BankTag,to_uid,money);
        }


        


        
        [AutoComplete]
        
public virtual void Withdraw(string db, string uid,int money) {
            AccountDAO dao
=new AccountDAO(db);
            BankDataSet.AccountsRow acc
=dao.getAccount(uid);
            
if(acc==nullthrow new Exception("不存在!"+uid);
            
if(acc.Balance<money )    throw new Exception("餘額不足!");
            
if (5000<money) throw new Exception("一次不能超過:"+5000);
            
            acc.Balance
-=money;
            dao.saveTable(acc.Table);
        }

        [AutoComplete]
        
public void InitDbs() {
            AccountDAO dao1
=new AccountDAO("demo1");
            AccountDAO dao2
=new AccountDAO("demo2");
            
            dao1.cleanTable();
            dao2.cleanTable();
            CreateNewAccount(
"demo1","alex",10000);
            CreateNewAccount(
"demo2","cx",0);
        }


        
public int getBalance(string db,string uid) {
            AccountDAO dao
=new AccountDAO(db);
            Dao.BankDataSet.AccountsRow acc
= dao.getAccount(uid);
            
if(acc==nullreturn 0;
            
return acc.Balance;
            
        }

        
private void CreateNewAccount(string db,string uid,int money) {
            AccountDAO dao
=new AccountDAO(db);
            
            BankDataSet.AccountsDataTable tbl
=dao.getScheme();

            BankDataSet.AccountsRow r
=tbl.NewAccountsRow();

            r.ID
=Guid.NewGuid().ToString();
            r.UserCode
=uid;
            r.Balance
=money;
            tbl.Rows.Add(r);

            dao.saveTable(tbl);
        }


    }

}


在MTS裏運行COM+,您的相關組件必須使用強名稱,請注意要把
[assembly: AssemblyVersion("1.0.0.0")] 明確設定,否則每次運行在組件管理其中都會產生新的版本。

我們再看看NHibernateIntegration的表現如何:

using System;
using System.Collections;

using Demo.Entities;
using Castle.Services.Transaction;
using Castle.Facilities.NHibernateIntegration;

using NHibernate;
using NHibernate.Expression;

namespace Demo {

    [Transactional]
    
public class AccountDao:NHibernateGenericDao {
        
private readonly ISessionManager sessManager;

        
private readonly string dbTag;

        
public AccountDao(ISessionManager sessManager):base(sessManager) {
            
this.sessManager = sessManager;
            
this.dbTag=Constants.DefaultAlias;
        }

    
        
public AccountDao(ISessionManager sessManager,string DbTag):base(sessManager) {
            
this.sessManager = sessManager;
            
this.dbTag=DbTag;
        }


        [Transaction(TransactionMode.Requires)]
        
public virtual void Deposit( string uid,int money) {
        
            
using(ISession session = sessManager.OpenSession(dbTag)) {
                Account acc
=getAccount(session,uid);
                acc.Balance
+=money;
                session.Save(acc);
                
//session.Flush();
                
            }

            
        }


        
private int getMaxMoney() {
            
return 5000;
        }


        
public  Account getAccount(string uid) {
            
using(ISession session = sessManager.OpenSession(dbTag)) {
                IList list
=session.CreateCriteria(typeof(Account)).Add(
                
new NHibernate.Expression.EqExpression("UserId",uid)).List();
                
if(list!=null&&list.Count>0return list[0as Account;
                
return null;

            }

        }


        
private Account getAccount(ISession session,string uid) {
            IList list
=session.CreateCriteria(typeof(Account)).Add(
                
new NHibernate.Expression.EqExpression("UserId",uid)).List();

        
//    Account[] accs=this.FindAllWithCustomQuery("from Account where UserId='"+uid+"'") as Account[];
            if(list==null||list.Count<1return null;//throw new Exception("帳戶不存在!");
            return list[0as Account;
            
        }

    
        
public virtual int getBalance( string uid) {
            
using(ISession session = sessManager.OpenSession(dbTag)) {
                Account acc
=getAccount(session,uid);
                
return acc.Balance;
            }

        }

        [Transaction(TransactionMode.Requires)]
        
public virtual void CrateNewAccount( string uid,int money) {
            
using(ISession session = sessManager.OpenSession(dbTag)) {
                Account acc
=new Account();
                acc.UserId
=uid;
                acc.Balance
=money;
                session.Save(acc);
                session.Flush();
                
//沒有上面這句提交事務時發生問題,但也無法回滾了。
            }

        }


        [Transaction(TransactionMode.Requires)]
        
public virtual void Withdraw( string uid,int money) {
            
using(ISession session = sessManager.OpenSession(dbTag)) {
                Account acc
=getAccount(session,uid);
                
if(acc.Balance<money ) throw new Exception("餘額不足!");
                
if (getMaxMoney()<money) throw new Exception("一次不能超過:"+getMaxMoney());
                
                acc.Balance
-=money;
                session.Save(acc);
                session.Flush();
                
            }

            
        }


        
    }

}


而上層的服務代碼如下:
using System;
using System.Collections;

using Demo.Entities;

using NHibernate;
using Castle.MicroKernel;
using Castle.Services.Transaction;

namespace Demo {

    
/// <summary>
    
/// BlogDao 的摘要說明。
    
/// </summary>

    [Transactional]
    
public class BankService {
        
private  Castle.MicroKernel.IKernel _container; 

        
public BankService(IKernel k) {
            _container
=k;
                        
        }


        

        [Transaction(TransactionMode.Requires)]
        
public virtual void Transfer( string from_BankTag,string from_uid,
            
string to_BankTag ,string to_uid,
            
int money) {
            
if(money<0return ;

            AccountDao fromDao
=_container[from_BankTag] as AccountDao;
            AccountDao toDao
=_container[to_BankTag] as AccountDao;

            fromDao.Withdraw(from_uid,money);

            toDao.CrateNewAccount(to_uid,money);
//演示提交事務時發生問題

        
//    Account acc=toDao.getAccount(to_uid);
        
//    if(acc==null) toDao.CrateNewAccount(to_uid,0);
        
//    toDao.Deposit(to_uid,money);
            
        }


    
    }

}
castle配置文件如下:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    
<facilities>
        
<facility id="nhibernate">
            
<!-- 數據庫一 -->
            
<factory id="demo1">
                
<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=demo1;Uid=sa;Pwd=</item>
                    
<item key="hibernate.dialect">NHibernate.Dialect.MsSql2000Dialect</item>
                
</settings>
                
<resources>
                    
<resource  name="./Account.hbm.xml" />
                
</resources>
            
</factory>
            
<!-- 數據庫二 -->
            
<factory id="demo2" alias="db2">
                
<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=demo2;Uid=sa;Pwd=</item>
                    
<item key="hibernate.dialect">NHibernate.Dialect.MsSql2000Dialect</item>
                
</settings>
                
<resources>
                   
<resource  name="./Account.hbm.xml" />
                
</resources>
            
</factory>
        
</facility>
        
    
</facilities>

    
<components>    
        
         
<component id="bank2" >
          
<parameters>
                
<DbTag>db2</DbTag>
            
</parameters>
         
</component>
       
</components>
</configuration>


通過改造NHibernateIntegration,我們可以在輸出窗口看到執行過程:
1) 啓動Transfer事務;
2) 啓動Withdraw事務;
3) 完成Withdraw處理;
4) 啓動CrateNewAccount處理;
5) 完成CrateNewAccount處理;
6) 對bank1和bank2進行數據庫事務提交;
7) 完成Transfer事務;

結論:
首次執行COM+明顯慢一些;
COM+的邏輯沒有發生混亂;而NHibernateIntegration在沒有及時進行緩存刷新時發生了嚴重的邏輯錯誤並且不能回滾。


另外我在一些有遞歸的情況下進行測試發現:使用延遲加載會出現session closed 或者訪問空值得異常情況。
讓我們期待NHibernateIntegration的完善吧......

alex

 


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章