前言
在開源面向?qū)ο髷?shù)據(jù)庫 db4o 之旅 系列文章的第 1 部分:初識 db4o 中,作者介紹了 db4o 的歷史和現(xiàn)狀,應(yīng)用領(lǐng)域,以及和 ORM 等的比較; 在第 2 部分:db4o 查詢方式中, 作者介紹了 db4o 的三種不同的查詢方式:QBE、SODA 以及 Native Queries,并分別通過這三種不同的途徑實現(xiàn)了兩個關(guān)聯(lián)對象的查詢。
前面我們已經(jīng)介紹了如何在 db4o 中查詢以及添加對象,在本文中我們將會向您介紹在 db4o 中如何對對象進(jìn)行更新以及刪除操作。?
更新數(shù)據(jù)
場景一
我們來設(shè)想這樣的場景:一位名叫“張三”的人買了車,并上好了牌照(如本系列第二部分之代碼),而他基本信息的地址并不詳細(xì),只寫了“成都市”,在一次主管部門檢查此人信息的時候,發(fā)現(xiàn)了這個問題,并立即著手修改。
在 db4o 中,我們這樣來實現(xiàn)對這個用戶信息的修改(清單1):
清單1. 修改地址
package com; import bo.People; import com.db4o.Db4o; import com.db4o.ObjectContainer; import com.db4o.ObjectSet; import com.db4o.query.Predicate; public class DB4OTest{ public static void main(String[] args){ //打開數(shù)據(jù)庫 ObjectContainer db = Db4o.openFile("auto.yap"); try{ ObjectSet<People> result = db.query(new Predicate<People>() { public boolean match(People people) { return people.getName().equals("張三"); } }); People people = result.next(); //修改地址 people.setAddress("成都市金牛區(qū)xxx號"); db.set(people); }finally{ //關(guān)閉連接 db.close(); } } }
修改數(shù)據(jù)是如此的簡單,通過 NQ 查詢出 People 對象,接著修改其地址,最后保存即可。現(xiàn)在我們來看看修改是否成功, 打開 ObjectManager ,如圖 1 所示,我們可以看到數(shù)據(jù)庫里的用戶數(shù)據(jù)已經(jīng)更新了。
圖1. 修改地址
與本系列文章第二部分不同的是,我們利用 ObjectSet<People> result 來獲取返回結(jié)果,而不是 List<People> list。查閱 ObjectSet 的 API 我們發(fā)現(xiàn) ObjectSet 實際上繼承了 java.util.List 和 java.util.Iterator。為什么要繼承兩個接口?這是由于 db4o 為了方便開發(fā)者而有意這樣設(shè)計的,db4o 的設(shè)計目標(biāo)就是輕量級,這樣的繼承方式為 ObjectSet 提供了多種特性,而無需開發(fā)者在多個集合接口之間轉(zhuǎn)換。
場景二
讓我們考慮下面這個場景:由于工作原因,“張三”要離開省會去其他城市發(fā)展,他的汽車也要在那里使用,為了方便,他還是決定重新更換為本地牌照。
這次我們幾乎和場景一采用同樣的代碼,但結(jié)果卻不同(清單2):
清單2. 修改地址和車牌(不成功)
package com; import bo.People; import com.db4o.Db4o; import com.db4o.ObjectContainer; import com.db4o.ObjectSet; import com.db4o.query.Predicate; public class DB4OTest{ public static void main(String[] args){ //打開數(shù)據(jù)庫 ObjectContainer db = Db4o.openFile("auto.yap"); try{ ObjectSet<People> result = db.query(new Predicate<People>() { public boolean match(People people) { return people.getName().equals("張三"); } }); People people = result.next(); //修改地址 people.setAddress("綿陽市xx區(qū)xxx號"); //修改車牌號 people.getAutoInfoList().get(0).setLicensePlate("川B00000"); db.set(people); }finally{ //關(guān)閉連接 db.close(); } } }
想必應(yīng)該保存成功了吧,只是多加入了設(shè)置車牌的代碼。打開 ObjectManager,如圖 2 所示。很奇怪,地址保存成功了,而車牌卻根本沒變化。
圖2. 修改地址和車牌(不成功)
其實這也是 db4o 的有意安排。設(shè)想一個復(fù)雜對象有很多成員,并且這些成員又有自己的成員。當(dāng)更新該對象,db4o 將不得不更新其所有的關(guān)聯(lián)對象、關(guān)聯(lián)對象的關(guān)聯(lián)對象,等等。這將引起嚴(yán)重的性能懲罰,而且在大部分的情況下是沒有必要這樣的。
db4o 引入了“更新深度(update depth)”這一概念來控制被更新的對象成員樹深度。默認(rèn)的更新深度是 1,這就意味著只有基本類型和 String 類型的成員變量可以被更新,而修改對象成員將得不到任何反映,例如本例中修改 People 對象的 _autoInfoList 成員。
為了能更新成員對象,ob4o 提供了 cascadeOnUpdate() 方法,該方法必須在每次開啟數(shù)據(jù)庫之前設(shè)置清單3:
清單3. 修改地址和車牌(成功)
package com; import bo.People; import com.db4o.Db4o; import com.db4o.ObjectContainer; import com.db4o.ObjectSet; import com.db4o.query.Predicate; public class DB4OTest{ public static void main(String[] args){ //級聯(lián)設(shè)置 Db4o.configure().objectClass("bo.People") .cascadeOnUpdate(true); //打開數(shù)據(jù)庫 ObjectContainer db = Db4o.openFile("auto.yap"); try{ ObjectSet<People> result = db.query(new Predicate<People>() { public boolean match(People people) { return people.getName().equals("張三"); } }); People people = result.next(); //修改地址 people.setAddress("綿陽市xx區(qū)xxx號"); //修改車牌號 people.getAutoInfoList().get(0).setLicensePlate("川B00000"); db.set(people); }finally{ //關(guān)閉連接 db.close(); } } }
這下終于如愿以償,如圖 3 所示。其實 db4o 為開發(fā)者想得很周到,關(guān)鍵是如何用好這些特性。
圖3. 修改地址和車牌(成功)
刪除數(shù)據(jù)
場景三
“張三”換了工作后,事業(yè)發(fā)展很快,準(zhǔn)備把車賣了換新的,于是他去交管部門辦理移交手續(xù),刪除關(guān)聯(lián)的車輛信息清單4:
清單4. 刪除車輛
package com; import bo.AutoInfo; import com.db4o.Db4o; import com.db4o.ObjectContainer; import com.db4o.ObjectSet; import com.db4o.query.Predicate; public class DB4OTest{ public static void main(String[] args){ //打開數(shù)據(jù)庫 ObjectContainer db = Db4o.openFile("auto.yap"); try{ ObjectSet<AutoInfo> result = db.query(new Predicate<AutoInfo>() { public boolean match(AutoInfo ai) { //匹配姓名和車牌號 return ai.getLicensePlate().equals("川B00000") && ai.getOwnerNo().getName().equals("張三"); } }); AutoInfo ai = result.next(); //刪除車輛信息 db.delete(ai); }finally{ //關(guān)閉連接 db.close(); } } }
如圖 4 所示,所關(guān)聯(lián)的車輛信息已被刪除了。
圖4. 刪除車輛信息
場景四
在場景三的基礎(chǔ)上修改一下,設(shè)想“張三”由于工作不順,導(dǎo)致最后維護(hù)汽車的開支都困難,他不得不退出有車一族的行列清單5:
清單5. 刪除所有信息
package com; import bo.People; import com.db4o.Db4o; import com.db4o.ObjectContainer; import com.db4o.ObjectSet; import com.db4o.query.Predicate; public class DB4OTest{ public static void main(String[] args){ //級聯(lián)設(shè)置 Db4o.configure().objectClass("bo.People") .cascadeOnDelete(true); //打開數(shù)據(jù)庫 ObjectContainer db = Db4o.openFile("auto.yap"); try{ ObjectSet<People> result = db.query(new Predicate<People>() { public boolean match(People people) { //匹配姓名 return people.getName().equals("張三"); } }); People people = result.next(); //刪除車主以及關(guān)聯(lián)的車輛信息 db.delete(people); }finally{ //關(guān)閉連接 db.close(); } } }
用過 Hibernate 的開發(fā)者都知道,它的級聯(lián)刪除讓人留下了深刻印象,第一次使用的時候都會為之振奮。db4o 也為開發(fā)者提供了級聯(lián)刪除,和場景二的級聯(lián)更新一樣, cascadeOnDelete() 是專門為刪除準(zhǔn)備的,基本概念和 cascadeOnUpdate() 一致。打開 ObjectManager 我們會發(fā)現(xiàn)數(shù)據(jù)庫已經(jīng)清空了,張三的購車經(jīng)歷到此結(jié)束。?
結(jié)論
通過本系列文章,db4o 的優(yōu)勢已經(jīng)體現(xiàn)得淋漓盡致,它的添加、更新、刪除是如此的簡單,正如 db4o 的口號那樣——“僅需一行代碼就能存儲復(fù)雜結(jié)構(gòu)對象,極大的降低了開發(fā)時間和成本,提供高效的性能,無需 DBA 干預(yù)”。
如本文有不詳盡之處,大家可以參考官方的《用戶指南》或訪問 db4o 官方中文論壇,db4o 中文社區(qū)正在火熱成長!?
參考資料
學(xué)習(xí)
獲得產(chǎn)品和技術(shù)
討論
作者簡介
Rosen Jiang 來自成都,是 db4o 和 OO 的忠實 fans,是 2005 年 db4o 的 dvp 獲得者之一。他正在 J2me 應(yīng)用中使用 db4o,你可以通過 rosener_722@hotmail.com 和他聯(lián)系。
Chris 來自香港,熱愛開源和 db4o。他創(chuàng)辦了中國最火熱的 Java 和開源社區(qū) Matrix(http://www.Matrix.org.cn), 你可以通過 chris@Matrix.org.cn 和他聯(lián)系。
張黃矚,熱愛開源軟件,熟悉 Java/C/C++ 編程語言,對數(shù)據(jù)庫技術(shù)網(wǎng)絡(luò)技術(shù)均感興趣。你可以通過 zhanghuangzhu@gmail.com 聯(lián)系他。
Powered by: BlogJava Copyright © Rosen