2009年6月28日 星期日

db4o client/server 模式

從 db4o 的 API 來看,要存取遠端資料庫伺服器跟本地端直接開啟資料庫伺服器以支援同時存取(concurrent transaction)的方法幾乎沒有甚麼不同。要使用同時存取,只要把原本開啟資料庫檔案的方式改換成開啟 db4o 伺服器的模式,並將其啟動於 port 0 即可(因而不需要任何的網路連線):
IObjectServer server = Db4oFactory.OpenServer("db.filename", 0);
try
{
    IObjectContainer client = server.OpenClient();
    // 讓 client 端做某些事情
    // ...
    client.Close();
}
finally
{
    server.Close();
}
在 db4o 中,client 端的 transaction 只處於 read commit(不過每個 client container 都維護一個 weak 參考快取,參考到它所有已知、參考到的物件)。要讓所有 client 端中未決的更新 comment 立即執行,我們可以明確的自伺服器中的更新已知的物件:
IObjectContainer client1 =server.OpenClient();
IObjectContainer client2 =server.OpenClient();
IList
result1 = client1.Query
(delegate(Article art) { return art.PMID == "7533967" && art.GM_Version == GeneMentionTagger.VERSION; }); IList
result2 = client2.Query
(delegate(Article art) { return art.PMID == "7533967" && art.GM_Version == GeneMentionTagger.VERSION; }); Article a = result1.Next(); // 修改 a 的內容 client1.Store(a); client2.Ext().Refresh(a,Int16.MaxValue); // 取得更新後的內容 client1.Commit();

遠端資料庫伺服器

要讓 db4o 能夠使用 TCP/IP 網路存取遠端的資料庫方法很簡單,只要指定連接埠大於零,並且設定一至多個 client 即可!
IObjectServer server = Db4oFactory.OpenServer("db_filename",
655);
server.GrantAccess("user", "passwd");
try
{
    IObjectContainer client = Db4oFactory.OpenClient("localhost",
    655, "user", "passwd");
    // Do something with this client, or open more clients
    client.Close();
}
finally
{
    server.Close();
}

如上例所示,client 連接提供了主機、埠號、使用者名稱和密碼的設定。

就這樣...其它的使用方式就跟在本地端一模一樣。

db4o 中的 Transparent Persistence

之前的文章提及,更新深度讓我們可以細緻化的控制物件的更新長度,以便在效能和便利上取得平衡,然而也造成下面的問題:
  • 設定特定更新深度:程式碼必須知道哪些物件對應的更新深度
  • 當更新深度為最大值時,會造成效能低落,沒用到的物件會被無意義的更新
一個解決方法也許是讓 db4o 引擎去幫你作決定。這個功能在 db4o 7.1 版時引入,稱之為「Transparent Persistence」(後文簡稱 TP)。db4o 藉由下面方式來支援 TP:
  1. 資料庫必須組態成 TransparentPersistenceSupport
  2. Persistent 類別必須實做 IActivatable 介面,該介面在 db4o 的 Activation一文中有介紹。該介面提供了 bind 方法取得 container 的 activator 物件。
  3. 當物件的欄位被修改時, db4o 的 Activation一文中提及的 Activate() 方法會被呼叫來儲存物件內容。而 db4o 的 Activation的程式碼中出現的 ActivationPurpose.Write 會在 TP 中被使用。
假設使用在 db4o 的 Activation一文中定義的 Article 物件,我們在開啟資料庫前,先設定好 TP 支援:
Db4oFactory.Configure().Add(new TransparentPersistenceSupport());
只要加上這行,再更新物件內容時,只要該物件欲更新的欄位有加上 ActivationPurpose.Write,則會被自動儲存。

db4o 的 Activation一文中提及的主要是取出物件時的情況,這裡是儲存物件的情況

db4o 的 Activation

我們在之前的文章提過關於物件更新深度的問題。db4o 的 Activation 則是設計用來簡化處理更新深度問題,達到 transpartent activation 的效果!

如同在物件更新深度一文中提到的,我們先將資料庫內塞滿 Article 物件。若是我們不再開啟資料庫前設定好更新深度的相關參數,則可能會造成取出的物件有部分內容是 null 值。

要讓 db4o 運行於 Transparent Activation 模式的方法很簡單:

Db4oFactory.Configure().Add(new TransparentActivationSupport());
就這樣,我們不需要再擔心會遇到 null 值的問題了!實際上,當啟用 Transparent Activation 模式時,任何未實做 Db4objects.Db4o.TA.IActivatable 介面的物件被使用時將會自動的 activate。很明顯的是,這種方式會造成記憶體的大量消耗,也造成載入時間變慢。要避免這種情況,我們可以替我們的類別實做 Activatable 介面。下面是我們將 Article 物件加上 Activatable 的實做:
public class Article: IActivatable
{  
   // ...

   [Transient]
   private IActivator _activator;

   private Sentence title;  
   public Sentence SentenceOfTitle  
   {  
       get { Activate(ActivationPurpose.Read);
             return title; }  
       set { Activate(ActivationPurpose.Write);
             title = value; }  
   }  
   
   // 這裡假設 IsStructure 不需要 activation
   private bool _IsStructure;  
   public bool IsStructure  
   {  
       get { // Activate(ActivationPurpose.Read);
             return _IsStructure; }  
       set { // Activate(ActivationPurpose.Write);
             _IsStructure = value; }  
   }  
   
    private List _SentencesOfAbs;  
    public List SentencesOfAbstract  
    {  
       get { Activate(ActivationPurpose.Read);
             return _SentencesOfAbs; }  
       set { Activate(ActivationPurpose.Write);
             _SentencesOfAbs = value; }  
    }  

    public override String ToString()
    {
       Activate(ActivationPurpose.Read);
       return string.Format("T:{0}", title);
    }

    // implement IActivatable
    public void Activate(ActivationPurpose purpose)
    {
        if(_activator != null)
        {
            _activator.Activate(purpose);
        }
    }
    public void Bind(IActivator activator)
    {
        if (_activator == activator)
        {
            return;
        }
        if (activator != null && null != _activator)
        {
            throw new System.InvalidOperationException();
        }
        _activator = activator;
    }
} 
一個 IActivatable 類別必須儲存 db4o 提供給 bind 方法的Activator 物件於一個 transient 變數中(上例中的 _activator 變數),並且在預被 active 的欄位被呼叫前,呼叫 Activate() 方法。假若該物件已經被 activate,這個方法會立即回傳,否則 activation 會自動發生。

db4o 的 Transaction

在 db4o 中,基本上我們對資料庫做任何動作,都處於一個 transaction 中;也就是說,當我們開啟一個 db4o 的 container,就會自動啟始一個 transaction,當我們關閉 container 時,也會自動的進行 commit。 若是我們要 rollback 回前一次的 commit 點,則可以直接呼叫 Rollback() 方法: 但是,事實上,這邊存在一個問題,那就是:當我們對於一個存資料庫取出的物件做修改後(稱為 live object),那麼我們就無法 rollback 該物件於資料庫中。 我們必須要明確的刷新我們的 live object,方法如下:

2009年6月27日 星期六

在 Javascript Array 中插入元素

方法是利用 splice;這個方法除了一般用法外,可以接受如下三個參數: Array.splice(index, howMany, [element1][, ..., elementN])
  • 第一個參數是開始移除元素的 index(從 0 開始)
  • 第二個參數是要移除多少個元素
  • 之後的參數就是要插入的元素
所以若是我們要在陣列的開頭位置插入一個元素“23",我們可以用如下的方式達成:
// info 為一個 Array
var new_array=info.splice(0,0,"23");