IBM DeveloperWorks(IBM DW) 版權所有!引用、轉貼本文應注明本文來自 IBM DW。 前言
在開源面向對象數據庫 db4o 之旅 系列文章的第 1 部分:初識 db4o 中,作者介紹了 db4o 的歷史和現狀、應用領域、以及和 ORM 等的比較;在第 2 部分:db4o 查詢方式 中 , 作者介紹了 db4o 的三種不同的查詢方式:QBE、SODA 以及 Native Queries,并分別通過這三種不同的途徑實現了兩個關聯對象的查詢;在第 3 部分:深入 db4o 中,作者介紹了 db4o 的修改和刪除,引入了“更新深度 (update depth)”這一重要概念。
從本系列第 3 部分到現在的第 4 部分,中間經歷了漫長的時間。db4o 本身也在進步,2008 年 12 月,對象數據庫領導廠商 Versant 公司收購了 db4o 及其開發團隊,這次收購為 db4o 注入了新的活力。前面我們介紹了 db4o 中如何對對象進行更新以及刪除操作,在本文中我將向您介紹在 db4o 中如何與關系型數據庫 (RDBMS) 進行同步。
回頁首
dRS 應用范圍
dRS 充分利用了 Hibernate 的優勢,可實現 db4o 到 RDBMS、db4o 到 db4o、以及 RDBMS 到 RDBMS 的雙向或單向的數據同步。
如 圖 1 所示,我們來設想這樣的場景:一位名叫“張三”的車主買了幾輛車,隨即去主管部門辦牌照,辦證人員把數據采集進部署了 db4o 的手持設備(可能是基于 Android OS 的平板電腦);數據采集完后直接從手持設備通過無線、有線連接同步到桌面應用程序、應用服務器 (Hibernate/RDBMS) 中存檔。正確!無需再編寫額外的代碼來關心對象如何寫入 RDBMS。
圖 1. dRS 模型
下載并安裝 dRS
進入 db4o 的 下載頁面,可以看到最新的 for java 穩定版本都已經是 7.4 了,這次需要在下載的是 db4o Replication System(dRS) for Java,為了能順利運行本文的例子,請一并下載 db4o 的 7.4 版。在 Eclipse 中建立一個 Java 項目,把 dRS lib 下的 jar 包都導入進去。
本系列前幾篇文章提到的 ObjectManager 工具已經升級為 ObjectManagerEnterprise(OME),作為 Eclipse 插件運行,在 db4o 7.4 版 ZIP 壓縮包中的”\ome\ObjectManagerEnterprise-Java-7.4.0.zip”路徑下可找到。
裝載數據表
本系列前幾篇文章中的 AutoInfo 和 People 還可以沿用,只是略微做了調整,由于 dRS 需要 Hibernate 的支持,故還要配置 Hibernate 映射文件。需要注意的是,映射文件中必須設置名為”typed_id”的主鍵字段,”type”必須是”long”,而”class”必須是”native”,這樣做是為了 RDBMS 中能夠維護對象間的關系以及 dRS 自身的管理,稍后會看到”typed_id”是如何發揮作用的;另外,"default-cascade"屬性必須設置為"save-update",如果設置成”delete”了,dRS 將不響應刪除操作。相應的業務對象和映射文件請到 下載 部分獲取。
現在類和映射文件都寫好了,還要配置最重要的 Hibernate 配置文件。要注意的是"hibernate.connection.pool_size"屬性只需設置為"1",因為 dRS 到 RDBMS 只需要一個連接,多了也沒作用;"hibernate.jdbc.batch_size"設置為"0"是為了調試方便,在實際使用的時候還是設置一下較好;"hibernate.hbm2ddl.auto"一定要設置為”update”,這是因為 dRS 在向 RDBMS 裝載數據表的時候會創建額外的元數據表,如果設置為"validate",那么就需要自己手工去建這些表了,否則會報錯。
清單 1. Hibernate 配置文件
<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>
萬事具備,現在開始編寫裝載數據表的代碼吧,請看 清單 2 中的代碼,涉及到 dRS 的其實只有兩行,由于 People 和 AutoInfo 對象之間是 one-to-many 的關系,故只需要關注 People。
清單 2. CreateTable 類
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(); } }
如果不出意外,運行完上面的代碼后,相應的數據庫表也就被建立好了。如 圖 2所示,除了 people 和 autoinfo 表以外,還有三個 dRS 的元數據表。
圖 2. Oracle 數據庫表
從 db4o 到 RDBMS 的復制
請看 清單 3中的代碼,在”main”方法中,我構造了一個 db4o 配置類,并設置了 UUIDs 和 VersionNumbers 生成策略。這里的 UUID 是為了標識 db4o 中存儲的數據,而 VersionNubmers 則是為了 dRS 在同步時維護數據狀態,所以必須進行設置。
清單 3. ReplicationExample 類
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 數據庫 ) B(Oracle 數據庫 ) 關系 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 數據庫 ) 中存在的數據 ObjectSet changed = replication.providerA().objectsChangedSinceLastReplication(); // 復制到 Oracle 數據庫 while (changed.hasNext()){ replication.replicate(changed.next()); } replication.commit(); replication.close(); odb.close(); } }
在”createReplication”方法中,通過 HibernateReplication 類的 begin(odb, config) 方法,把 db4o 和 Hibernate 的配置實例聯系到一起;接下來向 db4o 中創建一個 People 對象和 10 個 AutoInfo 對象,并提交到 db4o;最后找出哪些是 db4o 中存在而 RDBMS 卻沒有的數據,把這些數據委托給 dRS,讓 dRS 復制到 RDBMS。請注意代碼注釋中 A (db4o 數據庫 ) B (Oracle 數據庫 ) 的含義和關系。運行完代碼后,可以發現 Oracle 中有了新數據,參看 圖 3、4。
圖 3. people 表 圖 4. autoinfo 表
從 db4o 到 RDBMS 的更新與刪除
現在來看看如何進行更新和刪除。注意 清單 4中的代碼,在 updateOraReplication 方法中,首先通過 QBE 查詢把車牌號為”川 A000001”的 AutoInfo 對象查出來,然后改成”川 B000001”提交到 db4o,隨后 dRS 發現有條數據被修改,找出來之后更新到 RDBMS;同樣,通過 QBE 查詢把車牌號為”川 A000002”的 AutoInfo 對象查出來,刪除后提交到 db4o,最后使用 ReplicationSession 實例的 replicateDeletions(AutoInfo.class) 方法來通知 dRS 對刪除數據進行處理。
清單 4. updateOraReplication 方法
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 數據庫 ) B(Oracle 數據庫 ) 關系 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 數據庫 ) 中修改過的數據 ObjectSet changed = replication.providerA().objectsChangedSinceLastReplication(); // 更新到 Oracle 數據庫 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 把更新和刪除操作一并提交。運行完代碼后立刻查看 autoinfo 表的變化吧。
從 RDBMS 到 db4o 的更新
dRS 支持雙向數據同步,剛才我們已經看到單向是如何同步的,現在看看修改了 RDBMS 中的數據后如何反映到 db4o 里。
在繼續寫代碼之前,講講前面提到的”type_id”字段,其實該字段是 dRS 在做維護的時候需要關注的。RDBMS 中有一個 dRS 自動生成的 drs_objects 表,該表維護了每條業務數據對應 db4o 中的類名、對應業務數據表的”type_id”、創建和修改時間。那么在 RDBMS 中修改了業務數據的值怎么通知 dRS 呢?答案是修改 drs_objects 表對應的修改時間,讓該時間大于上次同步操作的時間,如何做?執行這樣的 SQL 語句:update drs_objects t set t.modified=(select max(a.time)+1 from drs_history a) where t.typed_id=xxx,其中 xxx 代表你要修改的業務數據的”typed_id”,通過這個 SQL 語句,讓我們知道了其實 dRS 記錄同步操作時間是在 drs_history 表中,每次同步都會改變其中的值。
有了上面的認識接下來就好寫了,首先修改 autoinfo 表中”type_id”為”454”的數據,把車牌號改為”川 D000003”,然后執行 SQL:update drs_objects t set t.modified=(select max(a.time)+1 from drs_history a) where t.typed_id=454,主動更新修改時間。
清單 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 數據庫 ) B(Oracle 數據庫 ) 關系 ReplicationSession replication = HibernateReplication.begin(odb, config); // 找出 B(Oracle 數據庫 ) 中修改過的數據 ObjectSet changed = replication.providerB().objectsChangedSinceLastReplication(); // 同步到 db4o 數據庫 while (changed.hasNext()){ replication.replicate(changed.next()); } replication.commit(); replication.close(); odb.close(); }
運行 清單 5 中的代碼,打開 OME 管理工具,可以看到剛才修改的數據已經被同步到了 db4o 中。正確,dRS 在 RDBMS 中找到了更新后的記錄,而且修改時間是在上次同步之后,隨即同步到 db4o 里。在理解了如何從 RDBMS 更新數據到 db4o 之后,相應的刪除和新增操作也可通過類似的辦法處理。
結論
通過上面的例子不難發現 dRS 使 db4o 的原生對象持久化體系能適用于所有的 Java 和 .NET 開發者,能夠很好的處理和現有 RDBMS 的一致性,對于由此產生的數據沖突,dRS 也能很好的解決(請進一步參考 dRS 軟件包中的開發文檔)。dRS 100% 的面向對象且基于 GPL 開源授權,尤其適合敏捷企業開發和軟件制造商的產品快速更替,以及大多數的移動業務環境。
下載
關于下載方法的信息
參考資料
學習
獲得產品和技術
討論
作者簡介
Rosen Jiang 是 db4o 和 OO 的忠實 fans,是 2007、2008 年 db4o 的 dVP 獲得者之一,為 Versant db4o 在中國的推廣作出了卓越貢獻。
Tiger Lau 長期致力于 odbms 的研究和應用。
Powered by: BlogJava Copyright © Rosen