跳转到主要内容

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

当您初次登录到 developerWorks 时,将会为您创建一份概要信息。您在 developerWorks 概要信息中选择公开的信息将公开显示给其他人,但您可以随时修改这些信息的显示状态。您的姓名(除非选择隐藏)和昵称将和您在 developerWorks 发布的内容一同显示。

所有提交的信息确保安全。

  • 关闭 [x]

当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

所有提交的信息确保安全。

  • 关闭 [x]

JDO 技术分析及企业应用研究

辉 阳易, 软件工程师
阳易辉,Sun 认证的 SCJP、SCEA。多年以来,专注于企业业务系统的设计开发工作,最近关注的领域是 MDA、企业规则、JDO 及工作流系统。

简介: JDO(Java Data Object)是 JCP 中较早开发出来并形成规范的 JSR12,该规范对数据的持久化存储进行了一系列规范,并已有众多的商业产品和开源项目是基于该规范。作为一种需要引起重视的技术,研究并探讨其企业应用可行性是十分重要的。

发布日期: 2005 年 10 月 24 日
级别: 中级
访问情况 : 1209 次浏览
评论: 


前言

在企业级的应用开发中,常需要有良好的持久化技术来支持数据存储。通过良好的规范或 API,将企业的领域业务对象进行持久化存储,大多采用 O/R 映射技术来进行模式化的数据转换及自动映射工作。

JDO(Java Data Object)是 JCP 中较早开发出来并形成规范的 JSR12,该规范对数据的持久化存储进行了一系列规范,并已有众多的商业产品和开源项目是基于该规范。作为一种需要引起重视的技术,研究并探讨其企业应用可行性是十分重要的。

以下主要对 JDO(JDO 1.0 规范)的应用开发技术作扼要介绍,通过该文,可以由浅入深、并较为全面地了解 JDO,掌握主要的技术细节及过程,理解其运行机制,并对企业级应用有个总体的把握,这将有助于企业应用软件的技术选型、体系架构及分析设计活动。

该文适合企业应用架构师、及关心数据持久层设计开发人员。


JDO 基本思想及特点

企业信息系统的一个重要问题是解决数据的存储,即持久化。在软件开发过程中,分析员分析领域业务,提取出领域业务模型,并对应设计出数据库中需要进行存储业务数据的数据库表及相应字段。

并根据业务流程,设计业务处理逻辑单元,进行数据的加工、处理及存储、查询等业务。其中一个较为繁烦、枯燥的工作,就是处理大量的数据持久化代码。为了解决数据从业务对象层向数据存储层之间的转换工作,JDO 提供了相应的开发规范及 API,解决了由 Java 对象直接存储为数据库相应表的底层处理过程,有助于设计人员更加专注于面向业务流程、面向业务对象等较高层次的应用。

由于采用 JDO 的映射机制,能降低了业务系统与数据存储系统的耦合,使得业务系统相对于关系数据库或对象型数据库,具有可移植性,同时,由于采用面向对象(而非传统的面向记录)的持久化技术,系统更为轻便、简洁,增强了可维护性。


JDO 应用示例及分析

以下将通过一些示例,由浅及深地讲解 JDO 技术。

临时对象与持久对象

这是一个普通的业务对象的代码。

 package business.model; 
 public class Book { 
  private String isbn; 
  private String name; 
  private Date publishDate; 
  public void setISBN(String isbn){ 
    this.isbn = isbn; 
  } 
  public String getISBN(){ 
    return this.isbn; 
  } 
  public void setName(String name){ 
    this.name = name; 
  } 
  public String getName(){ 
    return this.name; 
  } 
  public void setPublishDate(Date pubDate){ 
    this.publishDate = pubDate; 
  } 
  public Date getPublishDate(){ 
    return this.publishDate; 
  } 
 } 

现在将它作为一个 JDO 中对象保存到数据库中。代码如下:

 Book book = new Book(); 
 book.setISBN(“isbn-1234567”); 
 book.setName(“Java 设计模式”); 
 PersistenceManager manager = persistenceManagerFactory.getPersistenceManager(); 
 manager.currentTransaction().begin(); 
 manager.makePersistence(book); 
 manager.currentTransaction().commit(); 

Book 类的实例 book 对 JDO 的 API 而言,就是一个持久对象。类 Book 是可持久类。那任何一个普通 java 类都是 JDO 的可持久类吗?不是的。只有具备以下的条件,一个对象才可以被 JDO 持久到数据库中。

  • 它所属类应标记为可持久的类,有以下两种方法:

    显式:实现接口,javax.jdo.PersistenceCapable 即可;

    隐式:以 Sun 的 JDO 参考实现为例,Book.java 类的相同路径下还须有 Book.jdo 文件。

     <?xml version=“1.0” encoding = “UTF-8”?> 
     <!DOCTYPE jdo SYSTEM “jdo.dtd”> 
     <jdo> 
      <package name = “business.model”> 
         <class name = “Book”/> 
      </package> 
     </jdo> 
    

    并通过字节码增强工具(本例采用 Sun 的字节码增强工具)处理,

     javac Book.java 
     java com.sun.jdori.enhancer.Main Book.class Book.jdo。
    

    通过上述两种方法,获得的 Book.class 才是一个可持久的类。

    字节码增强的有如下功能:当应用程序通过 set 方法修改某个字段 1 时,由于通过增强过程,在其内部插入了某些代码,JDO 会获得数据状态变化的信息,从而在持久过程中,进行有选择性的处理。

    按照 JDO 规范,增强后的类可以在不同的 JDO 实现上使用,而无需重新编译或增强。

  • 并不是所有 Book 对象都是持久对象,只有当 makePersistence 后,该对象才是持久对象,并会通过 JDO 实现存储到数据库中。 通过 JDO 的供应商扩展标记符(vendor-extension),可详细描述 Book 类的存储特性,如为该可持久类指定数据库表和对应字段。

持久对象查询

JDO 查询主要有以下两种方式。

使用 Extend 查询

Extend 可以查询指定类及子类的持久对象。

 PersistenceManager manager = persistenceManagerFactory.getPersistenceManager(); 
 manager.currentTransaction().begin(); 
 Extend extend = manager.getExtend(Book.class,true);//true 表明同时查询子类
 Iterator it = extend.iterator(); 
 while(it.hasNext()){ 
  Book book = (Book)it.next(); 
  System.out.println(book.getISBN()); 
 } 
 extend.closeAll(); 
 manager.currentTransaction().commit(); 

Extend 查询方法,提供了一种基于类的查询途径,它可以与下面的 Query 构成更为强大的查询。

使用 Query 查询

Query 可以指定过滤条件,是一种常用的查询方式。

下例是查找条件为“书名以‘ Java 设计模式’开头且出版日期小于今天”的书籍。

 String filter = “((String)name).startsWith(\”Java 设计模式 \”) && publishDate < today”; 
 Query query = pm.getQuery(Book.class,filter); 
 query.declareImports(“import java.util.Date”); 
 query.declareParameters(“Date today); 
 Date today = new Date(); 
 results = (Collection)query.execute(today);// 传入参数值 today 
 if (results.isEmpty()){ 
  System.out.println(“No data!”); 
 }else{ 
  Iterator it = results.iterator(); 
  while(it.hasNext()){ 
    Book book = (Book)it.next(); 
    System.out.println(“Book Name:” + book.getName() + “, ISBN:” + book.getISBN()); 
  } 
 } 

注:该条件使用了一个变元‘ today ’,通过“declareParameters”来声明该变量,并在“execute”方法中传入该变量的实例。

这种带参数的查询,很类似于我们以前采用 JDBC 的带 ? 的查询方式。

其中 startsWith(String s) 是 JDO 提供的标准字符方法,类似的方法还有 endsWith(String s)。

JDOQL:上述使用的就是一个 JDOQL 样例,JDOQL 是 JDO 规范一个组成部分。使用 JDOQL 可以使用应用在不同的 JDO 实现上运行。 为了解决 JDOQL 的某些不足,JDO 规范提供了支持特定 JDO 供应商查询语句接口。

查询排序

下例是将查询结果按“出版日期降序、书名升序”进行排序。

 Query query = pm.newQuery(Book.class, filter); 
 String orderStr = “publishDate decending, name ascending”; 
 query.setOrdering(orderStr); 
 results = query.execute(today); 

对象更新

当客户端对业务数据进行了更新后,需要通过业务过程将其更新到持久层中。

这有两个过程,首先根据主键找到该实例,接着更新字段及提交。

如下例,将指定书目编号的书本的出版日期进行更改。

 public void updateBookPublishDate(String isbn, Date newDate){ 
  PersistenceManager pm = null; 
  try{ 
 pm = pmf.getPersistenceManager(); 
 Object obj = pm.newObjectIdInstance(Book.class,isbn); 
 Book book = (Book)pm.getObjectById(obj,true); 
 book.setPublishDate(newDate); 
  }catch(Exception e){ 
 xxxContext.setRollbackOnly(); 
 throw new Exception(e); 
  }finally{ 
    try{ 
 if (pm != null && !pm.isClosed()){ 
    pm.close(); 
 } 
 }catch(Exception ex){ 
  System.out.println(ex); 
  } 
 } 

注,在 PersistenceManager 使用 newObjectIdInstance()方法时,JDO 是如何知道通过书目编号 ISBN 来找到该对象呢?

其实在本可持久类 Book 的 jdo 描述文件中,还需提供如下信息:

 <?xml version=“1.0” encoding = “UTF-8”?> 
 <!DOCTYPE jdo SYSTEM “jdo.dtd”> 
 <jdo> 
  <package name = “business.model”> 
     <class name = “Book” identity-type=“application” objectid-class=“BookKey” > 
          <field name=“isbn” primary-key=“true”/> 
     </class> 
  </package> 
 </jdo> 

其中“identity-type=“application””声明可持久类 Book 采用程序标识方式,即应用程序传入 ID(字段 isbn 为“primary-key”)信息,JDO 实现构造出指定的“objectid-class”的实例(即 newObjectIdInstance 过程),并由 JDO 来检索出指定的持久化对象(即 getObjectById)。

BookKey 类源码如下:

 package businesss.model; 
 public class BookKey implements java.io.Serializable{ 
  	 public String isbn; 
  	 public BookKey(){ 
  	 } 
  	 public BookKey(String oid){ 
    isbn = oid; 
	 } 
	 public String toString(){ 
  		 return isbn; 
	 } 
	 public Boolean equals(Object obj){ 
	  return isbn.equals((BookKey)obj).isbn); 
	 } 
	 public int hashCode(){ 
  		 return isbn.hashCode(); 
	 } 
 } 

符合 JDO 的“objectid-class”类,如“BookKey”,须具备以下条件:

  • 类声明为 public,并实现 java.io.Serializable;
  • 带有一个公有且不带参数的构造方法;
  • 当字段作为主键时,须有公有的,且名称和类型与持久类的字段一致,如:public String isbn;
  • equals 和 hashCode 须使用全部(特指多字段的联合主键)的主键字段值;
  • 类必须有一个构造方法,与 toString 方法的处理过程是逆向过程;即将 toString 的输出值,作为该构造方法的输入值,又可以重新生成该实例(如构造方法“public BookKey(String oid)”)。

综上所述,如果 Book 由两个字段作为主键,如 isbn 和 name,则可能的代码是 pm.newObjectIdInstance(Book.class,isbn+“#”+name),且 BookKey 的构造方法作相应更改,并有两个公有字段“isbn”和“name”。

对象删除

对象删除采用方法 deletePersistence。示例如下:

 pm.currentTransaction().begin(); 
 Object obj = pm.newObjectIdInstance(Book.class,isbn); 
 Book book = (Book)pm.getObjectById(obj,true); 
 pm.deletePersistence(book); 
 pm.currentTransaction().commit(); 

获得 PersistenceManager 实例

上述的所有操作与需要 PersistenceManager 实例,它可以在两种环境方法下获得:非受管环境和受管环境。

非受管环境

非受管环境是多指两层开发模式,应用程序直接获得资源对象,进行业务操作。一般事务管理、安全管理或资源管理都需要应用程序自行维护。

 Properties properties = new Properties(); 
 properties.put(“javax.jdo.PersistenceManagerFactoryClass”, “com.xxx.jdo.xxxPMFClass”); 
 properties.put(“javax.jdo.option.ConnectionURL”, “xxx”); 
 properties.put(“javax.jdo.option.ConnectionUserName”, “xxx”); 
 properties.put(“javax.jdo.option.ConnectionPassword”, “xxx”); 
 PersistenceManagerFactory pmf = JDOHelper.getPersistenceManagerFactory(properties); 
 PersistenceManager pm = pmf.getPersistenceManager(); 

与 JDBC 获取类似。

受管环境

受管环境一般是多层开发模式,尤其是处于 J2EE 应用环境中,程序通过容器获得资源对象,进行业务操作,由于在容器环境下,事务、安全及资源管理都由容器进行统一集中管理,从而简化了代码结构。

以下是 EJB(EntityBean、SessionBean、MessageDrivenBean)中的 setXXXContext 中的代码示例。

private PersistenceManagerFactory pmf; 
public void setXXXContext(XXXContext context){ 
  try{ 
    InitialContext ctx = new InitialContext(); 
    Object obj = ctx.lookup(“java:comp/env/jdo/factory”); 
    pmf = (PersistenceManagerFactory)PortableRemoteObject
        .narrow(o,PersistenceManagerFactory.class); 
  }catch(NamingException e){ 
    throw new Exception(e); 
  } 
} 

PMF 是如何绑定到 J2EE 环境下的 JNDI 上,有兴趣可参考 JCA 相关的技术文档。

事务管理

事务管理及使用,主要有以下三种情形。

使用 JDO 事务的 Bean 管理情形

一般在非 J2EE 环境下,使用该事务管理模式。

 PersistenceManager pm = pmf.getPersistenceManager(); 
 pm.currentTransaction().begin(); 
 //do some business with jdo 
 pm.currentTransaction().commit(); 
 pm.close(); 

使用 JTA 事务的 Bean 管理情形

一般在 J2EE 环境下,采用 Bean 管理的事务情形下,使用以下方式。

该方式可用在 EJB 的事务环境下。

 xxxContext.getUserTransaction().begin(); 
 PersistenceManager pm = pmf.getPersistenceManager(); 
 //do some business with jdo 
 xxxContext.getUserTransaction().commit(); 
 pm.close(); 

使用 JTA 事务的容器管理情形

一般在 J2EE 环境下,采用容器管理的事务情形下,使用如下方式。

如下是某个会话 Bean 的业务方法。

 public void doBusiness(){ 
  PersistenceManager pm ; 
  try{ 
    pm = pmf.getPersistenceManager(); 
    //do some business with jdo 
  }catch(Exception e){ 
    xxxContext.setRollbackOnly(); 
    System.out.println(e); 
  }finally{ 
    try{ 
      if (pm != null && !pm.isClosed()) 
        pm.close(); 
    }catch(Exception ex){ 
      System.out.println(ex); 
    } 
  } 
 } 

综上,可以得出结论,JDO 的操作完全与 JDBC 的操作相差无几。


JDO 高级应用

复杂对象的持久化

在实际的应用中,一个可持久化类要远比 Book 类复杂很多。它可能会引用其它的 Java 类型、类、集合或数组,及可能复杂的继承关系。

当这些对象的状态发生变化时,JDO 是如何感知及跟踪状态变化?

JDO 提供了相应的 API 及规范来实现该功能。

基本类型及引用

可持久化类的字段能被 JDO 实现进行持久化的原则。

  • 原始类型、java.util.Date 等被支持(其它较为复杂或可选特性,详见 JDO 规范);
  • 如果引用是一个可持久类,则 JDO 进行持久化处理;
  • 通过元数据 ( 如 jdo 文件 ) 声明的字段,一般是非可持久化类的引用,JDO 进行持久化;

前面两种情形,当状态发生变化时,JDO 会自动感知,但如果引用是非可持久化类,则需要代码进行显式通知,否则 JDO 不会将变化进行存储。如下例:

 public class Book { 
  ……
	 private Object picture; 
	 public void setPicture(Object pic){ 
  		 picture = pic; 
	 } 
	 public Object getPicture(){ 
  		 Return picture; 
	 } 
 } 

该类字段 picture 需要持久化,但 java.lang.Object 不是一个可持久类,故声明如下:

 <?xml version=“1.0” encoding = “UTF-8”?> 
 <!DOCTYPE jdo SYSTEM “jdo.dtd”> 
 <jdo> 
  <package name = “business.model”> 
     <class name = “Book” > 
          <field name=“picture” persistence-modifier=“persistent”/> 
     </class> 
  </package> 
 </jdo> 

如果其它模块通过 getPicture 获得对象,并在 JDO 不可感知的外部,修改对象,则变化不会存储,较好的办法是修改 setPicture 方法,如下:

 public void setPicture(Object pic){ 
  JDOHelper.makeDirty(this, “picture”); 
  picture = pic; 
 } 

并通过 setPicture 方法来更新数据。

JDO 的“makeDirty”方法,主要负责通知 JDO 实现,可持久化类 Book 某个实例(this)的“picture”字段发生了变化。

集合

可持久类的字段引用为集合时,按照 JDO 规范,强制支持 java.util.HashSet,对 HashMap、HashTable、TreeMap、TreeSet、LinkedList、ArrayList 及 Vector 的支持对 JDO 实现而言是可选的,通过 PersistenceManagerFactory 的 supportedOptions 方法获得实现特性。

数组

如果可持久类的引用是数组类型,当数组单元发生变化时,需要调用“makeDirty”来通知 JDO 实现,该实例的数组引用内容发生了变化。

与引用是非可持久类实例不同,不需要进行 JDO 的元数据声明。

继承

如果使用可持久性,一般继承的子类与父类都为可持久类,以减少系统复杂性,这时需要在子类的元数据中指出其可持久超类,如下:

 <class name=“TechBook” persistence-capable-superclass=“Book”/> 

可为非持久类扩展持久类,或可为持久类扩展非可持久类;这两种情形 JDO 实现都将忽略非可持久类的字段部分,而不保存到数据库。


JDO 的一些不足之处

JDO 对数据的持久化技术相比于成熟的 SQL,还有不足之处,这些不足在某些情况下将可能会影响数据处理部分的设计实现。

以下列举了常用数据访问的必要功能

查询增强

如字符串不支持通配符、大小写比较;

不支持聚合操作,无法实现 MIN、MAX、SUM 和 AVG;

不支持投影操作,JDO 查询返回为对象,而不像 SQL 那样返回字段;

不支持联合、交叉(UNION/INTERSECT);

不支持 JOIN、IN 和 EXISTS;


企业应用探究

由于 JDO 采用面向对象的持久化处理技术,从而为解决企业业务系统的持久化问题提供了一个新技术解决方案。但是先进的未必就最适用。在某些应用场景下,需要结合各种因素,采取灵活的策略。

面向对象与面向记录

现在大多业务系统都采用面向对象分析设计,这就需要每个应用系统都自行实现将对象映射成记录,并存储到数据库中,而有 JDO 这样的面向对象的持久化技术,在某种程度上解放了这种转化设计的不规范性,进而获得相对更优的系统结构;另一方面,一个业务系统的数据持久化,一般都有这样的过程:对象层->记录层->物理层,JDO 无疑使分析设计人员从记录层的苦海中解脱出来,从而更加专注于对象层,这对开发人员无疑是一个令人欢欣鼓舞的技术。

JDO 并不能完全代替 JDBC。

根据经典的“8-2 原理”,如果用 JDO 来解决 80% 的问题,余下的 20% 由 JDBC 来实现,这种相互补充,各取所长的策略,是一个很有效的办法。

这样一方面可以获得较好的结构及提升开发质量,另一方面解决了 JDO 的某些技术不足,并可根据以后的技术变化,再做适当转化。

性能问题

JDO 与 JDBC 究竟谁的性能更优,目前还没有一个权威、且令人满意的答案。但对于一些 JDO 实现而言,通过采用缓存机制,使得性能有了较大提高。

跨数据库

使用 JDO 的系统能更好地进行数据库移植,甚至可以在对象数据库上运行;当然 JDBC 处理层如果完全遵循 SQL-92 标准,也同样具有很好的跨数据库能力。


参考资料

  • 标注 1:由于 JDO 将类属性映射为数据库中的字段,为了直观起见,以下多指类的属性。

  • 本文参考侯普秀、曹振新译《 JDO 核心技术》,ISBN7-302-09945-6/TP ﹒ 6835

关于作者

阳易辉,Sun 认证的 SCJP、SCEA。多年以来,专注于企业业务系统的设计开发工作,最近关注的领域是 MDA、企业规则、JDO 及工作流系统。

关于报告滥用的帮助

报告滥用

谢谢! 此内容已经标识给管理员注意。


关于报告滥用的帮助

报告滥用

报告滥用提交失败。 请稍后重试。


developerWorks:登录


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 使用条款

 


当您初次登录到 developerWorks 时,将会为您创建一份概要信息。您在 developerWorks 概要信息中选择公开的信息将公开显示给其他人,但您可以随时修改这些信息的显示状态。您的姓名(除非选择隐藏)和昵称将和您在 developerWorks 发布的内容一同显示。

请选择您的昵称:

当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

(长度在 3 至 31 个字符之间)


单击提交则表示您同意developerWorks 的条款和条件。 使用条款.

 


为本文评分

评论

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Java technology
ArticleID=97803
ArticleTitle=JDO 技术分析及企业应用研究
publish-date=10242005
author1-email=
author1-email-cc=

标签

Help
使用 搜索 文本框在 My developerWorks 中查找包含该标签的所有内容。

使用 滑动条 调节标签的数量。

热门标签 显示了特定专区最受欢迎的标签(例如 Java technology,Linux,WebSphere)。

我的标签 显示了特定专区您标记的标签(例如 Java technology,Linux,WebSphere)。

使用搜索文本框在 My developerWorks 中查找包含该标签的所有内容。热门标签 显示了特定专区最受欢迎的标签(例如 Java technology,Linux,WebSphere)。我的标签 显示了特定专区您标记的标签(例如 Java technology,Linux,WebSphere)。