IBM DeveloperWorks(IBM DW) 版權(quán)所有!引用、轉(zhuǎn)貼本文應(yīng)注明本文來自 IBM DW。
前言
在開源面向?qū)ο髷?shù)據(jù)庫 db4o 之旅 系列文章的第 1 部分:初識(shí) db4o 中,作者介紹了 db4o 的歷史和現(xiàn)狀、應(yīng)用領(lǐng)域、以及和 ORM 等的比較;在第 2 部分:db4o 查詢方式 中 , 作者介紹了 db4o 的三種不同的查詢方式:QBE、SODA 以及 Native Queries,并分別通過這三種不同的途徑實(shí)現(xiàn)了兩個(gè)關(guān)聯(lián)對(duì)象的查詢;在第 3 部分:深入 db4o 中,作者介紹了 db4o 的修改和刪除,引入了“更新深度 (update depth)”這一重要概念。
從本系列第 3 部分到現(xiàn)在的第 4 部分,中間經(jīng)歷了漫長的時(shí)間。db4o 本身也在進(jìn)步,2008 年 12 月,對(duì)象數(shù)據(jù)庫領(lǐng)導(dǎo)廠商 Versant 公司收購了 db4o 及其開發(fā)團(tuán)隊(duì),這次收購為 db4o 注入了新的活力。前面我們介紹了 db4o 中如何對(duì)對(duì)象進(jìn)行更新以及刪除操作,在本文中我將向您介紹在 db4o 中如何與關(guān)系型數(shù)據(jù)庫 (RDBMS) 進(jìn)行同步。
dRS 充分利用了 Hibernate 的優(yōu)勢(shì),可實(shí)現(xiàn) db4o 到 RDBMS、db4o 到 db4o、以及 RDBMS 到 RDBMS 的雙向或單向的數(shù)據(jù)同步。
如 圖 1 所示,我們來設(shè)想這樣的場(chǎng)景:一位名叫“張三”的車主買了幾輛車,隨即去主管部門辦牌照,辦證人員把數(shù)據(jù)采集進(jìn)部署了 db4o 的手持設(shè)備(可能是基于 Android OS 的平板電腦);數(shù)據(jù)采集完后直接從手持設(shè)備通過無線、有線連接同步到桌面應(yīng)用程序、應(yīng)用服務(wù)器 (Hibernate/RDBMS) 中存檔。正確!無需再編寫額外的代碼來關(guān)心對(duì)象如何寫入 RDBMS。
進(jìn)入 db4o 的 下載頁面,可以看到最新的 for java 穩(wěn)定版本都已經(jīng)是 7.4 了,這次需要在下載的是 db4o Replication System(dRS) for Java,為了能順利運(yùn)行本文的例子,請(qǐng)一并下載 db4o 的 7.4 版。在 Eclipse 中建立一個(gè) Java 項(xiàng)目,把 dRS lib 下的 jar 包都導(dǎo)入進(jìn)去。
本系列前幾篇文章提到的 ObjectManager 工具已經(jīng)升級(jí)為 ObjectManagerEnterprise(OME),作為 Eclipse 插件運(yùn)行,在 db4o 7.4 版 ZIP 壓縮包中的”\ome\ObjectManagerEnterprise-Java-7.4.0.zip”路徑下可找到。
本系列前幾篇文章中的 AutoInfo 和 People 還可以沿用,只是略微做了調(diào)整,由于 dRS 需要 Hibernate 的支持,故還要配置 Hibernate 映射文件。需要注意的是,映射文件中必須設(shè)置名為”typed_id”的主鍵字段,”type”必須是”long”,而”class”必須是”native”,這樣做是為了 RDBMS 中能夠維護(hù)對(duì)象間的關(guān)系以及 dRS 自身的管理,稍后會(huì)看到”typed_id”是如何發(fā)揮作用的;另外,"default-cascade"屬性必須設(shè)置為"save-update",如果設(shè)置成”delete”了,dRS 將不響應(yīng)刪除操作。相應(yīng)的業(yè)務(wù)對(duì)象和映射文件請(qǐng)到 下載 部分獲取。
現(xiàn)在類和映射文件都寫好了,還要配置最重要的 Hibernate 配置文件。要注意的是"hibernate.connection.pool_size"屬性只需設(shè)置為"1",因?yàn)?dRS 到 RDBMS 只需要一個(gè)連接,多了也沒作用;"hibernate.jdbc.batch_size"設(shè)置為"0"是為了調(diào)試方便,在實(shí)際使用的時(shí)候還是設(shè)置一下較好;"hibernate.hbm2ddl.auto"一定要設(shè)置為”update”,這是因?yàn)?dRS 在向 RDBMS 裝載數(shù)據(jù)表的時(shí)候會(huì)創(chuàng)建額外的元數(shù)據(jù)表,如果設(shè)置為"validate",那么就需要自己手工去建這些表了,否則會(huì)報(bào)錯(cuò)。
<hibernate-configuration> <session-factory> <property name="hibernate.connection.driver_class"> oracle.jdbc.driver.OracleDriver</property> <property name="hibernate.connection.url"> jdbc:oracle:thin:@192.168.1.173:1521:ora10g</property> <property name="hibernate.connection.username">test</property> <property name="hibernate.connection.password">test</property> <property name="hibernate.connection.pool_size">1</property> <property name="hibernate.dialect"> org.hibernate.dialect.OracleDialect</property> <property name="hibernate.show_sql">false</property> <property name="hibernate.hbm2ddl.auto">update</property> <property name="hibernate.jdbc.batch_size">0</property> <mapping resource="bo/People.hbm.xml"/> <mapping resource="bo/AutoInfo.hbm.xml"/> </session-factory> </hibernate-configuration> |
萬事具備,現(xiàn)在開始編寫裝載數(shù)據(jù)表的代碼吧,請(qǐng)看 清單 2 中的代碼,涉及到 dRS 的其實(shí)只有兩行,由于 People 和 AutoInfo 對(duì)象之間是 one-to-many 的關(guān)系,故只需要關(guān)注 People。
public class CreateTable { public static void main(String args[]){ Configuration cfg = new Configuration().configure("hibernate.cfg.xml"); ReplicationConfigurator.configure(cfg); SessionFactory sessionFactory = cfg.buildSessionFactory(); Session session = sessionFactory.openSession(); ReplicationConfigurator.install(session, cfg); session.createCriteria(People.class); session.close(); sessionFactory.close(); } } |
如果不出意外,運(yùn)行完上面的代碼后,相應(yīng)的數(shù)據(jù)庫表也就被建立好了。如 圖 2所示,除了 people 和 autoinfo 表以外,還有三個(gè) dRS 的元數(shù)據(jù)表。
請(qǐng)看 清單 3中的代碼,在”main”方法中,我構(gòu)造了一個(gè) db4o 配置類,并設(shè)置了 UUIDs 和 VersionNumbers 生成策略。這里的 UUID 是為了標(biāo)識(shí) db4o 中存儲(chǔ)的數(shù)據(jù),而 VersionNubmers 則是為了 dRS 在同步時(shí)維護(hù)數(shù)據(jù)狀態(tài),所以必須進(jìn)行設(shè)置。
public class ReplicationExample { public static void main(String args[]){ com.db4o.config.Configuration db4oconf = Db4o.newConfiguration(); db4oconf.generateUUIDs(ConfigScope.GLOBALLY); db4oconf.generateVersionNumbers(ConfigScope.GLOBALLY); createReplication(db4oconf); } private static void createReplication(com.db4o.config.Configuration db4oconf){ ObjectContainer odb = Db4o.openFile(db4oconf, "auto.yap"); Configuration config = new Configuration().configure("hibernate.cfg.xml"); // 綁定 A(db4o 數(shù)據(jù)庫 ) B(Oracle 數(shù)據(jù)庫 ) 關(guān)系 ReplicationSession replication = HibernateReplication.begin(odb, config); People peo = new People(); peo.setAddress("成都市"); peo.setName("張三"); for(int i=0; i<10; i++){ AutoInfo ai = new AutoInfo(); ai.setLicensePlate("川 A00000"+i); peo.addAutoInfo(ai); } odb.store(peo); odb.commit(); // 找出 A(db4o 數(shù)據(jù)庫 ) 中存在的數(shù)據(jù) ObjectSet changed = replication.providerA().objectsChangedSinceLastReplication(); // 復(fù)制到 Oracle 數(shù)據(jù)庫 while (changed.hasNext()){ replication.replicate(changed.next()); } replication.commit(); replication.close(); odb.close(); } } |
在”createReplication”方法中,通過 HibernateReplication 類的 begin(odb, config) 方法,把 db4o 和 Hibernate 的配置實(shí)例聯(lián)系到一起;接下來向 db4o 中創(chuàng)建一個(gè) People 對(duì)象和 10 個(gè) AutoInfo 對(duì)象,并提交到 db4o;最后找出哪些是 db4o 中存在而 RDBMS 卻沒有的數(shù)據(jù),把這些數(shù)據(jù)委托給 dRS,讓 dRS 復(fù)制到 RDBMS。請(qǐng)注意代碼注釋中 A (db4o 數(shù)據(jù)庫 ) B (Oracle 數(shù)據(jù)庫 ) 的含義和關(guān)系。運(yùn)行完代碼后,可以發(fā)現(xiàn) Oracle 中有了新數(shù)據(jù),參看 圖 3、4。
現(xiàn)在來看看如何進(jìn)行更新和刪除。注意 清單 4中的代碼,在 updateOraReplication 方法中,首先通過 QBE 查詢把車牌號(hào)為”川 A000001”的 AutoInfo 對(duì)象查出來,然后改成”川 B000001”提交到 db4o,隨后 dRS 發(fā)現(xiàn)有條數(shù)據(jù)被修改,找出來之后更新到 RDBMS;同樣,通過 QBE 查詢把車牌號(hào)為”川 A000002”的 AutoInfo 對(duì)象查出來,刪除后提交到 db4o,最后使用 ReplicationSession 實(shí)例的 replicateDeletions(AutoInfo.class) 方法來通知 dRS 對(duì)刪除數(shù)據(jù)進(jìn)行處理。
private static void updateOraReplication(com.db4o.config.Configuration db4oconf){ ObjectContainer odb = Db4o.openFile(db4oconf, "auto.yap"); Configuration config = new Configuration().configure("hibernate.cfg.xml"); // 綁定 A(db4o 數(shù)據(jù)庫 ) B(Oracle 數(shù)據(jù)庫 ) 關(guān)系 ReplicationSession replication = HibernateReplication.begin(odb, config); // 更新 AutoInfo ai = new AutoInfo(); ai.setLicensePlate("川 A000001"); List<AutoInfo> list = odb.queryByExample(ai); if(list.size() == 1){ ai = list.get(0); ai.setLicensePlate("川 B000001"); odb.store(ai); } odb.commit(); // 找出 A(db4o 數(shù)據(jù)庫 ) 中修改過的數(shù)據(jù) ObjectSet changed = replication.providerA().objectsChangedSinceLastReplication(); // 更新到 Oracle 數(shù)據(jù)庫 while (changed.hasNext()){ replication.replicate(changed.next()); } // 刪除 ai = new AutoInfo(); ai.setLicensePlate("川 A000002"); list = odb.queryByExample(ai); if(list.size() == 1){ odb.delete(list.get(0)); } odb.commit(); replication.replicateDeletions(AutoInfo.class); replication.commit(); replication.close(); odb.close(); } |
dRS 把更新和刪除操作一并提交。運(yùn)行完代碼后立刻查看 autoinfo 表的變化吧。
dRS 支持雙向數(shù)據(jù)同步,剛才我們已經(jīng)看到單向是如何同步的,現(xiàn)在看看修改了 RDBMS 中的數(shù)據(jù)后如何反映到 db4o 里。
在繼續(xù)寫代碼之前,講講前面提到的”type_id”字段,其實(shí)該字段是 dRS 在做維護(hù)的時(shí)候需要關(guān)注的。RDBMS 中有一個(gè) dRS 自動(dòng)生成的 drs_objects 表,該表維護(hù)了每條業(yè)務(wù)數(shù)據(jù)對(duì)應(yīng) db4o 中的類名、對(duì)應(yīng)業(yè)務(wù)數(shù)據(jù)表的”type_id”、創(chuàng)建和修改時(shí)間。那么在 RDBMS 中修改了業(yè)務(wù)數(shù)據(jù)的值怎么通知 dRS 呢?答案是修改 drs_objects 表對(duì)應(yīng)的修改時(shí)間,讓該時(shí)間大于上次同步操作的時(shí)間,如何做?執(zhí)行這樣的 SQL 語句:update drs_objects t set t.modified=(select max(a.time)+1 from drs_history a) where t.typed_id=xxx,其中 xxx 代表你要修改的業(yè)務(wù)數(shù)據(jù)的”typed_id”,通過這個(gè) SQL 語句,讓我們知道了其實(shí) dRS 記錄同步操作時(shí)間是在 drs_history 表中,每次同步都會(huì)改變其中的值。
有了上面的認(rèn)識(shí)接下來就好寫了,首先修改 autoinfo 表中”type_id”為”454”的數(shù)據(jù),把車牌號(hào)改為”川 D000003”,然后執(zhí)行 SQL:update drs_objects t set t.modified=(select max(a.time)+1 from drs_history a) where t.typed_id=454,主動(dòng)更新修改時(shí)間。
清單 5. updateDb4oReplication 方法
private static void updateDb4oReplication(com.db4o.config.Configuration db4oconf){ ObjectContainer odb = Db4o.openFile(db4oconf, "auto.yap"); Configuration config = new Configuration().configure("hibernate.cfg.xml"); // 綁定 A(db4o 數(shù)據(jù)庫 ) B(Oracle 數(shù)據(jù)庫 ) 關(guān)系 ReplicationSession replication = HibernateReplication.begin(odb, config); // 找出 B(Oracle 數(shù)據(jù)庫 ) 中修改過的數(shù)據(jù) ObjectSet changed = replication.providerB().objectsChangedSinceLastReplication(); // 同步到 db4o 數(shù)據(jù)庫 while (changed.hasNext()){ replication.replicate(changed.next()); } replication.commit(); replication.close(); odb.close(); } |
運(yùn)行 清單 5 中的代碼,打開 OME 管理工具,可以看到剛才修改的數(shù)據(jù)已經(jīng)被同步到了 db4o 中。正確,dRS 在 RDBMS 中找到了更新后的記錄,而且修改時(shí)間是在上次同步之后,隨即同步到 db4o 里。在理解了如何從 RDBMS 更新數(shù)據(jù)到 db4o 之后,相應(yīng)的刪除和新增操作也可通過類似的辦法處理。
通過上面的例子不難發(fā)現(xiàn) dRS 使 db4o 的原生對(duì)象持久化體系能適用于所有的 Java 和 .NET 開發(fā)者,能夠很好的處理和現(xiàn)有 RDBMS 的一致性,對(duì)于由此產(chǎn)生的數(shù)據(jù)沖突,dRS 也能很好的解決(請(qǐng)進(jìn)一步參考 dRS 軟件包中的開發(fā)文檔)。dRS 100% 的面向?qū)ο笄一?GPL 開源授權(quán),尤其適合敏捷企業(yè)開發(fā)和軟件制造商的產(chǎn)品快速更替,以及大多數(shù)的移動(dòng)業(yè)務(wù)環(huán)境。
描述 | 名字 | 大小 | 下載方法 |
---|---|---|---|
本文用到的業(yè)務(wù)對(duì)象和映射文件 | bo_mapping.zip | 2 KB | HTTP |
學(xué)習(xí)
- 訪問:Versant 中國 網(wǎng)站。
- 訪問:Versant db4o 官方網(wǎng)站。
- 訪問:developerWorks Java 技術(shù)專區(qū):數(shù)百篇關(guān)于 Java 編程各個(gè)方面的文章。
獲得產(chǎn)品和技術(shù)
討論
- 到 Versant db4o 論壇 尋求幫助。
- 查看 developerWorks 博客 的最新信息。