有一种朋友不在生活里,却在生命力;有一种陪伴不在身边,却在心间。图老师即在大家的生活中又在身边。这么贴心的服务你感受到了吗?话不多说下面就和大家分享开源面向对象数据库:db4o 查询方式吧。
【 tulaoshi.com - 编程语言 】
2006 年 11 月 20 这篇文章是 开源面向对象数据库 db4o 之旅 系列文章的第二篇,介绍了面向对象数据库 db4o 的安装、启动以及三种查询语言,并对三种查询语言做了比较。
前言
在 开源面向对象数据库 db4o 之旅 系列文章的第一部分:初识 db4o 中,作者介绍了 db4o 的历史和现状,应用领域,以及和 ORM 等的比较。在这篇文章中,作者将会介绍 db4o 的安装、启动以及三种不同的查询方式:QBE(Query by Example)、SODA(Simple Object Database Access) 以及 NQ(Native Queries),并分别通过这三种不同的途径实现了两个关联对象的查询。本文还示范了开发中最经常用到的几个典型功能的 db4o 实现。
下载和安装 db4o
db4o 所有最新的版本都可以直接在官方网站上下载,进入 db4o 的下载页面,我们可以看到最新的 for Java 稳定版本是 5.5,包括 JAR、源代码、入门文档、API 等内容的完整的打包文件只有 6 MB,db4o 还有一个对象数据库治理工具 ObjectManager,目前版本是 1.8(请在参考资源中下载)。
接着在 Eclipse 中新建 Java 项目,把 db4o 对象数据库引擎包 db4o-5.5-java5.jar 导入进项目。由于 db4o 支持多种版本的 JDK,除了 for JDK 5.0 的 db4o-5.5-java5.jar 外,还有 for JDK 1.1、1.2-1.4 的 JAR 包,以适应多种环境。与 Hibernate、iBATIS SQL Maps 相比,db4o 更加自然,无需过多地引用第三方支持库。
开启数据库
db4o 怎样进行对象持久化呢?通过浏览目录可以发现,与传统的 RDBMS 一样,db4o 也有自己的数据库文件, 在 db4o 中数据库文件的后缀名是“*.yap”。让我们先来了解一下 db4o 对象数据库引擎的主要包结构:
com.db4odb4o 提供两种运行模式,分别是本地模式和服务器模式。本地模式是指直接在程序里打开 db4o 数据库文件进行操作:
ObjectContainer db = Db4o.openFile("auto.yap");
而服务器模式则是客户端通过 IP 地址、端口以及授权口令来访问服务器:
服务器端:
ObjectServer server=Db4o.openServer("auto.yap",1212);server.grantAccess("admin","123456");
ObjectContainer db=Db4o.openClient("192.168.0.10",1212,"admin","123456");
两种方式都可以得到 ObjectContainer 实例,就目前 Java EE 应用环境来看,服务器模式更有现实意义;而本地模式更适合于嵌入式应用。为了简化演示,本文在下面的例子都将采用本地模式。
在下面的例子里,我们都会用到下面两个对象: People 和 AutoInfo 对象。
People 对象清单1:
清单1. People 对象
package bo;public class People {private java.lang.Integer _id;private java.lang.String _name;private java.lang.String _address;private java.util.ListAutoInfo _autoInfoList;public java.lang.Integer getId() {return _id;}public void setId(java.lang.Integer _id) {this._id = _id;}public java.lang.String getName() {return _name;}public void setName(java.lang.String _name) {this._name = _name;}public java.lang.String getAddress() {return _address;}public void setAddress(java.lang.String _address) {this._address = _address;}public java.util.ListAutoInfo getAutoInfoList() {return this._autoInfoList;}public void addAutoInfo(AutoInfo _autoInfoList) {if (null == this._autoInfoList)this._autoInfoList = new java.util.ArrayListAutoInfo();this._autoInfoList.add(_autoInfoList);}}
AutoInfo 对象清单2:
清单2. AutoInfo 对象
package bo;public class AutoInfo{private java.lang.Integer _id;private java.lang.String _licensePlate;private bo.People _ownerNo;public java.lang.Integer getId () {return _id;}public void setId (java.lang.Integer _id) {this._id = _id;}public java.lang.String getLicensePlate () {return _licensePlate;}public void setLicensePlate (java.lang.String _licensePlate) {this._licensePlate = _licensePlate;}public bo.People getOwnerNo () {return this._ownerNo;}public void setOwnerNo (bo.People _ownerNo) {this._ownerNo = _ownerNo;}}
利用 set 方法把新对象存入 ObjectContainer,而对 ObjectContainer 中已有对象进行 set 操作则是更新该对象。db4o 保存数据库很简单,下面就是一个段完整的保存对象的代码:
AutoInfo 对象清单3:
清单3
package com;import bo.AutoInfo;import bo.People;import com.db4o.Db4o;import com.db4o.ObjectContainer;public class DB4OTest{public static void main(String[] args){//打开数据库ObjectContainer db = Db4o.openFile("auto.yap");try{//构造 People 对象People peo = new People(); peo.setId(1);peo.setAddress("成都市");peo.setName("张三");//构造 AutoInfo 对象AutoInfo ai = new AutoInfo();ai.setId(1);ai.setLicensePlate("川A00000");//设置 People 和 AutoInfo 的关系ai.setOwnerNo(peo);peo.addAutoInfo(ai);//保存对象db.set(peo);}finally{//关闭连接db.close();}}}
当我们运行上述代码,db4o 会自动创建“auto.yap”文件。让我们来看看到底保存成功没有,打开 ObjectManager 工具,如图 1 所示。
图1. 对象数据库治理工具
“File”-“Open File”-选择刚才我们保存的“auto.yap”文件(“auto.yap”文件可在项目的根目录下找到),最新的 ObjectManager 1.8 版本为我们提供了“Read Only”方式读取数据库文件,避免 ObjectManager 占用数据库文件所导致的程序异常。
打开之后,如图 2 所示,刚才存贮的 People 对象已经在数据库中了,并且还可以很直观的看到 AutoInfo 对象也放入了 ArrayList 中。这种可视化的对象关系有利于我们对数据的理解,是传统 RDBMS 无法比拟的。有些开发者会说 ObjectManager 工具略显简单,这点我想随着 db4o 的不断发展会加入更多的特性。在这个工具中,我们意外的发现了 Java 集合对象的踪影,db4o 把与 ArrayList 有直接关系的所有接口和父类都保存了,这样显得更直观。
在此,我保留了 _id 属性,这是因为通常在 Java EE 环境中,DAO 第一次不是把整个对象都返回到表现层,而是只返回了“标题”、“发布时间”这些信息(并隐式的返回id),接着 DAO 与数据库断开;要查看详情(比如文章内容)就需要进行 findById 操作,这时 DAO 要再次与数据库交互,只有唯一标识符才能正确地找到对象。这种懒加载方式也是很多书籍所推荐的。
回到本文的范例程序中,这个 _id 属性可由人工编码实现的“序列”进行赋值,当然 db4o 也提供了内部标识符 Internal IDs,如图 2 中的 id=1669;以及 UUIDs。
图2. 对象结构
查询数据库
和 RDBMS 一样,db4o 也有自己的查询语言,分别是 QBE(Query by Example)、NQ(Native Queries)、SODA(Simple Object Database Access),db4o 更推荐使用 NQ 进行查询。NQ 方式提供了非常强大的查询功能,支持原生语言,也就意味着你可以使用 Java 来判定该对象是否符合条件,这是其他数据库查询语言无法比拟的。在某些情况下, db4o 核心会将 NQ 翻译成 SODA 以获得更高的性能。下面具体介绍一下这三种查询语言。
QBE(Query by Example)
(本文来源于图老师网站,更多请访问http://www.tulaoshi.com/bianchengyuyan/)QBE 规范可在这里下载。QBE 最初由 IBM 提出,同时业界也有许多和 QBE 兼容的接口,包括闻名的 Paradox。有些系统,比如微软的 Access,它的基于表单的查询也是受到了部分 QBE 思想的启发。在 db4o 中,用户可借用 QBE 快速上手,可以很轻易适应 db4o 存取数据的方式。
当利用 QBE 为 db4o 提供模板(example)对象时,db4o 将返回所有和非默认值字段匹配的全部对象。内部是通过反射所有的字段和构造查询表达式(所有非默认值字段结合”AND”表达式)来实现。
例如,利用 QBE 查找到车牌号为“川A00000”的车主姓名,这是一个级联查询。清单4:
清单4
package com;import java.util.List;import bo.AutoInfo;import com.db4o.Db4o;import com.db4o.ObjectContainer;public class DB4OTest{public static void main(String[] args){//打开数据库ObjectContainer db = Db4o.openFile("auto.yap");try{//构造模板对象AutoInfo ai = new AutoInfo();ai.setLicensePlate("川A00000");//查询对象ListAutoInfo list = db.get(ai);for(int x = 0; x list.size(); x++){System.out.println("车主姓名:"+list.get(x).getOwnerNo().getName());}}finally{//关闭连接db.close();}}}
但是 QBE 也有明显的限制:db4o 必须反射模板(example)对象的所有成员;无法执行更进一步的查询表达式(例如 AND、OR、NOT 等等);不能约束 0(整型)、””(空字符串)或者 null(对象),因为这些都被认为是不受约束的。要绕过这些限制,db4o 提供了 NQ(Native Queries)。
SODA(Simple Object Database Access)
SODA ,简单对象数据库访问,请查看官方站点,其中一位主要维护者是 Carl Rosenberger,Carl 正是 db4o 首席架构师。
SODA 就是一种与数据库通讯的对象 API。最终的目标是实现类型安全、对象复用、最小的字符串使用、与编程语言无关等特性。SODA 是 db4o 最底层的查询 API,目前 SODA 中使用字符串来定义字段,这样将不能实现类型安全也无法在编译时检查代码,而且写起来较麻烦,当然要达到设计目标这个阶段是必须的。大部分情况下 NQ(Native Queries)是很好的查询接口,不过碰到动态生成查询的时候 SODA 就大有作为了。
通过 SODA 查找到车牌号为“川A00000”的车主姓名。清单5:
清单5
package com;import java.util.List;import bo.AutoInfo;import com.db4o.Db4o;import com.db4o.ObjectContainer;import com.db4o.query.Query;public class DB4OTest{public static void main(String[] args){//打开数据库ObjectContainer db = Db4o.openFile("auto.yap");try{//构造查询对象Query query=db.query();//设置被约束实例query.constrain(AutoInfo.class);//设置被约束实例的字段和约束条件query.descend("_licensePlate").constrain("川A00000");//查询对象ListAutoInfo list = query.execute();for(int x = 0; x list.size(); x++){System.out.println("车主姓名:"+list.get(x).getOwnerNo().getName());}}finally{//关闭连接db.close();}}}
通过 API,发现 Query 实例增加了 sortBy 按字段排序方法和 orderAscending正序、orderDescending 倒序排列方法,SODA 比 QBE 更进了一步。
NQ(Native Queries)
出色总是在最后出场,NQ 才是 db4o 查询方式中最出色的地方!有没有想过用你熟悉的的编程语言进行数据库查询呢?要是这样,你的查询代码将是 100% 的类型安全、100% 的编译时检查以及 100% 的可重构,很奇妙吧?NQ 可以做到这些。
有两篇论文专门讲解了 NQ 的基本概念和设计思路,分别是 《Cook/Rosenberger,持久对象原生数据库查询语言》 和 《Cook/Rai,Safe Query Objects: Statically Typed Objects as Remotely Executable Queries》。作为结果集的一部分,NQ 表达式必须返回 true 值来标记特定实例。假如可能的话 db4o 将尝试优化 NQ 表达式,并依靠索引来运行表达式。
通过 NQ 查找到车牌号为“川A00000”的车主姓名。清单6:
清单6
package com;import java.util.List;import bo.AutoInfo;import com.db4o.Db4o;import com.db4o.ObjectContainer;import com.db4o.query.Predicate;public class DB4OTest{public static void main(String[] args){//打开数据库ObjectContainer db = Db4o.openFile("auto.yap");try{List AutoInfo list = db.query(new PredicateAutoInfo() {public boolean match(AutoInfo ai) {//这样才是类型安全的return ai.getLicensePlate().equals("川A00000");}});for(int x = 0; x list.size(); x++){System.out.println(list.get(x).getOwnerNo().getName());}}finally{//关闭连接db.close();}}}
QQRead.com 推出数据恢复指南教程 数据恢复指南教程 数据恢复故障解析 常用数据恢复方案 硬盘数据恢复教程 数据保护方法 数据恢复软件 专业数据恢复服务指南
必须指出 NQ 的一个的问题是:在内部,db4o 设法把 NQ 转换成 SODA。但并不是所有的查询表达式都可以成功转换。有些查询表达式的流向图(flowgraph)非常难于分析。这种情况下,db4o 将不得不实例化一些持久对象来真实地运行 NQ 表达式。
正在开发中的 NQ 查询优化器就可以化解这个障碍,它将分析 NQ 表达式的每个部分,以确保最少量的实例化对象,以此提高性能。当然,优化器的不是灵丹妙药,要害还需要自己多优化代码。
开发 Java EE 项目经常会用到分页,怎样用 NQ 实现呢?向数据库写入六条记录。清单7:
清单7
package com;import java.util.List;import bo.AutoInfo;import com.db4o.Db4o;import com.db4o.ObjectContainer;import com.db4o.query.Predicate;public class DB4OTest{public static void main(String[] args){//打开数据库ObjectContainer db = Db4o.openFile("auto.yap");try{ListAutoInfo list = db.query(new PredicateAutoInfo() {public boolean match(AutoInfo ai) {return true;}});//记录总数Integer count = list.size();//每页两条,分三页for(int x = 0; x 3; x++){System.out.println("第"+x+"页:"+list.get(x*2).getLicensePlate());System.out.println("第"+x+"页:"+list.get(x*2+1).getLicensePlate());}}finally{//关闭连接db.close();}}}
我们发现,在进行 NQ 查询时并没有加入任何条件(无条件返回 true),是不是相当于遍历了整个数据库?db4o 的设计者早就想到了这个问题,当 db.query() 执行完毕返回 list 实例的时候,db4o 只是与数据库同步取出内部 IDs 而已,并没有把所有的 AutoInfo 对象全部取出,只有在 list.get(x*2).getLicensePlate() 之后才会去根据 IDs 取出记录。所以不必担心性能问题。
结论
db4o 为开发者提供了多种查询方式,这些方式都很灵活。要引起大家注重的是:灵活在带来便利的同时也对开发者自身素质提出了更高的要求,(比如排序,既可以用 SODA 也可以用 Java 集合对象实现)在开发过程中一定要形成某种统一的开发模式,这样 db4o 才能最高效能地为我所用。
参考资料
学习
获得产品和技术
作者简介
Rosen Jiang 来自成都,是 db4o 和 OO 的忠实 fans,是 2005 年 db4o 的 dvp 获得者之一。他正在 J2me 应用中使用 db4o,你可以通过 rosener_722@hotmail.com 和他联系。
(本文来源于图老师网站,更多请访问http://www.tulaoshi.com/bianchengyuyan/)
Chris 来自香港,热爱开源和 db4o。他创办了中国最火热的 Java 和开源社区 Matrix(http://www.Matrix.org.cn), 你可以通过 chris@Matrix.org.cn 和他联系。
张黄瞩,热爱开源软件,熟悉 Java/C/C++ 编程语言,对数据库技术网络技术均感爱好。你可以通过 zhanghuangzhu@gmail.com 联系他。
来源:http://www.tulaoshi.com/n/20160219/1622723.html
看过《开源面向对象数据库:db4o 查询方式》的人还看了以下文章 更多>>