程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 使用DB4O對象數據庫進行簡單持久化

使用DB4O對象數據庫進行簡單持久化

編輯:關於JAVA

許多的Java程序都需要處理持久性數據,在大多數的情況下,需要和關系數據庫(數據庫培訓 數據庫認證 )打交道,可能是遺留數據庫或是一個工業標准的數據庫管理系統(DBMS)。JDBC 的API和驅動為大多數的數據庫系統提供了一個使用SQL語言進行查詢的標准方式,然而,在對象模型應用程序和關系模型的數據庫的領域之間,“錯誤匹配問題”使得接口程序十分復雜。對象模型是基於軟件工程的原理和業務領域的對象模型,而關系模型是基於數學原理和數據庫有效存取規則。兩種模型沒有誰比誰更好,問題是兩者是不同的,並不總能在程序中有效協調工作。

對於這個問題已經有一些解決方案,例如Hibernate和Java Data Objects,他們為開發者提供了透明的持久性――應用程序只需使用面向對象的API處理持久性的對象,而不需要在Java代碼中嵌入SQL語句。對於EJB的容器來說,容器管理的持久性(CMP)做了類似的工作,但是對Java平台來說這不是一個一般的持久性工具。在任何這些解決方案中,對象通過底層框架被映射關系數據庫中的表,這些底層的框架生成SQL所需要存取的對象的屬性。也就是說,對象模型越復雜,這種映射就越困難。我們所用的描述符,通常使用XML文件,來定義這種映射關系。繼承和多對多的關系,尤其是一些關系模型無法直接表示的關系增加了映射復雜度。繼承的結構可以有不同的方式被映射到一組表中,選擇何種方式就需要在儲存的效率和查詢的復雜度上權衡,就如一個單獨的表要實現一個多對多的關系一樣。

在一個數據庫中存儲一個對象,數據庫本身如果就是一個對象模型,這樣,就提供了另外一個解決方案。在二十世紀九十年代,各種各樣面向對象的數據庫管理系統被開發出來,但是這些數據庫配置十分復雜,而且需要使用對象定義語言。對象是作為對象格式存儲的,但是對應用語言來說它們不是本地化的。目前這些數據庫產品並沒對市場有大的影響,只是和一些將關系和對象混雜的數據庫一樣,在關系數據庫面向對象的API上下功夫。

嵌入式數據庫

在一些應用中,數據庫管理系統的維護費用是必不可少的,在一些小功耗,可嵌入的數據庫引擎條件下,可以更好的提供數據庫的存儲需求,例如,SQLite,提供了一個自我包含的數據庫引擎。但是,因為這個Java的接口是通過一個JDBC驅動,這種以SQL為基礎的解決方案也存在“錯誤匹配問題”。

在許多案例中,持久性問題可以變得相當的簡單,如果使用嵌入式的對象數據庫引擎話。這是一個很好的機會,我們來了解一下Carl Rosenberg創建的db4o。db4o曾經是一個商業的對象數據庫,現在它是開源的,而且,最近獲得了GPL(GNU通用公共許可證)的許可證。

Db4o的功能特點:

● 沒有錯誤匹配問題――對象以其本身方式來存儲

● 自動管理數據模式

● 存儲時沒有改變類特征,以使得易於存儲

● 與Java(.Net)無縫綁定

● 自動數據綁定

● 一個250Kb的庫文件的簡易安裝(Java jar or .Net DLL)

● 一個數據庫文件

● 自動模式版本

● 查詢實例

● S.O.D.A. (簡單對象數據庫訪問), 一個開源查詢的API

Db4o的優點

Db4o已經被一些嵌入式系統的應用程序所選用,這些程序特點都是要求零管理、高可靠性和低功耗的。例如,在德國,BMW Car IT在汽車嵌入式系統電子原型中使用了它。同樣是德國的Die Mobilanten,他們在中型公共事業的PDA解決方案中使用了db4o。在美國,MassIE Systems為嬰兒眼睛診斷的視網膜圖像處理系統依靠db4o加強其客戶交流圖像數據庫。

在db4o中存儲對象的方式相當的簡單,同時,對於教學目的來說也相當具有吸引力。艾塞克斯(英)大學和德州農工大學都把db4o作為教學和研究對象。在我自己的學院,對於那些需要如何要將面向對象的概念應用到他們的項目中去的學生,和關系數據庫打交道對於他們設計自己領域內的模型來說是一個負面的影響。使用db4o可以使得他們只需和持久層的數據打交道而不需要分心處理一個相沖突的不理解的數據關系模型,也不需要花費更多的時間學習一個工具,如Hibernate或一個復雜的OODBMS。同時,學習面向對象查詢API的概念可能在將來被證明更有效。

同樣的API,不同的存儲

有時候,你正好不得不使用一個關系數據庫。從一個Java開發者的觀點來看,透明的持久性是理想境界。如果持久性可以通過面向對象的API實現,那麼開發者就不必要為不同的數據存儲來學習不同的技術。盡管db4o不是JDO兼容的(作為一個結果很容易使用),它的創造者的伙伴們有許多其他的開源項目,包括MySQL和Hibernate,它們使用單一的、一致的對象持久性的API和對象數據庫交互,包括db4o本身,一些關系數據庫或者其他存儲模式,例如Prevayler。如果JDO對於你來說比較重要,你可以考慮ObjectDB,它是一個JDO兼容的純對象數據庫.

一個例子

這個例子演示了創建一個數據庫和存儲對象是如何簡單。它同時演示了兩種查詢的方法:實例查詢和更具有彈性的S.O.D.A查詢API。整個的源代碼在文章主題資源的部分可以下載[實際上對資源這部分不是合適的下載地方]。為了可以運行,你必須在你的路徑裡添加db4o jar文件並且執行Db4oTest.Java。

在實例中有兩個類,分別代表棒球隊(Team)和成員(Player)。為了使得例子更加有趣,我們還有一個投手的類(Pitcher),Pitcher是Player的一個子類。並且添加了一個額外的數據域在Pitcher中。Team類有一個屬性,就是該team的成員(player)列表,它當然包括Pitcher對象。Team、Player、Pitcher對象是一些簡單傳統的Java對象,這裡並沒有持久層的代碼。也不需要有唯一的關鍵字屬性,當一個對象數據庫在自動存儲對象時是需要唯一對象標識符(OIDS)。

Player class

public class Player {   protected String name;

protected int squadNumber;

protected float battingAverage;

protected Team team; public Player(String name, int squadNumber,   float battingAverage){

this.name = name;

this.squadNumber = squadNumber;

this.battingAverage = battingAverage;

}   public void setName(String n){this.name = n;} public String getName(){return this.name;}

public void setSquadNumber(int s){this.squadNumber = s;}

public int getSquadNumber(){return this.squadNumber;}

public void setBattingAverage(final float b) {

this.battingAverage = b; }

public float getBattingAverage(){

return this.battingAverage;} public void setTeam(Team t) {this.team = t;}  public Team getTeam() {return this.team;}

public String toString() {

return name + ":" + battingAverage;

}

}

Pitcher class

public class Pitcher extends Player{

private int wins;

public Pitcher(String name, int squadNumber,

float battingAverage, int wins) {

super(name,squadNumber,battingAverage);

this.wins = wins;

}

public void setWins(final int w){this.wins = w;}

public int getWins() {return this.wins;}

public String toString() {

return name + ":" + battingAverage + ", " + wins;

}

}

Team class

import Java.util.List;

import Java.util.ArrayList;

public class Team {

private String name;

private String city;

private int won;

private int lost;

private List players;

public Team(String name, String city,

int won, int lost){

this.name = name;

this.city = city;

this.won = won;

this.lost = lost;

this.players = new ArrayList();

}

public void addPlayer(Player p) {

players.add(p);

}

public void setName(String n){this.name = n;}

public String getName(){return this.name;}

public void setStadium(String c){this.city = c;}

public String getCity(){return this.city;}

public void setPlayers(List p){players = p;}

public List getPlayers(){return players;}

public void setWon(int w) {this.won = w;}

public int getWon(){return this.won;}

public void setLost(int l) {this.lost = l;}

public int getLost() {return this.lost;}

public String toString() {

return name;

}

}

首先,我們來建立一些測試數據。

// Create Players Player p1 = new Player("Barry Bonds", 25, 0.362f);  Player p2 = new Player("Marquis Grissom", 9, 0.279f);

Player p3 = new Player("Ray Durham", 5, 0.282f);

Player p4 = new Player("Adrian Beltre", 29, 0.334f);

Player p5 = new Player("Cesar Izturis", 3, 0.288f);

Player p6 = new Player("Shawn Green", 15, 0.266f);

// Create Pitchers Player p7 = new Pitcher("Kirk Rueter",46, 0.131f, 9);

Player p8 = new Pitcher("Kazuhisa Ishii",17, 0.127f, 13);

// Create Teams

Team t1 = new Team("Giants", "San Francisco", 91, 71);

Team t2 = new Team("Dodgers", "Los Angeles", 93, 69);

// Add Players to Teams

t1.addPlayer(p1); p1.setTeam(t1);

t1.addPlayer(p2); p2.setTeam(t1);

t1.addPlayer(p3); p3.setTeam(t1);

t2.addPlayer(p4); p4.setTeam(t2);

t2.addPlayer(p5); p5.setTeam(t2);

t2.addPlayer(p6); p6.setTeam(t2);

// Add Pitchers to Teams

t1.addPlayer(p7); p7.setTeam(t1);

t2.addPlayer(p8); p8.setTeam(t2);

存儲數據

一個Team的對象可以用一行代碼執行存儲操作:

db.set(t1);

這兒,db是一個指向ObjectContainer對象的一個引用,它在打開一個新數據文件時被創建,如下:

ObjectContainer db = Db4o.openFile(filename);

一個db4o數據庫是一個以.yap為擴展名的文件,其中set方法用來存儲一個對象。

注意這裡是存儲了一個Team的對象,並且它包含了Player的對象。我們能夠通過取回Player對象來測試是否存儲了這些Player的對象。最簡單的方法就是用QBE來查詢。

實例查詢的簡單查詢

下面的代碼列出了所有與實例對象匹配的Player對象,這裡它們是唯一的。通過調用ObjectContainer的get方法,結果以ObjectSet的方式返還。

Player examplePlayer = new Player("Barry Bonds",0,0f);

ObjectSet result=db.get(examplePlayer); System.out.println(result.size());

while(result.hasNext()) {

System.out.println(result.next());

}

同時,我們也可以得到所有我們預先虛構的,創建並存儲的Player對象(所有數據域為0或者是空),如下:

Player examplePlayer = new Player(null,0,0f);

ObjectSet result=db.get(examplePlayer);   System.out.println(result.size());

while(result.hasNext()) {

System.out.println(result.next());

}

結果如下:

8

Kazuhisa Ishii:0.127, 13

Shawn Green:0.266

Cesar Izturis:0.288

Adrian Beltre:0.334

Kirk Rueter:0.131, 9

Ray Durham:0.282

Marquis Grissom:0.279

Barry Bonds:0.362

注意:我們可以取回所有的Player類的對象,包括其子類(這裡是Pitcher類)的對象,而不需要任何額外的代價。上面結果中所示的Pitcher對象它們額外的信息(wins)也同樣被取回來了。而在關系數據庫中我們必須知道如何在表中映射繼承樹,可能不得不要加一些額外的表來取回對象所有的屬性。

更新和刪除

更新一個對象可以混合使用上面的方法技術。下面的代碼假定了只有一個結果匹配,並且這個匹配的對象可以上溯到Player,這樣可以保證它的屬性能得到修改。

Player examplePlayer = new Player("Shawn Green",0,0f);

ObjectSet result = db.get(examplePlayer);

Player p = (Player) result.next();

p.setBattingAverage(0.299f);

db.set(p);

數據庫的對象也可以以同樣的方式被刪除。

Player examplePlayer = new Player("Ray Durham",0,0f);

ObjectSet result = db.get(examplePlayer);

Player p = (Player) result.next();

db.delete(p);

功能更強的查詢

在早期版本的db4o中一個主要的缺點就是實例查詢提供相當有限的查詢能力。例如,你不能像這樣查詢“所有平均擊球率大於30%的隊員”。現在,db4o包含了S.O.D.A的API可以使得查詢的能力接近SQL。一個Query類的實例代表了含有約束條件的標准查詢圖中的一個節點,這個節點可以代表一個類,或者多個類,或者一個類屬性。

下面的代碼演示了如何執行這樣的操作。我們定義了一個查詢圖的節點並且指定/限制它為Player類。這意味著查詢的返回結果為Player對象。然後,我們向下具體化這張圖,去查找一個節點,代表屬性“平均擊球率”,限制其大於0.3。最後,執行查詢操作,返回數據庫中所有滿足條件的對象。

Query q = db.query();

q.constrain(Player.class);

q.descend("battingAverage").constrain(new Float(0.3f)).greater();

ObjectSet result = q.execute();

初一看,這個查詢操作很類似SQL的查詢,就如這樣:

SELECT * FROM players WHERE battingAverage > 0.3

然而,Player類的設計允許在Team對象和Player之間有相互引用關系,正如測試中的數據。一個Team的對象有一個應用指向一個序列的Player對象,同時每一個Player對象都有一個Team的引用。這就意味這查詢的結果應該包含Player和Team的對象。演示代碼如下:

System.out.println(result.size());

while(result.hasNext()) {

// Print Player

Player p = (Player) result.next();

System.out.println(p);

// Getting Player also gets Team - print Team

Team t = p.getTeam();

System.out.println(t);

}

輸出:

2

Adrian Beltre:0.334

Dodgers

Barry Bonds:0.362

Giants

現在的查詢十分類似SQL語言查詢,如下:

SELECT teams.name, players.name, players.battingAverage FROM teams, players

WHERE teams.teamID = players.playerID

AND battingAverage > 0.3

這個能正常工作是因為相互引用關系已經被設計到對象模型中。對象數據庫是具有可以導航性的:你只要根據預定義的關系就可以取回數據。從另一方面講,關系數據庫並不能直接在表中連接。所以,這樣對於特定查詢更具有彈性的。然而,在給定對象關系中,從對象數據庫中取回對象的關系幾乎不要任何的程序代價。數據庫模型和應用程序對象模型是一致的,所以不需要程序員考慮數據的差異性。如果對象在內存裡的時候,你可以從一個給定的Player中得到它所屬的Team,你就可以在對象數據庫中做同樣事。

S.O.D.A.其他功能

SQL查詢允許結果按要求排序,S.O.D.A.也可以達到同樣功效。下面是一個例子演示了取回我們開始存儲的Player對象,並以“平均擊球率”排序。(很顯然,這樣可以知道哪些是投手)

Query q = db.query();  q.constrain(Player.class);  q.descend("battingAverage").orderAscending();

ObjectSet result = q.execute();

結果:

7

Kazuhisa Ishii:0.127, 13

Kirk Rueter:0.131, 9

Marquis Grissom:0.279

Cesar Izturis:0.288

Shawn Green:0.299

Adrian Beltre:0.334

Barry Bonds:0.362

S.O.D.A.可以定義更加復雜的查詢,一旦你除去考慮關系數據庫的方式思考它,那將十分的簡單。為了設置限制條件,你只需根據查詢圖導航,去尋找你想設置條件的類或屬性。查詢圖概念和對象模型領域關系十分緊密。這些對開發者理解十分有益。另一方面,為了達到類似SQL的查詢結果,你必須考慮如何映射對象領域和關系數據的表格。

下面的例子演示了如何在Player類的兩個屬性上設置條件。我們將去查找這樣的隊員:他的擊球率大於0.130,而且他是一個贏過五次以上的投手。另外,我們定義了一個查詢圖節點,限制它為Player類,然後,我們具體化這張圖去尋找一個節點代表屬性“平均擊球率”,使其大於0.13,這個結果是一個Constraint的對象。為了設置下一個限制條件,我們具體化去尋找節點代表屬性“贏”,這本身就意味著是去查找一個Pticher類對象。這個節點的限制條件是大於5,然後使用邏輯與――“AND”操作前一個Constraint對象。

Query q = db.query();

q.constrain(Player.class);

Constraint constr =

q.descend("battingAverage").constrain(

new Float(0.13f)).greater();

q.descend("wins").constrain(

new Integer(5)).greater().and(constr);

result = q.execute();

結果:

1

Kirk Rueter:0.131, 9

Giants

最後一個例子演示了如何組合不同類的屬性作為查詢條件。我們將查詢滿足如下條件的隊員:他的擊球率大於0.300並且他的球隊已經贏了92場以上。最簡單的方式是從Player類開始,然後導航到Team。我們可以和前面一樣具體化去查找“平均擊球率”節點,然後,生成一個Constraint對象。然後,具體化去查找Team的屬性的時候,這個屬性是Team的,而這個節點代表的是Team類,因此,我們可以再次具體化去查找一個節點代表“贏”的Team的贏得屬性,然後生成Constraint對象,最後,我們與前一個Constraint對象與操作即可。

Query q = db.query();

q.constrain(Player.class);

Constraint constr =

q.descend("battingAverage").constrain(

new Float(0.3f)).greater();

q.descend("team").descend("won").constrain(

new Integer(92)).smaller().and(constr);

result = q.execute();

結果:

1

Barry Bonds:0.362

Giants

結論

一個低功耗、可嵌入的對象數據庫提供了一個十分簡單、間接的方法處理對象的持久性。Db4o現在是一個開源的對象數據庫。它擁有一系列很具有吸引力的特點,同時支持Java和.Net。簡單的安裝,同時在對象模型和數據模型間不存在“錯誤匹配問題”。這些使得db4o無論在商業上還是教育上都有廣泛的應用。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved