John Jiang

          a cup of Java, cheers!
          https://github.com/johnshajiang/blog

             :: 首頁 ::  :: 聯(lián)系 :: 聚合  :: 管理 ::
            131 隨筆 :: 1 文章 :: 530 評(píng)論 :: 0 Trackbacks
          深入db4o
              這是Rick Grehan發(fā)表在TheServerSide上的一篇關(guān)于面向?qū)ο髷?shù)據(jù)庫--db4o的文章,較全面地介紹了db4o的關(guān)鍵特性,希望對(duì)大家認(rèn)識(shí)db4o能有所幫助。(2007.12.07最后更新)

              db4o-針對(duì)對(duì)象的數(shù)據(jù)庫-是一個(gè)完全的對(duì)象數(shù)據(jù)庫;它以使對(duì)象在其生命周期中-無論是在數(shù)據(jù)庫內(nèi)或是在外-都保持著它們的本性這樣一種方式操縱對(duì)象。不論類的復(fù)雜性如何,對(duì)象的內(nèi)容,結(jié)構(gòu)和關(guān)系都能夠被保存。
              更準(zhǔn)確地說,db4o是一個(gè)數(shù)據(jù)庫引擎,你只要將它的一個(gè)jar文件包含到你的數(shù)據(jù)庫應(yīng)用的類路徑中就可以使用它了(至少對(duì)于Java是這樣的)。所以,db4o運(yùn)行在與你的應(yīng)用程序相同的進(jìn)程空間中,并能被直接地調(diào)用;它不需要類似于在ODBC或JDBC中使用的驅(qū)動(dòng)文件。db4o存在針對(duì)Java,.NET和Mono的版本;它們?cè)诠δ苌隙急舜讼嗟取?事實(shí)上,使用.NET創(chuàng)建的db4o數(shù)據(jù)庫也能由Java程序訪問;反之亦然。)
              db4o是開源的。可執(zhí)行文件,源代碼和文檔可從http://www.db4objects.com/中下載。廣泛的例子程序,和一個(gè)活躍的用戶社區(qū)一樣,也都可以從這個(gè)站點(diǎn)中找到。
              db4o最引人的特性之一就是它在簡易性與強(qiáng)大的功能之間的顯著平衡。一方面,它的API是如此地易于掌握和方便使用,即使是初學(xué)者也能在相同的時(shí)間內(nèi)創(chuàng)建一個(gè)功能完備的數(shù)據(jù)庫對(duì)象。另一方面,這些相同的API也提供了更底層的能夠深入調(diào)用數(shù)據(jù)庫引擎的方法,以允許核心開發(fā)者為了得到適當(dāng)?shù)男阅芏苌钊氲皆撘娴膬?nèi)部中去調(diào)整db4o的工具。
              db4o的特性就是最好的證明--這勝過只是討論--所以我們將通過示例這種方法去證明db4o。然而,我們必須牢記本文通篇只是展示了db4o特性中的一部分罷了。感興趣的朋友會(huì)發(fā)現(xiàn)為了知曉該數(shù)據(jù)庫引擎的全部功能而去查閱db4o的文檔所花的時(shí)間是值得的。

          db4o基礎(chǔ)
              讓我們以初學(xué)者使用db4o時(shí)可能會(huì)做的事情開始:定義了一些類,然后持久化這些類的對(duì)象。我們所假定的類為同樣也是假定的QA項(xiàng)目做一個(gè)跟蹤測試的系統(tǒng)。我們的系統(tǒng)由兩個(gè)類組成,首先是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對(duì)象的容器,(一個(gè)測試用例就是一個(gè)可單獨(dú)執(zhí)行的測試程序--相關(guān)的測試用例組合成一個(gè)測試組)。測試組使用額外的,全局的數(shù)據(jù)成員,每個(gè)數(shù)據(jù)成員的用途也是相對(duì)明顯的:configuration記錄被測試的指定系統(tǒng);overallScore是對(duì)整個(gè)測試組一個(gè)簡要的評(píng)分('P'代表通過,'F'代表失敗,'B'代表被阻塞,等等。);dateExec是一個(gè)毫秒級(jí)的域,標(biāo)識(shí)該測試組被執(zhí)行時(shí)的日期與時(shí)刻。是ArrayList對(duì)象的cases含有單個(gè)的測試用例,由TestCase類定義:
              public class TestCase {
                  private String name;
                  private String comment;
                  private char status;
                  private long duration;
                  private float result;
                  ... <remainder of TestCase definition> ...
              }
              每個(gè)測試用例都有一個(gè)名稱,形式自由的注釋字段,狀態(tài)(通過或失敗),持續(xù)時(shí)間和結(jié)果(例如,為了與測試-字節(jié)/秒的吞吐量-的任意數(shù)據(jù)關(guān)聯(lián))。
              因?yàn)槲覀兯P(guān)注是db4o的使用,所以我們不想在描述這些類的使用細(xì)節(jié)上停留。就讓我們簡單地說,我們已經(jīng)執(zhí)行了一個(gè)特別地測試組中所有的測試用例,將測試的結(jié)果存放在一個(gè)TestSuite對(duì)象中(與TestCase對(duì)象相關(guān)的ArrayList對(duì)象cases中),然后關(guān)閉數(shù)據(jù)庫。是不是太容易了。
              // 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();
              就是它。彈指一揮間,你就已經(jīng)做到了。(當(dāng)然,為了保持簡潔,我們?nèi)サ袅藙?chuàng)建TestSuite對(duì)象和它的TestCase組件的細(xì)節(jié))
              停下來想想上述代碼做了什么事情。特別要考慮你沒有看到的--db4o已經(jīng)做了但還未被告之的事情。
              首先,我們不需要告訴db4o任何關(guān)于TestSuite類的結(jié)構(gòu)的信息;不需要我們的幫助,db4o就能發(fā)現(xiàn)這個(gè)結(jié)構(gòu)。利用Java反射機(jī)制的能力,db4o測定TestSuite類的結(jié)構(gòu),并勾勒出該類的裝配方式以推導(dǎo)出此類對(duì)象的成員與關(guān)鍵數(shù)據(jù)。
              第二,我們不必建議db4o去關(guān)注ArrayList。不僅我們不必將ArrayList的大小告訴db4o,而且我們也不必把它里面的內(nèi)容告訴db4o。正如db4o在它剛接觸testsuite對(duì)象時(shí)就能夠發(fā)現(xiàn)它所需要的一切,db4o也能知道它所需要的關(guān)于(在ArrayList中的)TestCase對(duì)象的所有信息。
              結(jié)果就是,如果我們把testsuite作為一個(gè)任意寵大而復(fù)雜的對(duì)象樹的根,db4o能找到并存儲(chǔ)整個(gè)樹而不需要我們的任何協(xié)助。所以,存儲(chǔ)這個(gè)處于根部的對(duì)象testsuite也就是存儲(chǔ)了整個(gè)ArrayList對(duì)象。
              最后,我們也沒有必須要求db4o以事務(wù)的保護(hù)性方式去調(diào)用set方法。任何會(huì)修改ObjectContainer(表示數(shù)據(jù)庫的db4o對(duì)象)的調(diào)用都會(huì)自動(dòng)地開啟一個(gè)事務(wù),除非已經(jīng)有一個(gè)活躍的事務(wù)了。此外還會(huì)調(diào)用close方法去終止這個(gè)事務(wù),所以上述代碼等價(jià)于:
              db.startTransaction();
              db.set(testsuite);
              db.commitTransaction();
              db.close();
              此處的startTransaction和commitTransaction方法是為了證明我們的觀點(diǎn)而虛構(gòu)的。db4o也確實(shí)有顯示地提交或中止事務(wù)的方法,但為了使原先的代碼足夠的簡潔我們沒有使用這些方法。db4o隱式的事務(wù)使得數(shù)據(jù)庫能夠一直處于一致的狀態(tài);一旦commit方法已經(jīng)執(zhí)行了,數(shù)據(jù)庫的完整性就能夠得到保證,甚至是發(fā)生了災(zāi)難性失敗也能如此。

          查詢I - QBE
              有了已經(jīng)存于數(shù)據(jù)庫中的對(duì)象,下一步我們想要展示的操作將肯定就是查詢和恢復(fù)。db4o提供了三種查詢API:有一種簡單,有一種優(yōu)雅,還有一種則復(fù)雜。每一種API都有其所長,并適用于不同的查詢條件。以db4o的眼光來看,你選擇哪一種API并沒有關(guān)系:它們都是可兼容的。
              我們以簡單的API開始:query by exampel(QBE)。
              使用QBE是如此的容易:為你的查詢目標(biāo)構(gòu)建一個(gè)'模板'對(duì)象,然后把它傳入ObjectContainer的query方法。實(shí)際上,你就是告訴db4o'去拿到所有與這個(gè)對(duì)象看起來一樣的對(duì)象'。(這與JavaSpaces查詢API非常相似;為了清楚地了解如何處理基本數(shù)據(jù)類型,可以看下面的內(nèi)容,db4o的處理方式與JavaSpaces不同。也要注意,JavaSpace Entry對(duì)象期望使用public字段,db4o則沒有這種要求。)
              假設(shè)一個(gè)測試組名為"Network Throughput",我們想取出這個(gè)測試組執(zhí)行的所有測試以便我們能確定失敗了的測試所占的百分比(基于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的模板對(duì)象。注意,只有它的name字段有真實(shí)的值;所有其它的成員變量不是為null就是為0。Null或0字段不參與QBE查詢;因此,調(diào)用db.get方法就會(huì)返回在該數(shù)據(jù)庫中name字段匹配"Network Throughput"的所有TestSuite對(duì)象。匹配的TestSuite對(duì)象將返回在一個(gè)ObjectSet結(jié)果對(duì)象中。上述代碼遍歷該結(jié)果,取出對(duì)象,然后計(jì)算結(jié)果并展示出來。
              QBE明顯的優(yōu)點(diǎn)就是它的簡易性。不需要掌握其它單獨(dú)的查詢語言。另外,QBE也是類型安全的:你不需要?jiǎng)?chuàng)建一個(gè)類似于SQL的查詢語句
              SELECT TestSuite.overallScore FROM TestSuite WHERE TestSuite.name = 200.0
              另一方面,由于該查詢是由Java代碼創(chuàng)建的,編譯器不會(huì)允許你把一個(gè)浮點(diǎn)值賦給一個(gè)String字段;反之亦然。
              QBE明顯的缺點(diǎn)就是它只能執(zhí)行"等于"查詢。另外,QBE使用null值去確定不參與查詢的String或?qū)ο笠贸蓡T變量,使用0值去指定不參與查詢的數(shù)字字段。所以,例如,我不能發(fā)明一個(gè)QBE查詢?nèi)カ@得所有result字段的值為0的TestCase對(duì)象。
              更為精細(xì)的查詢要求一個(gè)能力更強(qiáng)的查詢機(jī)制,而db4o恰恰就有這樣一種機(jī)制。

          查詢方式II - 原生查詢(Native Query)
              db4o的原生查詢系統(tǒng)應(yīng)該是能想像得到的最具彈性的查詢機(jī)制。不像使用數(shù)據(jù)庫查詢語言去構(gòu)建查詢,你是使用"無格式的普通Java語句"去構(gòu)造原生查詢。原生查詢用兩種手段去實(shí)現(xiàn)這件不可思意的工作:一個(gè)是Predicate類;另一個(gè)是QueryComparator接口。這個(gè)類包含一個(gè)可重載的(overrideable)回調(diào)方法,該方法將指定如何從數(shù)據(jù)庫中選擇對(duì)象(如果你愿意,你將會(huì)看到查詢語句的主體....)。這個(gè)接口只聲明了一個(gè)方法,該方法用于指明如何對(duì)查詢結(jié)果進(jìn)行排序。
              作為一個(gè)例子,我們假設(shè)想找到在給定的一周內(nèi)執(zhí)行過了的總得分為"failed",但與之關(guān)聯(lián)的測試用例中有超過一半的被評(píng)為"passed"的測試組。這不是一個(gè)簡單的"等于"查詢,所以它不能使用QBE構(gòu)建。
              然而,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;

                  // 構(gòu)造器要在本地獲得ObjectContainer對(duì)象和日期范圍
                  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是否在指定的日期范圍內(nèi)
                      if(testsuite.getDateExec()<startdate ||
                        testsuite.getDateExec()>enddate) return false;

                      // 如果該測試組對(duì)象中沒有測試用例對(duì)象,則拒絕將該測試組對(duì)象放入查詢結(jié)果中
                      if(testsuite.getNumberOfCases()==0)
                          return false;

                      // 檢查該測試組對(duì)象中的測試用例的通過率是否超過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;
                  }
              }
              注意在這個(gè)類的使用中使用了Java泛型語義,這就是告訴db4o只去取TestSuite對(duì)象。當(dāng)查詢執(zhí)行時(shí),TestSuite對(duì)象就會(huì)傳入match方法(我們之前提到過的回調(diào)方法),如果傳入的TestSuite對(duì)象符合查詢規(guī)范該方法就返回true,否則就返回false。
              match方法中的代碼首先確定侯選對(duì)象是否是在一周的日期范圍內(nèi)。如果是,則循環(huán)該對(duì)象中的成員變量測試用例的對(duì)象,計(jì)算出所有通過了的測試用例的總數(shù)。如果,得到的通過率小于50%,該測試組就被拒絕;否則,就讓它通過。
              我們可以使用如下的代碼準(zhǔn)確地展示該查詢程序:
              . . .
              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();
              . . .
              可以把原生查詢想像成這樣:目標(biāo)類的對(duì)象一個(gè)接一個(gè)從數(shù)據(jù)庫中取出,然后把它們傳入match方法中。只有那些被match方法返回true的對(duì)象才會(huì)置于查詢結(jié)果ObjectSet對(duì)象中。基本上可以說,如果你會(huì)知道如何寫Java代碼,那么你就知道如何寫原生查詢。
              那么排序呢?如果想按日期的升序排列查詢結(jié)果,我們就要實(shí)現(xiàn)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方法的作用十分明顯。那些在查詢中得以勝出的對(duì)象會(huì)成對(duì)的傳入compare方法,如果第一個(gè)對(duì)象會(huì)排在第二個(gè)對(duì)象之前,相同或之后的位置,該方法就會(huì)分別返回一個(gè)小于,等于或大于0的值。為了準(zhǔn)確地說明對(duì)查詢結(jié)果的排序,我們實(shí)例化NativeQuerySort,并把對(duì)query方法的調(diào)用修改成如下:
              . . .
              // Instantiate the sort class
              nqsClass = new NativeQuerySort();
              . . .
              ObjectSet results = db.query(nqqClass, nqsClass);
              . . .
          其它的代碼仍然與原先的保持一致。
              好懷疑的讀者可能會(huì)抱怨道,原生查詢只是一種編程上的小伎倆--相比較于直接去拿到所有的TestSuite對(duì)象然后再排除其中不符合條件的對(duì)象這樣的程序,原生查詢并不快。
              是的,但并不十分準(zhǔn)確。原生能夠被優(yōu)化。你所需要做的只是把兩個(gè)jar文件--db4o-xxx-nqopt.jar(xxx表示db4o的版本)和bloat.jar--置于CLASSPATH環(huán)境變量中。在查詢執(zhí)行的時(shí)候,這些類庫中的代碼會(huì)對(duì)(在match方法中)例如基本數(shù)據(jù)類型比較,算術(shù)和布爾表達(dá)式,簡單的對(duì)象成員訪問,以及更多方面的結(jié)構(gòu)進(jìn)行優(yōu)化。這個(gè)被支持的優(yōu)化的列表在不停的增長,因?yàn)閐b4o引擎還在擴(kuò)展優(yōu)化的范圍。

          查詢方式III - S.O.D.A.
              db4o獨(dú)一無二的能力之一就是它的API被分層了。開發(fā)者能夠選擇通過高層次--賦予數(shù)據(jù)庫引擎相當(dāng)大的自由度,讓它決定如何去實(shí)現(xiàn)它的操作--或者開發(fā)者也可以使用一種更直接地方式去訪問db4o。后一種選擇為程序員平添了更多的負(fù)擔(dān),程序員必須更加小心地引導(dǎo)數(shù)據(jù)庫引擎的內(nèi)部工作。但回報(bào)就是得到一個(gè)更快,能力更強(qiáng)的數(shù)據(jù)庫應(yīng)用。
              db4o的S.O.D.A.(Simple Object Data Access)查詢機(jī)制就是該層次API的一個(gè)完美的例子。S.O.D.A.就是db4o的內(nèi)部查詢系統(tǒng)--QBE和原生查詢都被翻譯成了S.O.D.A.。然而,應(yīng)用程序也能直接地調(diào)用S.O.D.A.。
              假設(shè)我們想找到所有名為"Network Throughput",且至少擁有一個(gè)其result字段--我們使用這個(gè)參數(shù)作為字節(jié)/秒的量度--不小于指定值(比方說,100)的測試用例的測試組。為該請(qǐng)求而做的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所示圖表的幫助下,這些有點(diǎn)兒神秘的代碼就變得不那么神秘了。該程序所構(gòu)建的歸總起來就是一個(gè)用于指導(dǎo)底層數(shù)據(jù)庫引擎的查詢圖(Query Graph)。descend方法創(chuàng)建了該圖的一個(gè)分支,該分支向下步入對(duì)象的結(jié)構(gòu)中。每個(gè)descend方法就在這個(gè)樹中構(gòu)建一個(gè)結(jié)點(diǎn),可以在這些結(jié)點(diǎn)上再附上一個(gè)約束(使用constrain方法)。用SQL的話來說,約束指定了查詢的"WHERE"子句部分。多個(gè)約束可以在與(and)或或(or)方法的協(xié)助下結(jié)合起來。在上面的查詢中我們已經(jīng)使用了and方法去關(guān)聯(lián)這些約束。
                                              
          與其它的查詢方式一樣,查詢結(jié)果返回到ObjectSet對(duì)象中,通過遍歷該對(duì)象就可取出那些拿到的對(duì)象。
              注意,由于S.O.D.A.是一種低層次的訪問方法,沒有智能的指示,它就沒有默認(rèn)的行為。訪問cases對(duì)象的成員變量result字段的代碼很簡單
              query.descend("cases").descend("result"). ...
          我們并沒有告訴S.O.D.A."cases"是一個(gè)集合對(duì)象。所以當(dāng)查詢執(zhí)行時(shí),它會(huì)不被察覺地檢測ArrayList對(duì)象cases中所有元素(TestCase對(duì)象)的result字段,然后會(huì)正確地返回那些擁有符合搜索規(guī)范的測試用例的測試組。

          db4o性能調(diào)優(yōu)
              我們已經(jīng)展示了db4o的基本操作(但無關(guān)緊要的刪除操作除外,下面將會(huì)提到它)。但,正如我們?cè)诒疚耐ㄆ岬降模琩b4o發(fā)布(expose)了一個(gè)API層次結(jié)構(gòu),以允許開發(fā)者能夠選擇以何種層次的API去控制建立在該數(shù)據(jù)庫引擎之上的應(yīng)用程序。從另一方面看,如果你所想做的只是向數(shù)據(jù)庫存入,及從數(shù)據(jù)庫取出對(duì)象,那么你就已經(jīng)看到了你所需要的一切。然而,如果你的應(yīng)用的需求超出了添加,更新,查詢和刪除,可能還有一個(gè)db4o的特性可解決你的問題。
              db4o的ObjectContainer實(shí)際上發(fā)布(expose)了兩個(gè)API。第一個(gè)API非常的簡單,由十個(gè)方法組成。這些方法處理數(shù)據(jù)庫的打開與關(guān)閉;添加,更新,查詢和刪除對(duì)象;及提交或中止事務(wù)。短言之,該API為你提供了在操縱數(shù)據(jù)庫時(shí)所需要所有功能。然而,該API中的一個(gè)方法--ext()--是進(jìn)入"被擴(kuò)展的"ObjectContainer的一個(gè)入口。該被擴(kuò)展的ObjectContainer為深入控制db4o的內(nèi)部發(fā)布(expose)了更多方法。例如,你可以獲得并改變數(shù)據(jù)庫的配置上下文,使用它你能夠修改該引擎的行為。
              例如,假設(shè)你已經(jīng)從數(shù)據(jù)庫中拿到了一個(gè)TestSuite對(duì)象,發(fā)現(xiàn)該對(duì)象中的數(shù)據(jù)是錯(cuò)誤的,并決定該對(duì)象應(yīng)該被刪除。此外,你還決定你必須刪除的不僅是該TestSuite對(duì)象,而且還有所有與之關(guān)聯(lián)的TestCase對(duì)象(在ArrayList對(duì)象cases中)。
              你是可以冗長而乏味地遍歷這個(gè)ArrayList對(duì)象,一個(gè)接一個(gè)地刪除每一個(gè)TestCase對(duì)象,然后再刪除TestSuite對(duì)象本身。可能一種更好的解決方案是為這個(gè)TestSuite對(duì)象啟用db4o的"級(jí)聯(lián)刪除"特性。
              . . .
              // 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);
              . . .
          在上述代碼中,我們實(shí)例化了一個(gè)ObjectClass對(duì)象,該對(duì)象使我們能夠訪問到TestSuite對(duì)象的db4o內(nèi)部表現(xiàn)形式。我們打開cascadeOnDelete標(biāo)記,以便當(dāng)執(zhí)行db4o.delete(ts1)時(shí),不僅ts1對(duì)象會(huì)被刪除,而且所有由ts1引用的TestCase對(duì)象也會(huì)被刪除。(由于顯而易見的原因,默認(rèn)情況下,級(jí)聯(lián)刪除是被關(guān)閉的)
              作為另一個(gè)例子,假設(shè)你想為數(shù)據(jù)庫預(yù)分配存儲(chǔ)空間,以至于要最小化磁盤驅(qū)動(dòng)器的頭移動(dòng)(head movement)。(最好是在硬盤碎片整理之后,并新創(chuàng)建一個(gè)數(shù)據(jù)庫時(shí)這樣做。)并且假設(shè)以O(shè)bjectContainer對(duì)象db作為打開了的數(shù)據(jù)庫:
              // Fetch the database's configuration context
              // 獲得數(shù)據(jù)庫的配置上下文
              Configuration config = db.ext().configure();
              // Pre-allocate 200,000 bytes
              // 預(yù)分配200000000字節(jié)
              config.reserveStorageSpace(200000000L);
          把數(shù)據(jù)庫文件預(yù)擴(kuò)展到200000000字節(jié)(略小于200兆字節(jié))。假設(shè)該磁盤已經(jīng)做碎片整理了,那么被分配的塊就是連續(xù)的,這可顯著提升數(shù)據(jù)庫的訪問。

          db4o高級(jí)應(yīng)用
              完全可以說,db4o在它不大的空間(約500K)內(nèi)已裝入了足夠多的特性,相比較于db4o在運(yùn)行過程中所做的眾多事情,我們不能花費(fèi)更多的筆墨去解釋它們了。但是有兩個(gè)特性十分突出,以至于肯定要提到它們。
              db4o的對(duì)象復(fù)制實(shí)現(xiàn)了被總稱為面向?qū)ο蟀娴臄?shù)據(jù)庫同步。使用復(fù)制所提供的功能,你能為一個(gè)數(shù)據(jù)庫中的一個(gè)對(duì)象做一個(gè)副本,并將該副本放入另一個(gè)數(shù)據(jù)庫中。使用這樣的方法,副本對(duì)象就無形中和原始對(duì)象關(guān)聯(lián)在了一起。對(duì)任一對(duì)象--原始對(duì)象或副本對(duì)象--的改變都會(huì)被跟蹤到,以便在之后的某個(gè)時(shí)候,數(shù)據(jù)庫能夠被重組,并且這兩個(gè)數(shù)據(jù)庫中對(duì)象的不同之處可被分解(例如,可同步這兩個(gè)數(shù)據(jù)庫)。
              它工作起來就像這樣:為使一個(gè)數(shù)據(jù)庫可被復(fù)制,與事務(wù)計(jì)數(shù)器一樣,該數(shù)據(jù)庫中被創(chuàng)建的任何一個(gè)對(duì)象都用一個(gè)唯一全局標(biāo)識(shí)符(UUID)進(jìn)行了標(biāo)記。當(dāng)你從原始數(shù)據(jù)庫中"復(fù)制"一個(gè)對(duì)象到另一個(gè)數(shù)據(jù)庫中,副本對(duì)象會(huì)帶著與它的原始對(duì)象相同的UUID和事務(wù)計(jì)數(shù)器。副本數(shù)據(jù)庫現(xiàn)在就可以從它的原始數(shù)據(jù)庫那兒弄走了。修改副本對(duì)象的內(nèi)容將會(huì)導(dǎo)致對(duì)象的事務(wù)計(jì)數(shù)器被修改。所以,當(dāng)這兩個(gè)數(shù)據(jù)庫重新連接起來,db4o內(nèi)建的同步處理機(jī)制就能一個(gè)對(duì)象一個(gè)對(duì)象地進(jìn)行正確的匹配(使用UUID),并確定原始或副本對(duì)象是否已經(jīng)改變了。db4o甚至能追蹤到每個(gè)對(duì)象發(fā)生最后一次修改時(shí)的時(shí)間,以便用戶寫的沖突解決代碼能確定哪個(gè)對(duì)象是最近更新的。
              從操作行為來看,db4o的同步處理機(jī)制與原生查詢十分相似。回想一下,當(dāng)實(shí)現(xiàn)了一個(gè)原生查詢類時(shí),我們要定義一個(gè)match方法,該方法確定哪些對(duì)象符合(或不符合)查詢規(guī)范。使用同步復(fù)制,我們要定義一個(gè)ReplicationProcess對(duì)象,我們會(huì)把沖突處理對(duì)象傳入該方法中。這些Java代碼可能像這樣:
              . . .
              ReplicationProcess replication = db1.ext().
                  replicationBegin(db2, new ReplicationConflictHandler()
                  {  
                      public Object resolveConflict(
                          ReplicationProcess rprocess, Object a, Object b)
                          {
                              . . .  ...
                              return winning_object;
                          }
                      )
                  };
          在上述代碼中,Object a是來自于數(shù)據(jù)庫db1的對(duì)象,Object b則來自于數(shù)據(jù)庫db2。默認(rèn)情況下,同步是雙向的。同步處理保證勝出的對(duì)象(由resolveConflict方法返回的對(duì)象)能存入這兩個(gè)數(shù)據(jù)庫中。所以當(dāng)復(fù)制完成時(shí),這兩個(gè)數(shù)據(jù)庫中被復(fù)制的對(duì)象就同步了。
              最后,db4o最強(qiáng)大的特性之一就是它能毫不費(fèi)力地容忍類結(jié)構(gòu)的演變。假設(shè),在向數(shù)據(jù)庫內(nèi)加入數(shù)百個(gè)TestSuite對(duì)象之后,我們決定這個(gè)類必須要被修改。就是說,我們已經(jīng)被告之,該系統(tǒng)必須能追蹤到每個(gè)TestSuite的執(zhí)行QA工程師,所以必須加入如下字段
              private int engineerID;
          到TestSuite類的定義中。
              現(xiàn)在我們就遇到了兩個(gè)相關(guān)聯(lián)的問題。第一個(gè)問題不是很糟糕:已存在于數(shù)據(jù)庫中的用于表示測試的TestSuite對(duì)象,并沒有為它們記錄QA工程師的ID,所以我們將不得不將一個(gè)虛擬的值賦給這些對(duì)象的engineerID字段;該值會(huì)指出"未記錄QA工程師的ID"。第二個(gè)問題就更難應(yīng)付了:我們必須不知原因地把已有的TestSuite對(duì)象移植到"新的"類結(jié)構(gòu)中。我們必須為數(shù)據(jù)庫中所有已存在的TestSuite對(duì)象加上一個(gè)engineerID字段。除了把舊數(shù)據(jù)庫中的對(duì)象復(fù)制到一個(gè)中間性的文件,然后重新創(chuàng)建一個(gè)數(shù)據(jù)庫這種方法之外,我們還能怎么做呢?
              幸運(yùn)地是,使用db4o,我們確實(shí)什么都不用做。為了能操縱新的engineerID字段,以完成業(yè)務(wù)邏輯上所要求的變化,如果就只是向(我們的應(yīng)用程序中的)TestSuite類添加一個(gè)engineerID字段,我們完全不必觸動(dòng)db4o API的任何調(diào)用。當(dāng)db4o使用"新的"TestSuite類結(jié)構(gòu)去讀取"舊的"TestSuite對(duì)象,db4o將認(rèn)為這些對(duì)象中的engineerID字段消失了,并且會(huì)優(yōu)雅地把該字段的值設(shè)為0。如果我們把0當(dāng)作"未記錄QA工程師的ID",那么我們所做的移植就完成了。寫入到數(shù)據(jù)庫中的新TestSuite對(duì)象將會(huì)包括新字段。(事實(shí)上,對(duì)舊的TestSuite對(duì)象本身進(jìn)行重寫,會(huì)使db4o不被察覺地為這些對(duì)象加上新字段。)所以,通過發(fā)布一個(gè)包含新的TestSuite定義的更新應(yīng)用,我們完全可以不被察覺地從舊的TestSuite對(duì)象移植到新的...正如前述應(yīng)用所做的那樣。

          全方位數(shù)據(jù)庫
              通過適當(dāng)?shù)貞?yīng)用,db4o就能成為數(shù)據(jù)庫中的"瑞士軍刀"。它占用足夠小的內(nèi)存空間,使它能夠被包含在一個(gè)不需要消耗大量資源的項(xiàng)目中。同樣地,一個(gè)數(shù)據(jù)庫只在磁盤上占用一個(gè)文件的事實(shí)可能會(huì)使人們?cè)诘谝谎劭吹剿鼤r(shí),不能認(rèn)識(shí)到它豐富的功能。將數(shù)據(jù)庫從一個(gè)地方移到另一個(gè)地方就是一個(gè)簡單的文件拷貝;你不必?fù)?dān)心分離的索引文件,數(shù)據(jù)文件,數(shù)據(jù)庫結(jié)構(gòu)文件等等這些文件的位置。對(duì)于快速部署和零管理的數(shù)據(jù)庫應(yīng)用,db4o很能勝任。
              另外,根據(jù)我們已多次描述過的,db4o在簡單性和優(yōu)雅之間達(dá)到了適度的平衡。db4o的QBE既如此的簡單,又如此的功能強(qiáng)大,對(duì)于一組令人驚奇的應(yīng)用,它經(jīng)常是我們唯一需要的查詢API。如果你主要是通過指針導(dǎo)航而不是查詢?nèi)ピL問數(shù)據(jù)庫,QBE就特別有吸引力。在這種情況下,QBE經(jīng)常能高效地拿到一個(gè)對(duì)象網(wǎng)絡(luò)(Object Network)的根對(duì)象。然后,你就能使用db4o的激活(activation)功能從根部向下進(jìn)行對(duì)象引用導(dǎo)航,如果這些對(duì)象都完全在內(nèi)存中的話,你就更可以這么做了。
              而在使用QBE并不高效的時(shí)候,原生查詢和S.O.D.A.就能派上用場了,并且它們伴隨著一堆特性和低層次的API。我們還沒有展示db4o的加密功能,插入式文件I/O(例如,它允許你添加"寫后讀(read-after-write)"驗(yàn)證),信號(hào)量,客戶端/服務(wù)器端模式,以及其它難以計(jì)數(shù)的功能。我們結(jié)論性的建議很簡單:當(dāng)你的下一個(gè)Java應(yīng)用需要一個(gè)數(shù)據(jù)庫,在最終開始編碼之前,你可以去訪問一下http://www.db4objects.com/。這是很值得的。

          posted on 2007-12-05 12:31 John Jiang 閱讀(4687) 評(píng)論(8)  編輯  收藏 所屬分類: DatabaseJavadb4o翻譯

          評(píng)論

          # re: 深入db4o(譯) 2007-12-05 12:43 Sha Jiang
          非常歡迎大家能指正本譯文中的錯(cuò)誤,謝謝!  回復(fù)  更多評(píng)論
            

          # re: 深入db4o(譯) 2007-12-05 12:46 sitinspring
          做個(gè)記號(hào),有空細(xì)看.  回復(fù)  更多評(píng)論
            

          # re: 深入db4o(譯) 2007-12-06 08:32 sitinspring
          怎么不見DB4O的predicate式查詢?

          另外DB4O沒有工具去查詢已有數(shù)據(jù)庫的對(duì)象是個(gè)遺憾.  回復(fù)  更多評(píng)論
            

          # re: 深入db4o(譯) 2007-12-06 09:05 Sha Jiang
          > 怎么不見DB4O的predicate式查詢?
          不明白何為"predicate式查詢" :-)
          不過,db4o的原生查詢API中有一個(gè)類名為"Predicate",不知是否為你所期望的。
          詳見"查詢方式II - 原生查詢(Native Query)"一節(jié)。

          > 另外DB4O沒有工具去查詢已有數(shù)據(jù)庫的對(duì)象是個(gè)遺憾.
          ObjectManager,可以到如下鏈接中下載,
          http://developer.db4o.com/files/folders/objectmanager_64/default.aspx  回復(fù)  更多評(píng)論
            

          # re: 深入db4o(譯) 2007-12-06 16:46 sitinspring
          @Sha Jiang

          好,再去看看.  回復(fù)  更多評(píng)論
            

          # re: 深入db4o(譯) 2007-12-07 07:57 結(jié)下梁子
          我想問一下db4o目前有哪些成功項(xiàng)目?  回復(fù)  更多評(píng)論
            

          # re: 深入db4o(譯) 2007-12-07 08:53 Sha Jiang
          > 我想問一下db4o目前有哪些成功項(xiàng)目?
          下面的鏈接中有db4o的部分成功案例,
          http://www.db4o.com/about/customers/success/  回復(fù)  更多評(píng)論
            

          # re: 深入db4o(譯) 2011-07-14 09:03 tom.cat
          @sitinspring
          怎么沒人,有ECLIPSE插件  回復(fù)  更多評(píng)論
            

          主站蜘蛛池模板: 溧水县| 晴隆县| 乐东| 沂水县| 朝阳市| 宜川县| 灵宝市| 日土县| 府谷县| 邯郸县| 灵石县| 乐清市| 台安县| 积石山| 和静县| 德江县| 南充市| 从化市| 建德市| 成安县| 电白县| 苏尼特右旗| 京山县| 博野县| 错那县| 平和县| 册亨县| 乌拉特中旗| 孟村| 曲阜市| 科技| 宁德市| 靖边县| 宜兰市| 塔河县| 开封县| 西峡县| 保康县| 炎陵县| 漳州市| 萍乡市|