当前位置导航:炫浪网>>网络学院>>编程开发>>JAVA教程>>Java入门

开源面向对象数据库:db4o 查询方式

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.db4o
    com.db4o 包含了使用 db4o 时最经常用到的功能。两个最重要的接口是 com.db4o.Db4o 和 com.db4o.ObjectContainer。com.db4o.Db4o 工厂是运行 db4o 的起点,这个类中的静态方法可以开启数据库文件、启动服务器或连接一个已经存在的服务器,还可以在开启数据库之前进行 db4o 环境配置。com.db4o.ObjectContainer 接口很重要,开发过程中 99% 的时间都会用到它,ObjectContainer 可在单用户模式下作为数据库实例,也可作为 db4o 服务器的客户端。每个 ObjectContainer 实例都有自己的事务。所有的操作都有事务保证。当打开 ObjectContainer,就已经进入事务了,commit() 或 rollback() 时,下一个事务立即启动。每个 ObjectContainer 实例维护它自己所管理的已存储和已实例化对象,在需要 ObjectContainer 的时候,它会一直保持开启状态,一旦关闭,内存中数据库所引用的对象将被丢弃。
  • com.db4o.ext
    你也许想知道为什么在 ObjectContainer 中只能看见很少的方法,原因如下:db4o 接口提供了两个途径,分别在 com.db4o 和 com.db4o.ext 包中。这样做首先是为了让开发者能快速上手;其次为了让其他产品能更容易的复制基本的 db4o 接口;开发者从这一点上也能看出 db4o 是相当轻量级的。每个 com.db4o.ObjectContainer 对象也是 com.db4o.ext.ExtObjectContainer 对象。可以转换成 ExtObjectContainer 获得更多高级特性。
  • com.db4o.config
    com.db4o.config 包含了所有配置 db4o 所需的类。
  • com.db4o.query
    com.db4o.query 包包含了构造“原生查询, NQ(Native Queries)”所需的 Predicate 类。NQ 是 db4o 最主要的查询接口。

    db4o 提供两种运行模式,分别是本地模式和服务器模式。本地模式是指直接在程序里打开 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.List<AutoInfo> _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.List<AutoInfo> getAutoInfoList() {

		return this._autoInfoList;

	}



	public void addAutoInfo(AutoInfo _autoInfoList) {

		if (null == this._autoInfoList)

			this._autoInfoList = new java.util.ArrayList<AutoInfo>();

		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. 对象数据库管理工具
图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. 对象结构
图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)

    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");

			//查询对象

			List<AutoInfo> 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)。

 

相关内容
赞助商链接