JDO(Java Data Object)是JCP中较早开发出来并形成规范的JSR12,该规范对数据的持久化存储进行了一系列规范,并已有众多的商业产品和开源项目是基于该规范。作为一种需要引起重视的技术,研究并探讨其企业应用可行性是十分重要的。
前言
在企业级的应用开发中,常需要有良好的持久化技术来支持数据存储。通过良好的规范或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.p