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 开源授权,尤其适合敏捷企业开发和软件制造商的产品快速更替,以及大多数的移动业务环境。
回页首
下载
描述 |
名字 |
大小 |
下载方法 |
本文用到的业务对象和映射文件 |
bo_mapping.zip |
2 KB |
HTTP |
关于下载方法的信息
参考资料
学习
获得产品和技术
讨论
作者简介
Rosen Jiang 是 db4o 和 OO 的忠实 fans,是 2007、2008 年 db4o 的 dVP 获得者之一,为 Versant db4o 在中国的推广作出了卓越贡献。
Tiger Lau 长期致力于 odbms 的研究和应用。
IBM DeveloperWorks(IBM DW) 版权所有!引用、转贴本文应注明本文来自 IBM DW。