JDO有一个充满活力的社区。所以如果你正在为你的POJO寻找持久化 解决方案,JDO是一个通过JCP项目开发的标准。JDO为POJO持久化提供了一个丰富和具有全部特征的JSR规范,并且很多开发商正在提供竞争性的JDO实现。 EJB3.0,正由Sun微系统和来自于不同的持久化开发商的专家创建,将是未来持久化模型的选择。
持久化 Java技术对象到任何关系型 数据库都是一项富有挑战性的任务,因为它必须潜心于序列化层次化结构的 java对象到基于表结构的数据库,反之亦然。这个挑战是独一无二的,因为将 java技术对象映射到数据库的列和记录的方式,必须要在速度和效率上同时达到最优化。 java数据对象(JDO)规范,JSR-12,为使用标准的方式来透明地持久化简单 java技术对象和访问数据库定义了API。使用JDO, java应用程序开发者可以在不使用任何数据库特定代码的情况下编写代码访问底层的数据存储。JDO被设计成在一个企业架构的多重层次中工作,包括 java平台、标准版( java SE,以前被称为 J2SE)、 Web层和 应用服务器。
本文提供了JDO的指南,它包含了JDO的基本要素,并且提供了使用JDO持久化 java技术对象的示例代码。
引言
持久化,在程序退出后信息可以长期保存,在 java语言中可以使用不同的选择完成。最简单的是文件I/O,但是这种方式不适合在企业级存储中使用(原因很明显)。其它的选择包括以下几种:
·序列化(Serialization):API简单,不支持查询、多用户共享、事务处理和局部数据读取或更新。另外,它不适合大容量数据存储。
· JDBC软件:具有完全访问的SQL数据的功能。JDBC要求开发者必须显式地管理字段值以及把它们映射到关系数据库的表。在这种情况下,开发者必须熟悉另外一种 开发语言(例如,查询语言SQL)。
· EJB架构容器管理持久性(CMP):作为 java企业级平台( java EE,以前被称为 J2EE)组件模型的一部分,CMP为EJB容器提供了一种简便的对象持久化服务。然而,它不是一种对于 java平台的普通持久化工具。
一种可行的选择是JDO API,它提供了一种标准的方式,使用 java技术完成对象的持久化,通过结合 XML元数据和增强的字节码简化了开发复杂性和降低开发成本。
JDO架构
高级的JDO API为开发者透明地存储数据提供了接口,开发者不必为每一种新的持久化数据存储而学习一种新的数据库访问语言(例如SQL)。JDO也可以使用低级的API(例如JDBC)来存储数据。它使开发者可以编写 java代码透明地访问底层的数据存储,而不需要使用数据库特定代码。JDO在JCP的项目中作为一个JSR开发:JDO1.0,从2002年开始存在,是JSR-12;JDO2.0,2005年初被核准并且正在开发中,是JSR-243。
JDO2.0是一个适合于简单 java对象(POJOs)持久化特点的JSR规范,很多厂家都在提供竞争性的实现。另外,很多厂家很可能在同一个的产品中实现JDO2.0和EJB3.0。这就允许你同时使用两种API,为你提供了逐渐迁移到EJB3.0 POJO的简单方法,将会变为持久化模型的选择。
JDO架构(如图1所示)的两个主要目的就是,为 java应用程序开发者提供透明地以 java技术为核心的访问持久化信息的方式,使数据存储的可插入式实现集成到应用 服务器中。
JDO实例的生命周期
JDO管理一个对象从创建到删除的生命周期。在它的生命周期,JDO实例不断地转换它的状态,直到最后被 Java虚拟机(JVM)作为垃圾回收。状态的转换使用PersistenceManager类的方法完成,包括TransactionManager——例如makePersistent()、makeTransient()、deletePersistent()——和提交或者回滚更改。
表1显示JDO规范定义的10种状态。前面的七种是必须的,后面的三种是可选的。如果一个实现不支持某些操作,那么就不会获得三种可选的状态。
表1 JDO生命周期
状态 | 描述 |
Transient | 任何使用开发者定义的构造函数创建的对象,都不包括持久化环境。一个瞬时实例没有JDO身份。 |
Persistent-new | 被应用程序组件请求的任何对象都变为持久的,通过使用PersistenceManager类的makePersistent()。这样的一个对象将会拥有一个分配的JDO身份。 |
Persistent-dirty | 在当前事务中被改变的持久对象。 |
Hollow | 代表在数据存储中特定数据的持久对象,但是在它的实例中没有包含值。 |
Persistent-clean | 代表在数据存储中的特定事务数据的持久对象,并且它们的数据在当前事务处理中还没有被改变。 |
Persistent-deleted | 代表在数据存储中的特定数据的持久对象,并且在当前事务处理中已经被删除。 |
Persistent-new-deleted | 在同一个事务处理中最近被持久化和删除的持久对象。 |
Persistent-nontransactional | 代表数据存储中的数据的持久对象,当前它们的值已经被装载,但是还没有事务处理一致。 |
Transient-client | 代表一个瞬时事务处理实例的持久对象,它们的数据在当前事务中还没有被改变。 |
Transient-dirty | 代表一个瞬时事务处理实例的持久对象,它们的数据在当前事务中已经被改变。 |
图2显示了JDO实例各状态之间的转换。
![]() 图2 JDO实例的状态转换 |
本文稍后的代码片断,将示范如何执行我们刚刚讨论的操作。
JDO参考实现
JDO参考实现,来自于Sun微系统,已经可用,一同发行的还有一种被称为fstore的基于文件的存储机制。Sun已经把JDO捐献给开源社区。JDO1.0和JDO2.0将会作为Apache JDO项目的一部分进行开发。但是由于时间的限制,JDO2.0的参考实现并不是作为Apache项目建立的,而是作为一个JPOX 发行。一些商业实现也是可用的。
JDO编程模型
JDO定义了两种类型的接口:JDO API(在 javax.jdo包中)和JDO服务提供者接口(SPI)(在 javax.jdo.spi包中)。JDO API面向应用程序开发者,而JDO SPI面向容器提供者,和JDO卖主。
一个应用程序包含两个主要的接口:
·PersistenceManagerFactory代表了应用程序开发者用来获得PersistenceManager实例的访问点。这个接口的实例可以被配置和序列化以备后来使用。然而,需要注意的是,一旦第一个PersistenceManager实例从PersistenceManagerFactory中被获得,这个工厂就不再是可配置。你可以使用下面的代码来获得PersistenceManagerFactory。
// 为JDO实现和数据存储设置一些属性 Properties props = new Properties(); props.put(...); // 得到一个PersistenceManagerFactory PersistenceManagerFactory pmf = JDOHelper.getPersistenceManagerFactory (props); |
·PersistenceManager是JDO-aware应用部分的主要接口。它提供了方法来持久化一个对象,也可以重新得到持久对象和将它们从持久存储中移除。可以使用下面的方法获得PersistenceManager。
PersistenceManager pm = pmf.getPersistenceManager (); |
一旦获得了PersistenceManager对象后,应用程序就可以一些任务,例如:持久化一个对象、从持久数据中获得一个对象、从持久数据中删除一个对象、更新一个对象等等。
接下来的代码片断示范了如何持久化一个对象,它更新一个对象的状态从Transient到Hollow。
Employee emp = new Employee("Sarah Jones", 23, 37000.00); Transaction tx; try { tx = pm.currentTransaction(); tx.begin(); pm.makePersistent(emp); tx.commit(); } catch (Exception e) { if(tx.isActive()) { tx.rollback(); } } |
从持久数据中获得一个对象同样简单,你可以使用Extent(一个信息的持有者)或者Query(提供了更精确的过滤)。下面是一个使用Extent的例子:
try { tx = pm.currentTransaction(); tx.begin(); Extend ex = pm.getExtent(Employee.class, true); Iterator i = ex.iterator(); while(i.hasNext()) { Employee obj = (Employee) i.next(); } tx.commit(); } catch (Exception e) { if(tx.isActive()) { tx.rollback(); } } |
最后,从持久数据中删除一个对象也可以简单完成,首先获得一个从持久数据中获得一个对象,然后调用deletePersistent(obj)方法。
查询对象
JDO规范要求开发商必须提供使用JDOQL的查询能力,JDOQL是一种面向围绕被持久化对象的查询语言。PersistenceManager类定义了构造Query实现类的实例的方法。一个查询过滤器可以被指定为一个布尔表达式,就像SQL的布尔操作符。
生命周期开发:在你的应用程序中使用JDO
可以通过以下六个步骤建立一个JDO应用:
1. 设计你的范围内的将会正常使用的类。对一个要求持久化的类的唯一要求就是它要有一个默认构造函数,访问权限可能是private。
2. 使用元数据定义持久化定义:在这个步骤中,你编写元数据,指定那些类和字段应该被持久化等等。这个文件可以包含对于一个类或一个或者多个包含持久类的包的持久化信息。一个类的元数据文件的名称是这个类的名字加上“.jdo”后缀,注意,这个文件必须放在和.class文件相同的目录中。对于整个包的元数据文件的必须包含在一个称作package.jdo的文件中。元数据文件可以使用XDoclet或手动开发。下面是一个简单的对于两个类的元数据文件:
<?XML version="1.0" encoding="UTF-8"?> <!DOCTYPE jdo SYSTEM "jdo.dtd"> <jdo> <package name="com.xyz.hr"> <class name="Employee" identity-type="application" objectidclass="EmployeeKey"> <field name="name" primary-key="true"> <extension vendor-name="sunw" key="index" value="btree"/> </field> <field name="salary" default-fetch-group="true"/> <field name="dept"> <extension vendor-name="sunw" key="inverse" value="emps"/> </field> <field name="boss"/> </class> <class name="Department" identity-type="application" objectidclass="DepartmentKey">
|
3. 编译这些类,并且使用JDO加强器来加强它们。任何persistence-capable类的实例在被JDO持久化引擎管理之前必须被加强。JDO字节码加强器通过对类定义特定的改变来装换这个类,使得任何持久实例可以和数据存储中的数据描述保持同步。和参考实现一起发行的JDO加强器,能够从Sun微系统得到,可以使用如下的方式运行:
prompt> java -classpath %JDO-HOME%/lib/jdo.jar;%JDO-HOME%/lib/jdori.jar; %JDO-HOME%/jdori-enhancer.jar com.sun.jdori.enhancer.Main -d /enhanced -s . -f path/tp/package.jdo path/to/theclasses.class |
注意:对JDO加强器最重要的参数是一个.jdo文件的名字和.class文件的名字。另外,
·-d选项指定输出文件的目标文件夹;
·-s选项指定jdo和class文件的源文件夹;
·-f选项强制重写输出文件。
如果忽略这个步骤,那么当你运行应用程序和持久化一个对象时将会抛出ClassNotPersistenceCapableException异常。
4. 为被持久化的类建立 数据库表。如果你已经有了一个数据库方案,那么这一步是可选的。基本上,你必须建立表、索引和在JDO元数据文件中为类定义的外键。有些JDO实现包含一个方案工具,可以根据JDO元数据文件产生所有的这些东西。
5. 编写代码来持久化你的对象。在这个步骤中,你要指定那些类在什么时间被实际持久化。正如前面提到的,最初的步骤是获得一个PersistenceManager的使用权。
6. 运行你的应用程序。使用 java命令,并且包含必要的.jar文件在你的classpath中。
结束
JDO提供了基于接口的数据存储定义和事务处理,以及持久存储的数据到本地 java技术对象的转换和选择。它致力于三种情况的需求:
(1)一种持久保存 java技术对象到事务性数据存储的标准。
(2)一种对待关系性数据库数据像 java技术对象一样的标准方式。
(3)一种联合那些对象定义事务性语义的标准方式。
JDO API,仅仅是由一些接口组成,非常便于学习和使用,但是更重要的是它为对象持久化定义了一种标准。今后将会有很多JDO实现供选择,其中有些将是免费的。JDO允许你通过使用POJO来获得它的简便性,并非使用私有的API。