原文:http://www.libelis.com/inner-index.jsp?next=jdo.html

JDO是Java对象持久化的新的规范。JDO经SunJava Community Process认定。

一、历史

JDO是对象持久化工作的综合成果,试图提供一个对象持久化的完全版本。JDO同时继承于ODMG(对象数据管理小组,标准化对象数据库的一个独立委员会)和对象关系映射工具提供商。
JSR #000012 approved in July 1999
1999-8组建的专家小组:包括Sun、Apple、BEA、IBM、Oracle、SAP、WebGain等
2000-5 完成公开评论草案
2000-6 在JavaOne上引入
2001-3 最终草案0.93
2001-5 最终草案0.96公布
2001-6 在JavaOne上启动
2001-11 最终草案0.98

二、目标

定义对象级的持久化。
  • 完全支持对象模型,包括引用、集合、接口、继承
  • 完全透明持久化:这使得业务对象完全独立于任何数据库技术,这使用了一个字节码增强机制(a byte-code enhancement mechanism)。
  • 缩短开发周期(不再需要映射)
  • 在开发小组中清晰地划分业务人员和数据库人员。
  • 通用持久性
     JDBC限于RDBMS,JDO潜在地可以处理任何类型的数据源,包括RDBMS,ODBMS,TP监控处理,ASCII无格式文件,xml文件,properties文件,大机上的Cobol数据库,等
     JDO是面向大型信息系统的一个完全的解决方案,在这样的系统中,信息存储于多种异质数据源
  • 涵盖J2EE、J2SE、J2ME的广泛实现
  • 强壮的事务模型
  • 同时支持C/S和多层体系结构


三、体系

JDO 包
PersistentCapable:拥有持久实例的类必须实现这个接口。管理对象生命周期。
PersistenceManager :代表了到数据源的连接。一个应用可以打开一个或多个PersistenceManagers。
PersistenceManagerFactory:允许从数据源中得到一个PersistenceManager的实例,这个工厂也可以作为一个连接池。
Transaction:允许界定事务
Query :允许明确地声明性地使用JDO查询语言从数据源中获取数据。NB:也可以通过引用之间的基本的定位,隐含地、透明地从数据源中获取对象。
InstanceCallback:在数据库操作中(比如before/after read, before/after write,等),定义一些钩子,以做特殊处理(像暂时属性的初始化)。
JDOException:JDO操作中抛出的例外。

JDO也定义了帮助类,对象标识(由应用或数据源管理)
JDO实现可以支持或不支持兼容的PersistenceManager(当PersistenceManager是兼容的时,你可以得到存储于不同数据库的对象引用)。
NB:在JDO的第一个发布版本中,并没有严格地定义锁和锁策略。

JDO对象模型
  • JDO对象模型基本上是Java的对象模型,包括所有的基本类型,引用,集合和事件接口。
  • 除了系统定义类(system-defined classes),支持所有的字段类型(包括简单型、可变和不变的对象类型〔immutable and mutable object types〕、用户定义类、数组、集合、接口)。
  • 支持所有的成员变量修饰符(private, public, protected, static, transient, abstract, final, synchronized, volatile)
  • 除了对象状态依赖于不可访问的或远程对象,即继承于java.net.SocketImpl、本地方法等,所有的用户定义类都可以是PersistentCapable。


JDO对象生命周期
为了能够在数据源中访问、存储对象,应用必须首先得到一个或几个数据源的连接。一个JDO PersistenceManager对象就代表了这样一个连接。它可以通过PersistenceManagerFactory类得到。持久化对象必须是实现了PersistentCapable接口的类的实例。这样的类可能同时拥有持久化的或临时的(transient)实例。

为了使一个实例持久化,编程者必须调用PersistentManager的makePersistent方法。通知JDO对象为持久化或临时的很重要,即使它们可以从JDO的行为中得到它是临时的,比如事务管理和对象标识。对象标识可以由应用管理,或者由数据源代理(这大多是在使用ODBMS实例时,因为概念ObjectID本身就是ODMG模型的一部分)。

JDO支持一种持久化模型,这这种模型中持久性自动传播到引用的对象。这种机制经常称为“延伸持久(persistence by reachability)”或者“传递持久(transitive persistence)”。这意味着一旦一个已经持久化的对象引用了一个临时对象,这个临时对象自动变成持久化的。对于JDBC编程者,这个模型可能很奇怪,但是他们会发现这是大多数情况下编程者希望从持久化框架中得到的支持。

例子:
pmf = (PersistenceManagerFactory) (Class.forName("com.libelis.lido.PersistenceManagerFactory").newInstance()); 
pmf.setConnectionDriverName("versant");
pmf.setConnectionURL(dbName);
pm = pmf.getPersistenceManager();
tx = pm.currentTransaction();
tx.begin();
Provider aProvider = new Provider("LIBeLIS");
pm.makerPersistent(aProvider); // aProvider now persists
Address anAddress = new Address("25 rue Paul Barruel", "France", "Paris");
aProvider.address = anAddress ; // anAddress now persists
tx.commit();
pm.close();

对象或者通过明确的JDO查询结果从内存中得到,或者通过标准的Java对象之间的导航(navigation)得到。

最后这个机制很强大,你可以把持久对象想像为在JVM堆中的一个特殊的部分,我们称之为“客户端缓存(client cache)”。每次当你企图从一个对象导航到另一个时,如果这个对象尚不在内存中,JDO会自动从数据源中获取,并把它放在缓存中。

例子
假设aProvider对象已经装载到内存,但是它的Address对象还没有。当你写下如下代码时:
System.out.println(aProvider.address.city);

Address对象会自动装载。缓存管理自动链接到事务边界(transaction boundaries),这意味着缓存将在事务结束是刷新,所有的实体都标示为无效的。下次对象访问它们的状态时,自动地、透明地从数据源中再次装载。

例子
pmf = (PersistenceManagerFactory) (Class.forName("com.libelis.lido.PersistenceManagerFactory").newInstance()); 
pmf.setConnectionDriverName("versant");
pmf.setConnectionURL(dbName);
pm = pmf.getPersistenceManager();
tx = pm.currentTransaction();
tx.begin();
Provider aProvider = new Provider("LIBeLIS");
pm.makerPersistent(aProvider);
Address anAddress = new Address("25 rue Paul Barruel", "France", "Paris");
aProvider.address = anAddress ;
tx.commit(); // objects are stored into the data source, client cache is discarded, references are invalid
tx.begin();
System.out.println(aProvider); // aProvider is refreshed from the data source
tx.commit();
pm.close();

每次你修改了一个对象,它在JDO缓存中对应的实体将被标示为“脏(dirty)”。

例子
tx.begin(); 
aProvider.address = aNewAdr; // aProvider is marked as dirty
tx.commit(); // aProvider will updated in the data source

在事务的结束,PersistentManager 将把所有标示为“脏”的JDO对象(脏对象)更新到数据源中。

JDO缓存中的每一个对象都有自己的状态(实际上它们都和一个StateManager对象相关联)。并且JDO规范规定了一大堆状态和它们之间的过渡。如果感兴趣,请参照JDO规范了解详细。
NB:这些状态一般是JDO提供商关心的,而不是JDO编程者。
开发周期
为了达到上面提到的完全透明的持久化,JDO定义了一个称为“增强(Enhancement)”的字节码工具机制(byte-code instrumentation mechanism)。它的思想是从业务类中剔除所有的显式的数据库依赖代码。和已存在的或新的数据源的映射通过外部的元数据(metadata)XML文件定义。
JDO增强器读取编译的java文件(.class文件),并且应用元数据文件中定义的持久化规则。例示如下:

增强会添加到元数据文件描述的类的字节码中:
• 实现PersistentCapable接口的声明
• 该接口中声明的方法的字节码必须被实现
• 当修改了一个属性后该对象标示为“脏”的代码
• 需要时从数据源中获取对象的代码
• 根据元数据文件中指定的从数据源中的原始数据(raw data)到Java对象映射的代码
NB:在专家小组中,是否要把增强列入JDO规范曾经激烈争论过。一些专家认为开发者可能会被增强技术吓倒。不熟悉这个技术的开发者会惊讶于字节码增强,这是一个事实。但是增强是这样一种通用、强大的开发技术,可以在很多案例中有效地使用。开始时,每当开发者遇到一个Bug时,可能会责难增强器。我们强烈推荐增强器的“新手”仔细地看一下BCEL网站(由Source Forge开源社区提供)。在那儿,你能看到许多关于字节码增强的有用信息和工具。这种机制的熟手可以在他们的Java接口中成功地使用。所有的熟手都表现出了兴趣。

在两层(Client/Server)结构中整合
在传统的Client/Server体系中,JDO应用需要使用由JDO实现提供的PersistenceManagers把它自己连接到一个或多个数据源。


临时对象可以持有持久对象的引用。持久对象可以持有分布在多个PersistenceManagers中的持久对象的引用。但是这不是一个必须遵从的特性。
大部分数据库相关的代码都从业务对象中移去,但是事务仍需要编程者明确的划分(使用Transaction 的begin、commit、rollback方法)。
管理运行在内存中的Java模型和磁盘上的数据源的物理模型之间的映射是PersistentManager的职责。当使用ODBMS时,这个映射非常直接,但使用RDBMS或更简单的数据源时可能会非常复杂。

四、JDO和J2EE

JDO已经设计为整合入J2EE体系。JDO依靠一个新的Connector规范来管理一个J2EE应用和JDO容器之间的交互。在JDO规范中这个描述为管理环境(managed environments,意思是事务和连接由应用服务器自己管理)。JDO容器和J2EE应用服务器交互,得到数据源的连接(因为应用有它自己的连接池),并根据应用服务器的JTA兼容的事务管理器执行事务。

JDO明确的解决了J2EE体系的一个主要缺陷,就是EJB部件模型将持久性和分布性结合起来。这两个概念是不相关的,但是EJB规范试图简化,迫使应用对持久性和分布性使用相同的粒度级别。这在软件工程的视角并不清晰,并且也不具可扩展性。
另外一点,EJB持久模型(CMP和BMP)过分简化了,只能应付有限的情况,不适合现实的应用需要。感兴趣的读者可以可以很容易地J2EE的门户中(例如the ServerSide)找到大量的EJB持久性和JDO的讨论。
使用JDO,你可以使用一种非常优雅、通用的设计:Session Bean(访问业务处理对象)自己访问业务数据对象。这样做,应用仍然拥有J2EE体系所有的优点(分布式、事务、连接,…),并且持久化由JDO透明、高效地管理。

JDO可以支持在异种数据源中非常复杂的映射机制,然而,EJB/CMP模型仅适用于简单的JDBC模型。
使用JDO你没有业务对象的复杂性的限制(然而EJB/CMP不支持继承)。
使用JDO在你的业务数据对象中完全没有数据库代码(然而使用EJB/BMP 你的业务代码会掺杂入JDBC代码)
业务处理对象提供了处理多个业务数据对象的方法。
业务处理对象通常是非持久化的,它们通常通过混合的JDO查询和导航得到业务数据对象。
从SessionBean中剥离业务处理方法仍然很重要,因为这样你的业务模型可以把众多应用中任何的基础构架应用到J2EE应用中。

资源链接


Sun JDO Specification
JDO JSR page
Sun JDO site
Byte-code enhancement web sites
LIBeLIS LiDO site
Versant enJin

摘自:http://www.javaresearch.org/article/showarticle.jsp?column=108&thread=2406