大家好,又見面了,我是你們的朋友全棧君。深入db4o
深入db4o
這是Rick Grehan發表在TheServerSide上的一篇關于面向對象資料庫–db4o的文章,較全面地介紹了db4o的關鍵特性,希望對大家認識db4o能有所幫助。(2007.12.07最後更新)
db4o-針對對象的資料庫-是一個完全的對象資料庫;它以使對象在其生命周期中-無論是在資料庫内或是在外-都保持着它們的本性這樣一種方式操縱對象。不論類的複雜性如何,對象的内容,結構和關系都能夠被儲存。
更準确地說,db4o是一個資料庫引擎,你隻要将它的一個jar檔案包含到你的資料庫應用的類路徑中就可以使用它了(至少對于Java是這樣的)。是以,db4o運作在與你的應用程式相同的程序空間中,并能被直接地調用;它不需要類似于在ODBC或JDBC中使用的驅動檔案。db4o存在針對Java,.NET和Mono的版本;它們在功能上都彼此相等。(事實上,使用.NET建立的db4o資料庫也能由Java程式通路;反之亦然。)
db4o是開源的。可執行檔案,源代碼和文檔可從http://www.db4objects.com/中下載下傳。廣泛的例子程式,和一個活躍的使用者社群一樣,也都可以從這個站點中找到。
db4o最引人的特性之一就是它在簡易性與強大的功能之間的顯著平衡。一方面,它的API是如此地易于掌握和友善使用,即使是初學者也能在相同的時間内建立一個功能完備的資料庫對象。另一方面,這些相同的API也提供了更底層的能夠深入調用資料庫引擎的方法,以允許核心開發者為了得到适當的性能而能深入到該引擎的内部中去調整db4o的工具。
db4o的特性就是最好的證明–這勝過隻是讨論–是以我們将通過示例這種方法去證明db4o。然而,我們必須牢記本文通篇隻是展示了db4o特性中的一部分罷了。感興趣的朋友會發現為了知曉該資料庫引擎的全部功能而去查閱db4o的文檔所花的時間是值得的。
db4o基礎
讓我們以初學者使用db4o時可能會做的事情開始:定義了一些類,然後持久化這些類的對象。我們所假定的類為同樣也是假定的QA項目做一個跟蹤測試的系統。我們的系統由兩個類組成,首先是TestSuite類:
public class TestSuite {
private String name; // Test Suite name
private String description;
private String configuration;
private char overallScore;
private ArrayList <TestCase> cases;
private long dateExec;
… <remainder of TestSuite definition> …
}
TestSuite是TestCase對象的容器,(一個測試用例就是一個可單獨執行的測試程式–相關的測試用例組合成一個測試組)。測試組使用額外的,全局的資料成員,每個資料成員的用途也是相對明顯的:configuration記錄被測試的指定系統;overallScore是對整個測試組一個簡要的評分(‘P’代表通過,’F’代表失敗,’B’代表被阻塞,等等。);dateExec是一個毫秒級的域,辨別該測試組被執行時的日期與時刻。是ArrayList對象的cases含有單個的測試用例,由TestCase類定義:
public class TestCase {
private String name;
private String comment;
private char status;
private long duration;
private float result;
… <remainder of TestCase definition> …
}
每個測試用例都有一個名稱,形式自由的注釋字段,狀态(通過或失敗),持續時間和結果(例如,為了與測試-位元組/秒的吞吐量-的任意資料關聯)。
因為我們所關注是db4o的使用,是以我們不想在描述這些類的使用細節上停留。就讓我們簡單地說,我們已經執行了一個特别地測試組中所有的測試用例,将測試的結果存放在一個TestSuite對象中(與TestCase對象相關的ArrayList對象cases中),然後關閉資料庫。是不是太容易了。
// Create the database
new File(“testsuites.YAP”).delete();
ObjectContainer db = Db4o.openFile(“testsuites.YAP”);
// Store the TestSuite object
db.set(testsuite);
// Close the database
db.close();
就是它。彈指一揮間,你就已經做到了。(當然,為了保持簡潔,我們去掉了建立TestSuite對象和它的TestCase元件的細節)
停下來想想上述代碼做了什麼事情。特别要考慮你沒有看到的–db4o已經做了但還未被告之的事情。
首先,我們不需要告訴db4o任何關于TestSuite類的結構的資訊;不需要我們的幫助,db4o就能發現這個結構。利用Java反射機制的能力,db4o測定TestSuite類的結構,并勾勒出該類的裝配方式以推導出此類對象的成員與關鍵資料。
第二,我們不必建議db4o去關注ArrayList。不僅我們不必将ArrayList的大小告訴db4o,而且我們也不必把它裡面的内容告訴db4o。正如db4o在它剛接觸testsuite對象時就能夠發現它所需要的一切,db4o也能知道它所需要的關于(在ArrayList中的)TestCase對象的所有資訊。
結果就是,如果我們把testsuite作為一個任意寵大而複雜的對象樹的根,db4o能找到并存儲整個樹而不需要我們的任何協助。是以,存儲這個處于根部的對象testsuite也就是存儲了整個ArrayList對象。
最後,我們也沒有必須要求db4o以事務的保護性方式去調用set方法。任何會修改ObjectContainer(表示資料庫的db4o對象)的調用都會自動地開啟一個事務,除非已經有一個活躍的事務了。此外還會調用close方法去終止這個事務,是以上述代碼等價于:
db.startTransaction();
db.set(testsuite);
db.commitTransaction();
db.close();
此處的startTransaction和commitTransaction方法是為了證明我們的觀點而虛構的。db4o也确實有顯示地送出或中止事務的方法,但為了使原先的代碼足夠的簡潔我們沒有使用這些方法。db4o隐式的事務使得資料庫能夠一直處于一緻的狀态;一旦commit方法已經執行了,資料庫的完整性就能夠得到保證,甚至是發生了災難性失敗也能如此。
查詢I – QBE
有了已經存于資料庫中的對象,下一步我們想要展示的操作将肯定就是查詢和恢複。db4o提供了三種查詢API:有一種簡單,有一種優雅,還有一種則複雜。每一種API都有其所長,并适用于不同的查詢條件。以db4o的眼光來看,你選擇哪一種API并沒有關系:它們都是可相容的。
我們以簡單的API開始:query by exampel(QBE)。
使用QBE是如此的容易:為你的查詢目标建構一個’模闆’對象,然後把它傳入ObjectContainer的query方法。實際上,你就是告訴db4o’去拿到所有與這個對象看起來一樣的對象’。(這與JavaSpaces查詢API非常相似;為了清楚地了解如何處理基本資料類型,可以看下面的内容,db4o的處理方式與JavaSpaces不同。也要注意,JavaSpace Entry對象期望使用public字段,db4o則沒有這種要求。)
假設一個測試組名為”Network Throughput”,我們想取出這個測試組執行的所有測試以便我們能确定失敗了的測試所占的百分比(基于TestSuite的overalScore值)。使用QBE,完成該工作的代碼如下:
// Open the database
ObjectContainer db = Db4o.openFile(“testsuites.YAP”);
// Instantiate the template object, filling in
// only the name field
testTemplate = new TestSuite(“Network Throughput”);
// Execute the query
ObjectSet result = db.get(testTemplate);
fails = 0.0f;
total = 0.0f;
// Step through results,
while(result.hasNext())
{
testsuite = (TestSuite)result.next();
if(testsuite.getOverallScore()==’F’)
fails += 1.0f;
total += 1.0f;
}
if(total == 0.0f)
System.out.println(“No tests of that type found.”);
else
{
System.out.println(“Percent failed: ” + (fails/total * 100.0f) + “%”);
System.out.println(“Total executed: ” + total);
}
db.close();
在上面的代碼中,testTemplate是QBE的模闆對象。注意,隻有它的name字段有真實的值;所有其它的成員變量不是為null就是為0。Null或0字段不參與QBE查詢;是以,調用db.get方法就會傳回在該資料庫中name字段比對”Network Throughput”的所有TestSuite對象。比對的TestSuite對象将傳回在一個ObjectSet結果對象中。上述代碼周遊該結果,取出對象,然後計算結果并展示出來。
QBE明顯的優點就是它的簡易性。不需要掌握其它單獨的查詢語言。另外,QBE也是類型安全的:你不需要建立一個類似于SQL的查詢語句
SELECT TestSuite.overallScore FROM TestSuite WHERE TestSuite.name = 200.0
另一方面,由于該查詢是由Java代碼建立的,編譯器不會允許你把一個浮點值賦給一個String字段;反之亦然。
QBE明顯的缺點就是它隻能執行”等于”查詢。另外,QBE使用null值去确定不參與查詢的String或對象引用成員變量,使用0值去指定不參與查詢的數字字段。是以,例如,我不能發明一個QBE查詢去獲得所有result字段的值為0的TestCase對象。
更為精細的查詢要求一個能力更強的查詢機制,而db4o恰恰就有這樣一種機制。
查詢方式II – 原生查詢(Native Query)
db4o的原生查詢系統應該是能想像得到的最具彈性的查詢機制。不像使用資料庫查詢語言去建構查詢,你是使用”無格式的普通Java語句”去構造原生查詢。原生查詢用兩種手段去實作這件不可思意的工作:一個是Predicate類;另一個是QueryComparator接口。這個類包含一個可重載的(overrideable)回調方法,該方法将指定如何從資料庫中選擇對象(如果你願意,你将會看到查詢語句的主體….)。這個接口隻聲明了一個方法,該方法用于指明如何對查詢結果進行排序。
作為一個例子,我們假設想找到在給定的一周内執行過了的總得分為”failed”,但與之關聯的測試用例中有超過一半的被評為”passed”的測試組。這不是一個簡單的”等于”查詢,是以它不能使用QBE建構。
然而,db4o的原生查詢可以直接地生成該查詢。首先,我們繼承db4o的Predicate類:
// Predicate class sub-class for native query example
public class NativeQueryQuery extends Predicate<TestSuite>
{
ObjectContainer db;
private long startdate;
private long enddate;
// 構造器要在本地獲得ObjectContainer對象和日期範圍
public NativeQueryQuery(ObjectContainer _db,
long _start, long _end)
{
db = _db;
startdate = _start;
enddate = _end;
}
// 這就是查詢的主體
public boolean match(TestSuite testsuite)
{
float passed;
float total;
TestCase testcase;
// 判斷testsuite是否在指定的日期範圍内
if(testsuite.getDateExec()<startdate ||
testsuite.getDateExec()>enddate) return false;
// 如果該測試組對象中沒有測試用例對象,則拒絕将該測試組對象放入查詢結果中
if(testsuite.getNumberOfCases()==0)
return false;
// 檢查該測試組對象中的測試用例的通過率是否超過50%
passed = 0.0f;
total = 0.0f;
for(int i=0; i<testsuite.getNumberOfCases(); i++)
{
testcase = testsuite.getTestCase(i);
if(testcase.getStatus()==’P’)
passed+=1.0f;
total+=1.0f;
}
if((passed/total)<.5) return false;
return true;
}
}
注意在這個類的使用中使用了Java泛型語義,這就是告訴db4o隻去取TestSuite對象。當查詢執行時,TestSuite對象就會傳入match方法(我們之前提到過的回調方法),如果傳入的TestSuite對象符合查詢規範該方法就傳回true,否則就傳回false。
match方法中的代碼首先确定侯選對象是否是在一周的日期範圍内。如果是,則循環該對象中的成員變量測試用例的對象,計算出所有通過了的測試用例的總數。如果,得到的通過率小于50%,該測試組就被拒絕;否則,就讓它通過。
我們可以使用如下的代碼準确地展示該查詢程式:
. . .
TestSuite testsuite;
NativeQueryQuery nqqClass;
Date now;
// Open the database
ObjectContainer db = Db4o.openFile(“testsuites.YAP”);
// Instantiate a NativeQueryQuery object,
// setting the start and end dates for
// any test in the past week
// 604800000 = milliseconds in a week
now = new Date();
nqqClass = new NativeQueryQuery(db,
now.getTime()-604800000L,
now.getTime());
// Execute the query and display the
// results
System.out.println(“Results:”);
ObjectSet results = db.query(nqqClass);
if(results.isEmpty())
System.out.println(” NOTHING TO DISPLAY”);
while(results.hasNext())
{
testsuite = (TestSuite)(results.next());
System.out.println(testsuite.toString());
}
db.close();
. . .
可以把原生查詢想像成這樣:目标類的對象一個接一個從資料庫中取出,然後把它們傳入match方法中。隻有那些被match方法傳回true的對象才會置于查詢結果ObjectSet對象中。基本上可以說,如果你會知道如何寫Java代碼,那麼你就知道如何寫原生查詢。
那麼排序呢?如果想按日期的升序排列查詢結果,我們就要實作QueryComparator接口,如下所示:
public class NativeQuerySort implements QueryComparator<TestSuite>{
public int compare(TestSuite t1, TestSuite t2)
{
if (t1.getDateExec() < t2.getDateExec()) return -1;
if (t1.getDateExec() > t2.getDateExec()) return 1;
return 0;
}
}
compare方法的作用十分明顯。那些在查詢中得以勝出的對象會成對的傳入compare方法,如果第一個對象會排在第二個對象之前,相同或之後的位置,該方法就會分别傳回一個小于,等于或大于0的值。為了準确地說明對查詢結果的排序,我們執行個體化NativeQuerySort,并把對query方法的調用修改成如下:
. . .
// Instantiate the sort class
nqsClass = new NativeQuerySort();
. . .
ObjectSet results = db.query(nqqClass, nqsClass);
. . .
其它的代碼仍然與原先的保持一緻。
好懷疑的讀者可能會抱怨道,原生查詢隻是一種程式設計上的小伎倆–相比較于直接去拿到所有的TestSuite對象然後再排除其中不符合條件的對象這樣的程式,原生查詢并不快。
是的,但并不十分準确。原生能夠被優化。你所需要做的隻是把兩個jar檔案–db4o-xxx-nqopt.jar(xxx表示db4o的版本)和bloat.jar–置于CLASSPATH環境變量中。在查詢執行的時候,這些類庫中的代碼會對(在match方法中)例如基本資料類型比較,算術和布爾表達式,簡單的對象成員通路,以及更多方面的結構進行優化。這個被支援的優化的清單在不停的增長,因為db4o引擎還在擴充優化的範圍。
查詢方式III – S.O.D.A.
db4o獨一無二的能力之一就是它的API被分層了。開發者能夠選擇通過高層次–賦予資料庫引擎相當大的自由度,讓它決定如何去實作它的操作–或者開發者也可以使用一種更直接地方式去通路db4o。後一種選擇為程式員平添了更多的負擔,程式員必須更加小心地引導資料庫引擎的内部工作。但回報就是得到一個更快,能力更強的資料庫應用。
db4o的S.O.D.A.(Simple Object Data Access)查詢機制就是該層次API的一個完美的例子。S.O.D.A.就是db4o的内部查詢系統–QBE和原生查詢都被翻譯成了S.O.D.A.。然而,應用程式也能直接地調用S.O.D.A.。
假設我們想找到所有名為”Network Throughput”,且至少擁有一個其result字段–我們使用這個參數作為位元組/秒的量度–不小于指定值(比方說,100)的測試用例的測試組。為該請求而做的S.O.D.A.查詢可能就像這樣:
. . .
TestSuite testsuite;
// Open the database
ObjectContainer db = Db4o.openFile(“testsuites.YAP”);
// Construct the query
Query query = db.query();
query.constrain(TestSuite.class);
Constraint nameConst = query.descend(“name”).
constrain(“Network Throughput”);
query.descend(“cases”).descend(“result”).
constrain(100.0f).smaller().and(nameConst);
System.out.println(“Results:”);
// Execute the query
ObjectSet result = query.execute();
if(result.isEmpty())
System.out.println(“NOTHING TO DISPLAY”);
while(result.hasNext())
{
testsuite = (TestSuite)(result.next());
System.out.println(testsuite.toString());
}
db.close();
. . .
在由Illustration 1所示圖表的幫助下,這些有點兒神秘的代碼就變得不那麼神秘了。該程式所建構的歸總起來就是一個用于指導底層資料庫引擎的查詢圖(Query Graph)。descend方法建立了該圖的一個分支,該分支向下步入對象的結構中。每個descend方法就在這個樹中建構一個結點,可以在這些結點上再附上一個限制(使用constrain方法)。用SQL的話來說,限制指定了查詢的”WHERE”子句部分。多個限制可以在與(and)或或(or)方法的協助下結合起來。在上面的查詢中我們已經使用了and方法去關聯這些限制。
與其它的查詢方式一樣,查詢結果傳回到ObjectSet對象中,通過周遊該對象就可取出那些拿到的對象。
注意,由于S.O.D.A.是一種低層次的通路方法,沒有智能的訓示,它就沒有預設的行為。通路cases對象的成員變量result字段的代碼很簡單
query.descend(“cases”).descend(“result”). …
我們并沒有告訴S.O.D.A.”cases”是一個集合對象。是以當查詢執行時,它會不被察覺地檢測ArrayList對象cases中所有元素(TestCase對象)的result字段,然後會正确地傳回那些擁有符合搜尋規範的測試用例的測試組。
db4o性能調優
我們已經展示了db4o的基本操作(但無關緊要的删除操作除外,下面将會提到它)。但,正如我們在本文通篇所提到的,db4o釋出(expose)了一個API層次結構,以允許開發者能夠選擇以何種層次的API去控制建立在該資料庫引擎之上的應用程式。從另一方面看,如果你所想做的隻是向資料庫存入,及從資料庫取出對象,那麼你就已經看到了你所需要的一切。然而,如果你的應用的需求超出了添加,更新,查詢和删除,可能還有一個db4o的特性可解決你的問題。
db4o的ObjectContainer實際上釋出(expose)了兩個API。第一個API非常的簡單,由十個方法組成。這些方法處理資料庫的打開與關閉;添加,更新,查詢和删除對象;及送出或中止事務。短言之,該API為你提供了在操縱資料庫時所需要所有功能。然而,該API中的一個方法–ext()–是進入”被擴充的”ObjectContainer的一個入口。該被擴充的ObjectContainer為深入控制db4o的内部釋出(expose)了更多方法。例如,你可以獲得并改變資料庫的配置上下文,使用它你能夠修改該引擎的行為。
例如,假設你已經從資料庫中拿到了一個TestSuite對象,發現該對象中的資料是錯誤的,并決定該對象應該被删除。此外,你還決定你必須删除的不僅是該TestSuite對象,而且還有所有與之關聯的TestCase對象(在ArrayList對象cases中)。
你是可以冗長而乏味地周遊這個ArrayList對象,一個接一個地删除每一個TestCase對象,然後再删除TestSuite對象本身。可能一種更好的解決方案是為這個TestSuite對象啟用db4o的”級聯删除”特性。
. . .
// Fetch the database’s configuration context
Configuration config = db.ext().configure();
// Get the ObjectClass for TestSuite
ObjectClass oc = config.objectClass(“testsuites.TestSuite”);
// Turn on cascaded delete
oc.cascadeOnDelete(true);
… …
db.delete(ts1);
. . .
在上述代碼中,我們執行個體化了一個ObjectClass對象,該對象使我們能夠通路到TestSuite對象的db4o内部表現形式。我們打開cascadeOnDelete标記,以便當執行db4o.delete(ts1)時,不僅ts1對象會被删除,而且所有由ts1引用的TestCase對象也會被删除。(由于顯而易見的原因,預設情況下,級聯删除是被關閉的)
作為另一個例子,假設你想為資料庫預配置設定存儲空間,以至于要最小化磁盤驅動器的頭移動(head movement)。(最好是在硬碟碎片整理之後,并新建立一個資料庫時這樣做。)并且假設以ObjectContainer對象db作為打開了的資料庫:
// Fetch the database’s configuration context
// 獲得資料庫的配置上下文
Configuration config = db.ext().configure();
// Pre-allocate 200,000 bytes
// 預配置設定200000000位元組
config.reserveStorageSpace(200000000L);
把資料庫檔案預擴充到200000000位元組(略小于200兆位元組)。假設該磁盤已經做碎片整理了,那麼被配置設定的塊就是連續的,這可顯著提升資料庫的通路。
db4o進階應用
完全可以說,db4o在它不大的空間(約500K)内已裝入了足夠多的特性,相比較于db4o在運作過程中所做的衆多事情,我們不能花費更多的筆墨去解釋它們了。但是有兩個特性十分突出,以至于肯定要提到它們。
db4o的對象複制實作了被總稱為面向對象版的資料庫同步。使用複制所提供的功能,你能為一個資料庫中的一個對象做一個副本,并将該副本放入另一個資料庫中。使用這樣的方法,副本對象就無形中和原始對象關聯在了一起。對任一對象–原始對象或副本對象–的改變都會被跟蹤到,以便在之後的某個時候,資料庫能夠被重組,并且這兩個資料庫中對象的不同之處可被分解(例如,可同步這兩個資料庫)。
它工作起來就像這樣:為使一個資料庫可被複制,與事務計數器一樣,該資料庫中被建立的任何一個對象都用一個唯一全局辨別符(UUID)進行了标記。當你從原始資料庫中”複制”一個對象到另一個資料庫中,副本對象會帶着與它的原始對象相同的UUID和事務計數器。副本資料庫現在就可以從它的原始資料庫那兒弄走了。修改副本對象的内容将會導緻對象的事務計數器被修改。是以,當這兩個資料庫重新連接配接起來,db4o内建的同步處理機制就能一個對象一個對象地進行正确的比對(使用UUID),并确定原始或副本對象是否已經改變了。db4o甚至能追蹤到每個對象發生最後一次修改時的時間,以便使用者寫的沖突解決代碼能确定哪個對象是最近更新的。
從操作行為來看,db4o的同步處理機制與原生查詢十分相似。回想一下,當實作了一個原生查詢類時,我們要定義一個match方法,該方法确定哪些對象符合(或不符合)查詢規範。使用同步複制,我們要定義一個ReplicationProcess對象,我們會把沖突處理對象傳入該方法中。這些Java代碼可能像這樣:
. . .
ReplicationProcess replication = db1.ext().
replicationBegin(db2, new ReplicationConflictHandler()
{
public Object resolveConflict(
ReplicationProcess rprocess, Object a, Object b)
{
. . . …
return winning_object;
}
)
};
在上述代碼中,Object a是來自于資料庫db1的對象,Object b則來自于資料庫db2。預設情況下,同步是雙向的。同步處理保證勝出的對象(由resolveConflict方法傳回的對象)能存入這兩個資料庫中。是以當複制完成時,這兩個資料庫中被複制的對象就同步了。
最後,db4o最強大的特性之一就是它能毫不費力地容忍類結構的演變。假設,在向資料庫内加入數百個TestSuite對象之後,我們決定這個類必須要被修改。就是說,我們已經被告之,該系統必須能追蹤到每個TestSuite的執行QA工程師,是以必須加入如下字段
private int engineerID;
到TestSuite類的定義中。
現在我們就遇到了兩個相關聯的問題。第一個問題不是很糟糕:已存在于資料庫中的用于表示測試的TestSuite對象,并沒有為它們記錄QA工程師的ID,是以我們将不得不将一個虛拟的值賦給這些對象的engineerID字段;該值會指出”未記錄QA工程師的ID”。第二個問題就更難應付了:我們必須不知原因地把已有的TestSuite對象移植到”新的”類結構中。我們必須為資料庫中所有已存在的TestSuite對象加上一個engineerID字段。除了把舊資料庫中的對象複制到一個中間性的檔案,然後重新建立一個資料庫這種方法之外,我們還能怎麼做呢?
幸運地是,使用db4o,我們确實什麼都不用做。為了能操縱新的engineerID字段,以完成業務邏輯上所要求的變化,如果就隻是向(我們的應用程式中的)TestSuite類添加一個engineerID字段,我們完全不必觸動db4o API的任何調用。當db4o使用”新的”TestSuite類結構去讀取”舊的”TestSuite對象,db4o将認為這些對象中的engineerID字段消失了,并且會優雅地把該字段的值設為0。如果我們把0當作”未記錄QA工程師的ID”,那麼我們所做的移植就完成了。寫入到資料庫中的新TestSuite對象将會包括新字段。(事實上,對舊的TestSuite對象本身進行重寫,會使db4o不被察覺地為這些對象加上新字段。)是以,通過釋出一個包含新的TestSuite定義的更新應用,我們完全可以不被察覺地從舊的TestSuite對象移植到新的…正如前述應用所做的那樣。
全方位資料庫
通過适當地應用,db4o就能成為資料庫中的”瑞士軍刀”。它占用足夠小的記憶體空間,使它能夠被包含在一個不需要消耗大量資源的項目中。同樣地,一個資料庫隻在磁盤上占用一個檔案的事實可能會使人們在第一眼看到它時,不能認識到它豐富的功能。将資料庫從一個地方移到另一個地方就是一個簡單的檔案拷貝;你不必擔心分離的索引檔案,資料檔案,資料庫結構檔案等等這些檔案的位置。對于快速部署和零管理的資料庫應用,db4o很能勝任。
另外,根據我們已多次描述過的,db4o在簡單性和優雅之間達到了适度的平衡。db4o的QBE既如此的簡單,又如此的功能強大,對于一組令人驚奇的應用,它經常是我們唯一需要的查詢API。如果你主要是通過指針導航而不是查詢去通路資料庫,QBE就特别有吸引力。在這種情況下,QBE經常能高效地拿到一個對象網絡(Object Network)的根對象。然後,你就能使用db4o的激活(activation)功能從根部向下進行對象引用導航,如果這些對象都完全在記憶體中的話,你就更可以這麼做了。
而在使用QBE并不高效的時候,原生查詢和S.O.D.A.就能派上用場了,并且它們伴随着一堆特性和低層次的API。我們還沒有展示db4o的加密功能,插入式檔案I/O(例如,它允許你添加”寫後讀(read-after-write)”驗證),信号量,用戶端/伺服器端模式,以及其它難以計數的功能。我們結論性的建議很簡單:當你的下一個Java應用需要一個資料庫,在最終開始編碼之前,你可以去通路一下http://www.db4objects.com/。這是很值得的。
釋出者:全棧程式員棧長,轉載請注明出處:https://javaforall.cn/162598.html原文連結:https://javaforall.cn