2008年9月12日 星期五

db4o 與結構化的物件;如何處理物件間的關連

我們導入另一個更複雜的物件 Article:

儲存結構化的物件

如同儲存一般物件一樣,我們也是呼叫 Set() 方法來儲存 Article 物件。與 Article 物件關連的 Sentence 物件會被自動儲存!
string pmid = "123";
string title = "sent0";
Article article = new Article(pmid, title);
List abs=new List();
abs.Add(new Sentence("sent1","METHOD"));
abs.Add(new Sentence("sent2","METHOD"));
abs.Add(new Sentence("sent3","CONCLUSION"));
article.SentencesOfAbstract=abs;

db.Set(article);
上面的程式碼相當於我們手動將各物件存入資料庫:
List abs=new List();
abs.Add(new Sentence("sent1","METHOD"));
abs.Add(new Sentence("sent2","METHOD"));
abs.Add(new Sentence("sent3","CONCLUSION"));
db.Set(abs);

string pmid = "123";
string title = "sent0";
Article article = new Article(pmid, title);
article.SentencesOfAbstract=abs;
db.Set(article);

取回結構化物件

QBE

要取回所有的 Article 物件,我們跟以前一樣,只要提供一個空的 prototype 即可:
Article proto = new Article(null,null);
IObjectSet result = db.Get(proto);
ListResult(result);
當然,我也可以取回所有的 Sentence 物件!
Sentence proto = new Sentence(null,null);
IObjectSet result = db.Get(proto);
ListResult(result);
我們也可以取得所有 Article 物件中滿足 section 為 TITLE 的 Sentence 物件:
Sentence title = new Sentence(null,"TITLE");
Article article = new Article(null,title);
IObjectSet result = db.Get(article);
ListResult(result);

Native Queries

對結構化物件使用 Native Query 跟一般物件幾乎一模一樣。下面示範如何取回 一群 Article 物件,使其 Title Sentence 滿足指定的參數:
/// C# .NET 1.1
public class RetrieveArticlesByTitlePredicate : Predicate
{
   readonly string _title;
   public RetrieveArticlesByTitlePredicate(string title)
   {
      _title = title;
   }
   public bool Match(Article candidate)
   {
      return candidate.SentenceOfTitle.RawText == _title;
   }
}

// 使用方法
string title = "sent0";
IObjectSet results = db.Query(new
RetrieveArticlesByTitlePredicate(title));
ListResult(results);

/// C# .NET 2.0
string title = "sent0";
List
results = db.Query
(delegate(Article article) { return article.SentenceOfTitle.RawText == title; }); listResults(results);

SODA Query API

使用 SODA 的話,必須 descend 兩次以便取得 rawText:
IQuery query = db.Query();
query.Constrain(typeof(Article));
query.Descend("title").Descend("rawText")
.Constrain("sent0");
IObjectSet result = query.Execute();
ListResult(result);
另外,我們也可以藉由指定 SentenceOfTitle 欄位來達到相同的效果:
IQuery query = db.Query();
query.Constrain(typeof(Sentence));
Sentence proto = new Sentence("sent0", null);
query.Descend("title").Constrain(proto);
IObjectSet result = query.Execute();
ListResult(result);
下面我們示範如何取得所有 PMID 為 123 的 SentencesOfAbstract 欄位:
IQuery artQuery = db.Query();
artQuery.Constrain(typeof(Article));
artQuery.Descend("pmid").Constrain("123");
IQuery sentQuery = artQuery.Descend("_SentencesOfAbs");
IObjectSet result = sentQuery.Execute();
ListResult(result);

更新結構化物件

如同一般物件一般,要更新結構化物件,也是對它們再次呼叫 Set() 方法:
IObjectSet result = db.Get(new Article("123",null));
Article found = (Article)result.Next();
found.SentenceOfTitle = new Sentence("sentXYZ",null);
db.Set(found);
下面我們嘗試直接更新 SentenceOfAbstract 物件內容:
IObjectSet result = db.Get(new Article("123",null));
Article found = (Article)result.Next();
List abs=new List();
abs.Add(new Sentence("sent1","INTRODUCTION"));
abs.Add(new Sentence("sent2","METHOD"));
abs.Add(new Sentence("sent3","CONCLUSION"));
found.SentenceOfAbstract=abs;
db.Set(found);
看起來似乎一切都很簡單、美好?但是實際上,若是我們再度存取資料庫,檢查我們的更新:
result = db.Get(new Article("123",null));
ListResult(result);
我們會發現,更新並未成功!這是因為效能上的考量,db4o 不會自動的更新結構化物件內的所有成員類別!為了兼顧效能與程式碼撰寫的簡潔,db4o 導入了 update depth 的概念,來控制程式在更新時,物件成員樹的更新深度。

update depth

對於所有的物件而言,預設的 update depth 是 1,代表只有 primitive 以及 String 成員會被更新。

db4o 提供了相當精細的方法來控制 update depth。下面我們藉由設定 CascadeOnUpdate 為 true,指定 db4o 完整的更新 Article 物件的內容:

Db4oFactory.Configure().ObjectClass(typeof(Article))
.CascadeOnUpdate(true);
// 接下來才去設定 Article 物件內容
要注意的地方是,先設定好 update depth 後才去更新才有效。

刪除結構化物件

一樣,用 Delete() 方法即可:
IObjectSet result = db.Get(new Article("123",null));
Article found = (Article)result.Next();
db.Delete(found);
result = db.Get(new Car(null));
我們應該會認為 db4o 應該也會把 Article 物件內一堆的 Sentence 物件也刪除了吧?但是實際上, db4o 並不會幫我們做這件事!

遞迴刪除結構化物件內成員

跟更新時一樣,我們可以設定 db4o,幫我們完成這件事:
Db4oFactory.Configure().ObjectClass(typeof(Article))
.CascadeOnDelete(true);

// 接下來才去刪掉
// ...
另外要注意的一點是,目前的 db4o 版本(6.0)中,就算我們要刪除的資料庫物件,目前仍然被程式內的變數參考到,它們經由遞迴刪除後,一樣會被從資料庫內移除。

1 則留言:

H.-J.Dai 提到...

關於 update depth 的部分:CascadeOnUpdate 的設定必須要在資料庫開啟前就設定好了!