Java 数据对象(JDO)介绍
by
Jeff Brown, Senior Software Engineer
Object Computing, Inc. (OCI)
介绍
Java 数据对象 (JDO) is 是一个存储Java对象的规范. 它已经被JCP组织定义成JSR12规范。JDO的第一个版本在2000年6月6日提交并在2002年4月30日正式发布1.0版本规范。
规范的两个主要目的是提供数据处理和访问机制的API以及允许规范的实现作为应用服务器的一部分。
JDO 和 JDBC
JDBC和JDO都是Java调用数据库的APIs。区别在于,他们访问数据存储的具体方法不同。JDBC提供了一种非常好的机制,它可以使应用程序代码与具体的数据库厂商和数据库位置无关。在大多数情况下数据存储使用的是关系数据库。但是当使用JDBC驱动运行在一个非关系数据库时就不是很好了。而对于JDO来说,运行在它之下的数据库就可以是一个关系数据库,面向对象的数据库或者其他完全不同的数据库。在JDO运行于关系数据库之上时,它可以通过JDBC很好的完成数据存储。而这一切对于开发人员来说都是透明的,所有实现都有JDO本身来完成。
JDBC不支持面向对象的数据库表示。JDBC数据库表示完全围绕关系数据库模型。这常常导致书写代码时在应用程序和数据库之间存在一个中间层。这个层的作用也许是分解Java对象。分解Java对象的过程会对使用的对象稍作改变,这样对象才能存储到关系数据库之中。同样的必须存在一种机制来把数据库中的记录转换成适当的Java对象。JDO与JDBC刚好相反,他的数据库标示完全面向对象。这种机制并不新奇,在一些面向对象数据库中已经使用了这种机制。
JDBC的查询语言一般都是用SQL。JDO的查询语言看起来与Java更接近。使用JDO无需再专门学习一种查询语言比如SQL。如果你会用Java那么你就能够使用JDO的查询语言。
执行
JDO规范定义了JDO实现的接口已经JDO实现的内容。SUN提供了一个JDO规范的参考实现,但是在2002年5月份发布的版本中它并没有完全实现规范中所要求的内容。还有众多的厂商提供的JDO实现可以使用。在下面的表格中列出了一部分。
厂商
产品
SolarMetric
PrismTech
Signsoft
Poet
API
javax.jdo.spi.PersistenceCapable
任何被JDO实现控制的类都必须扩展PersistenceCapable接口。任何扩展PersistenceCapable接口的类的实例都可以看作是一个“JDO实例”。这个接口定义了JDO实现使用的方法来控制类的实例。
public abstract javax.jdo.PersistenceManager jdoGetPersistenceManager();
public abstract void jdoReplaceStateManager(javax.jdo.spi.StateManager)
throws SecurityException;
public abstract void jdoProvideField(int);
public abstract void jdoProvideFields(int[]);
public abstract void jdoReplaceField(int);
public abstract void jdoReplaceFields(int[]);
public abstract void jdoReplaceFlags();
public abstract void jdoCopyFields(Object, int[]);
public abstract void jdoMakeDirty(String);
public abstract Object jdoGetObjectId();
public abstract Object jdoGetTransactionalObjectId();
public abstract boolean jdoIsDirty();
public abstract boolean jdoIsTransactional();
public abstract boolean jdoIsPersistent();
public abstract boolean jdoIsNew();
public abstract boolean jdoIsDeleted();
public abstract javax.jdo.spi.PersistenceCapable
jdoNewInstance(javax.jdo.spi.StateManager);
public abstract javax.jdo.spi.PersistenceCapable
jdoNewInstance(javax.jdo.spi.StateManager, Object);
public abstract Object jdoNewObjectIdInstance();
public abstract Object jdoNewObjectIdInstance(String);
public abstract void jdoCopyKeyFieldsToObjectId(Object);
public abstract void jdoCopyKeyFieldsToObjectId(
javax.jdo.spi.PersistenceCapable.ObjectIdFieldSupplier, Object);
public abstract void jdoCopyKeyFieldsFromObjectId(
javax.jdo.spi.PersistenceCapable.ObjectIdFieldConsumer, Object);
通常会有一个由JDO实现厂商提供的工具来完成普通的Java类到“JDO实例”类的转化。这个工具可能通过不同的方法来实现。第一种方法是通过字节码增强器来实现。一个JDO字节码增强器把普通的Java类转换成“JDO实例”类的过程就是把扩展PersistenceCapable接口的方法的代码加到类中。第二种方法是使用一个代码生成器,它解析现有的普通Java代码然后输出一个扩展了PersistenceCapable接口的新版本的代码。如果通过手工方式来扩展这个接口是没有好处的。
javax.jdo.PersistenceManagerFactory
PersistenceManagerFactory接口 被用来获得一个PersistenceManager 实例。这个接口中定义两个工厂方法(有关工厂方法的内容可以参考Java设计模式)。
public PersistenceManager getPersistenceManager()
public PersistenceManager getPersistenceManager(String userid,
String password)
Because PersistenceManagerFactory is an interface, some vendor specific class, which implements this interface, must be used as a bootstrap mechanism. This should turn out to be the only vendor specific code that a JDO application uses. Because of this, the JDO specification suggests that an application level factory class be implemented which returns the appropriate instance of the PersistenceManagerFactory so that implementations may be swapped out with minimal impact on application code. Only the application's factory would need to be modified in this case.
因为PersistenceManagerFactory 是一个接口,一些实现该接口的厂商特定的类必须通过一种“自举”机制来使用。
// SolarMetric's 实现PersistenceManagerFactory 接口的例子...
PersistenceManagerFactory managerFactory =
new com.solarmetric.kodo.impl.jdbc.JDBCPersistenceManagerFactory();
// 获得一个控制器 manager...
PersistenceManager manager = managerFactory.getPersistenceManager();
javax.jdo.PersistenceManager
The PersistenceManager interface is the primary point of contact between a Java application and the JDO implementation. Application code uses a PersistenceManager to retrieve Java objects from the data store and to add Java objects to the data store. The PersistenceManager interface also serves as a factory for several other JDO components discussed below.
PersistenceManager 接口是连接Java应用程序和JDO实现的要点。应用程序使用PersistenceManager 从数据存储中获得对象或者把一个Java对象放到数据存储中。PersistenceManager 接口也为下面将要讨论的几个JDO组件提供服务。
PersistenceManager 接口中定义了几个方法来把JDO实例对象添加到数据存储中。
public abstract void makePersistent(Object);
public abstract void makePersistentAll(Object[]);
public abstract void makePersistentAll(java.util.Collection);
通过下面几个方法实现把JDO实例对象添加到数据存储中的过程:
// 获得一个 manager...
PersistenceManager manager = managerFactory.getPersistenceManager();
// 下面的 Employee 必须扩展了 PersistenceCapable...
Employee newEmployee = new Employee(...);
manager.makePersistent(newEmployee);
javax.jdo.Extent
Extent (范围)对象表示所有的在当前数据库中实际的类对象。在PersistenceManager 中的一个工厂方法负责获得一个Extent (范围)对象。
public Extent getExtent(Class persistenceCapableClass, boolean subclasses)
Class参数标明了接收的对象类型。boolean 参数标明是否包含第一个参数指定的类的子类。
Extent 接口定义了一个iterator()方法,它返回一个 java.util.Iterator来遍历所有由Extent 描述的实例。
//获得一个 manager...
PersistenceManager manager = managerFactory.getPersistenceManager();
// the Employee class must implement PersistenceCapable...
Extent employeesExtent = manager.getExtent(Employee.class, false);
java.util.Iterator iterator = employeesExtent.iterator();
javax.jdo.Query
Query 接口允许从数据存储中获得符合某些条件的实例。Query 实例可以重载自PersistenceManager 接口中的newQuery()方法获得。
Query 接口定义了几个不同版本的重载execute() 的方法,这些方法将执行查询(Query) 并返回匹配的结果。
// 获得一个 manager...
PersistenceManager manager = managerFactory.getPersistenceManager();
// Employee 类必须实现 PersistenceCapable接口...
Extent employeesExtent = manager.getExtent(Employee.class, false);
// 一个Query 获得所有在公司工作五年以上的 Employees....
Query query = manager.newQuery(Employee.class, employeesExtent,
"yearsOfEmployement > 5");
// 执行查询Query...
Collection employees = (Collection) query.execute();
// 处理结果...
Iterator iterator = employees.iterator();
while (iterator.hasNext()) {
Employee employee = (Employee) iterator.next();
(...)
}
注意newQuery()方法的第三个参数"yearsOfEmployement > 5"。它标明了返回结果的条件。上面的实现要求Employee 类中必须有一个属性字段叫yearsOfEmployment 。
试例
简介
下面的例子示范了JDO的基础应用。代码示范了如何向数据库添加数据和取回数据。所有的代码使用的都是SolarMetric's Kodo JDO实现。包括创建数据库的机制,使域对象类扩展PersistenceCapable接口的增强和厂商特定的PersistenceManagerFactory实现使用的都是Kodo JDO实现。其他的部分使用的是标准的JDO实现。
域对象
The samples will work with a small set of classes, which represent a Fleet of Vehicle objects. The two specific types of Vehicles defined are Bicycle and MotorVehicle. MotorVehicle objects have an Engine attribute.
例子是用了一个很小的类,这个类描述了一个Vehicle(交通工具) 对象的Fleet(车队)。Vehicle 定义了两个特定的对象Bicycle 和MotorVehicle 。MotorVehicle对象有一个Engine 属性。
域对象源代码 /**
* Fleet.java
*/
package com.ociweb.jdodemo;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
public class Fleet {
private List vehicles = new Vector();
public void addVehicle(Vehicle vehicle) {
vehicles.add(vehicle);
}
public Iterator getVehicles() {
return vehicles.iterator();
}
public String toString() {
StringBuffer buffer = new StringBuffer("Fleet:\n");
Iterator iter = getVehicles();
while (iter.hasNext()) {
buffer.append("\t" + iter.next() + "\n");
}
return buffer.toString();
}
}
/**
* Vehicle.java
*/
package com.ociweb.jdodemo;
public class Vehicle {
private int numberOfWheels;
public Vehicle(int numberOfWheels) {
this.numberOfWheels = numberOfWheels;
}
public int getNumberOfWheels() {
return numberOfWheels;
}
}
/**
* Bicycle.java
*/
package com.ociweb.jdodemo;
public class Bicycle extends Vehicle {
private String model;
public Bicycle(String model) {
super(2);
this.model = model;
}
public String toString() {
return "Bike: Model " + model;
}
}
/**
* MotorVehicle.java
*/
package com.ociweb.jdodemo;
public class MotorVehicle extends Vehicle {
private Engine engine;
public MotorVehicle(int numberOfWheels, Engine engine) {
super(numberOfWheels);
this.engine = engine;
}
public String toString() {
return "MotorVehicle With " + getNumberOfWheels()
+ " Wheels. " + engine;
}
}
/**
* Engine.java
*/
package com.ociweb.jdodemo;
public class Engine {
private int numberOfCylinders;
public Engine(int numberOfCylinders) {
this.numberOfCylinders = numberOfCylinders;
}
public int getNumberOfCylinders() {
return numberOfCylinders;
}
public String toString() {
return numberOfCylinders + " Cylinder Engine.";
}
}
Kodo JDO 规范
Kodo JDO包含自己的类来创建数据库模型和增强类。
模型生成工具被用于揣安数据库模型,这个模型将被用于存储JDO对象实例。工具正式支持的数据库包括:
DB2
InstantDB
SQLServer
MySQL
Oracle
PostgreSQL
其他数据库的JDBC驱动可以通过扩展代码被加入。详细内容参考Kodo JDO的文档。
模型创建工具要依赖于一个package.jdo文件,这个文件被用于定义JDO实例类的一些细节。下面的文件被用于这个例子。可以查看Kodo JDO的文档来获得文件格式和概念的详细信息。
<?xml version="1.0"?>
<jdo>
<package name="com.ociweb.jdodemo">
<class name="Engine"/>
<class name="Vehicle"/>
<class name="Bicycle" persistence-capable-superclass="Vehicle"/>
<class name="MotorVehicle" persistence-capable-superclass="Vehicle"/>
<class name="Fleet">
<field name="vehicles">
<collection element-type="Vehicle"/>
</field>
</class>
</package>
</jdo>
schematool.bat文件被用来运行模型创建工具。.jdo文件必须作为一个参数放到命令行中。
schematool.bat package.jdo
一旦模型被创建,域对象的类文件必须被增强以实现PersistenceCapable 接口。jdoc.bat文件被用于运行类增强器。jdoc.bat批处理文件也需要package.jdo文件作为参数方到命令行中。
jdoc.bat package.jdo
上面演示的模型创建和类增强是Kodo JDO实现特有的,它并不是JDO规范的一部分。其他的厂商也许有他们自己的方法来完成以上部分,具体实现要参考厂商的文档。
向数据库增加数据
由于数据库已经配置完成并且我们的域对象也已经设计好了,代码也通过增强实现了PersistenceCapable接口,现在那些类的可以被实例化并添加到数据库中了。
下面的类将实例化一个Fleet,给他添加几条数据然后把它存储到数据库中。
/**
* SeedDatabase.java
*/
package com.ociweb.jdodemo;
// 厂商实现的PersistenceManagerFactory
import com.solarmetric.kodo.impl.jdbc.JDBCPersistenceManagerFactory;
import javax.jdo.PersistenceManager;
import javax.jdo.Transaction;
public class SeedDatabase {
public static void main(String[] args) {
// 创建一个vehicles的fleet...
Fleet fleet = new Fleet();
fleet.addVehicle(new Bicycle("Schwinn"));
fleet.addVehicle(new Bicycle("Giant"));
fleet.addVehicle(new MotorVehicle(4, new Engine(8)));
fleet.addVehicle(new MotorVehicle(2, new Engine(4)));
fleet.addVehicle(new MotorVehicle(4, new Engine(4)));
// 获得的一个PersistenceManager...
PersistenceManager pm =
new JDBCPersistenceManagerFactory().getPersistenceManager();
// 开始一个transaction...
Transaction transaction = pm.currentTransaction();
transaction.begin();
// 存储 fleet...
pm.makePersistent(fleet);
// 提交transaction...
transaction.commit();
// 关闭 manager...
pm.close();
}
}
从数据库中获得数据
下面的代码从数据库中获得类的所有实例(包括子类的)并且把他们打引导控制台。
/**
* ListAll.java
*/
package com.ociweb.jdodemo;
// 厂商实现的PersistenceManagerFactory
import com.solarmetric.kodo.impl.jdbc.JDBCPersistenceManagerFactory;
import javax.jdo.Extent;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.Query;
import java.util.Collection;
import java.util.Iterator;
public class ListAll {
public static void main(String[] args) {
// 厂商特定的工厂实现...
PersistenceManagerFactory managerFactory =
new JDBCPersistenceManagerFactory();
// 获得一个 manager...
PersistenceManager manager =
managerFactory.getPersistenceManager();
Extent ext = manager.getExtent(Vehicle.class, true);
Query query = manager.newQuery(Vehicle.class, ext, "");
Collection vehicles = (Collection) query.execute();
Iterator iterator = vehicles.iterator();
while (iterator.hasNext()) {
Vehicle vehicle = (Vehicle) iterator.next();
System.out.println("vehicle = " + vehicle);
}
manager.close();
}
}
ListAll 输出内容:
vehicle = Bike: Model Schwinn
vehicle = Bike: Model Giant
vehicle = MotorVehicle With 4 Wheels. 8 Cylinder Engine.
vehicle = MotorVehicle With 2 Wheels. 4 Cylinder Engine.
vehicle = MotorVehicle With 4 Wheels. 4 Cylinder Engine.
限制输出的结果为有四个cylinders的Vehicle 对象。条件参数必须放到newQuery()中。
注意这个例子中使用MotorVechicle类来实例化Vechicle,因为只有MotorVehicle 对象有Engines。
/**
* ListFourCylinderVehicles
*/
package com.ociweb.jdodemo;
// 厂商实现的PersistenceManagerFactory
import com.solarmetric.kodo.impl.jdbc.JDBCPersistenceManagerFactory;
import javax.jdo.Extent;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.Query;
import java.util.Collection;
import java.util.Iterator;
public class ListFourCylinderVehicles {
public static void main(String[] args) {
// 厂商特定的工厂实现...
PersistenceManagerFactory managerFactory =
new JDBCPersistenceManagerFactory();
// 获得 a manager...
PersistenceManager manager =
managerFactory.getPersistenceManager();
Extent ext = manager.getExtent(MotorVehicle.class, true);
// 只返回有4个cylinders的 vehicles...
Query query = manager.newQuery(MotorVehicle.class, ext,
"engine.numberOfCylinders == 4");
Collection vehicles = (Collection) query.execute();
Iterator iterator = vehicles.iterator();
while (iterator.hasNext()) {
Vehicle vehicle = (Vehicle) iterator.next();
System.out.println("vehicle = " + vehicle);
}
manager.close();
}
}
ListFourCylinderVehicles 的输出结果为:
vehicle = MotorVehicle With 2 Wheels. 4 Cylinder Engine.
vehicle = MotorVehicle With 4 Wheels. 4 Cylinder Engine.
结论
JDO提供了一个数据库表示,在面向对象方面它比JDBC有何多好处。具体的对象映射和数据库厂商对于开发人员来讲都是隐藏的(换句话说开发人员根本不用管)。存储和查询的数据库操作非常简单明了。开发人员写的有关数据存储的代码非常少。有理由认为JDO对于Java数据存储是非常引人注目的技术。
参考
JCP - http://www.jcp.org/
JDO - http://access1.sun.com/jdo/
JDO规范- http://www.jcp.org/jsr/detail/12.jsp
JSR - http://www.jcp.org/jsr/overview/index.jsp
JDBC - http://java.sun.com/products/jdbc/
JDO Central - http://www.jdocentral.com/
Poet FastObjects JDO - http://www.fastobjects.com/FO_Products_FastObjectsj1_Body.html
PrismTech OpenFusion JDO - http://www.prismtechnologies.com/English/Products/JDO/index.html
Signsoft IntelliBO JDO - http://www.signsoft.com/en/intellibo/jdo.jsp
SolarMetric Kodo JDO - http://www.solarmetric.com/Software/Kodo_JDO/