JDO(Java Data Object)是JCP中較早開發(fā)出來并形成規(guī)范的JSR12,該規(guī)范對(duì)數(shù)據(jù)的持久化存儲(chǔ)進(jìn)行了一系列規(guī)范,并已有眾多的商業(yè)產(chǎn)品和開源項(xiàng)目是基于該規(guī)范。作為一種需要引起重視的技術(shù),研究并探討其企業(yè)應(yīng)用可行性是十分重要的。
前言
在企業(yè)級(jí)的應(yīng)用開發(fā)中,常需要有良好的持久化技術(shù)來支持?jǐn)?shù)據(jù)存儲(chǔ)。通過良好的規(guī)范或API,將企業(yè)的領(lǐng)域業(yè)務(wù)對(duì)象進(jìn)行持久化存儲(chǔ),大多采用O/R映射技術(shù)來進(jìn)行模式化的數(shù)據(jù)轉(zhuǎn)換及自動(dòng)映射工作。
JDO(Java Data Object)是JCP中較早開發(fā)出來并形成規(guī)范的JSR12,該規(guī)范對(duì)數(shù)據(jù)的持久化存儲(chǔ)進(jìn)行了一系列規(guī)范,并已有眾多的商業(yè)產(chǎn)品和開源項(xiàng)目是基于該規(guī)范。作為一種需要引起重視的技術(shù),研究并探討其企業(yè)應(yīng)用可行性是十分重要的。
以下主要對(duì)JDO(JDO 1.0規(guī)范)的應(yīng)用開發(fā)技術(shù)作扼要介紹,通過該文,可以由淺入深、并較為全面地了解JDO,掌握主要的技術(shù)細(xì)節(jié)及過程,理解其運(yùn)行機(jī)制,并對(duì)企業(yè)級(jí)應(yīng)用有個(gè)總體的把握,這將有助于企業(yè)應(yīng)用軟件的技術(shù)選型、體系架構(gòu)及分析設(shè)計(jì)活動(dòng)。
該文適合企業(yè)應(yīng)用架構(gòu)師、及關(guān)心數(shù)據(jù)持久層設(shè)計(jì)開發(fā)人員。
JDO基本思想及特點(diǎn)
企業(yè)信息系統(tǒng)的一個(gè)重要問題是解決數(shù)據(jù)的存儲(chǔ),即持久化。在軟件開發(fā)過程中,分析員分析領(lǐng)域業(yè)務(wù),提取出領(lǐng)域業(yè)務(wù)模型,并對(duì)應(yīng)設(shè)計(jì)出數(shù)據(jù)庫中需要進(jìn)行存儲(chǔ)業(yè)務(wù)數(shù)據(jù)的數(shù)據(jù)庫表及相應(yīng)字段。
并根據(jù)業(yè)務(wù)流程,設(shè)計(jì)業(yè)務(wù)處理邏輯單元,進(jìn)行數(shù)據(jù)的加工、處理及存儲(chǔ)、查詢等業(yè)務(wù)。其中一個(gè)較為繁煩、枯燥的工作,就是處理大量的數(shù)據(jù)持久化代碼。
為了解決數(shù)據(jù)從業(yè)務(wù)對(duì)象層向數(shù)據(jù)存儲(chǔ)層之間的轉(zhuǎn)換工作,JDO提供了相應(yīng)的開發(fā)規(guī)范及API,解決了由Java對(duì)象直接存儲(chǔ)為數(shù)據(jù)庫相應(yīng)表的底層處理過程,有助于設(shè)計(jì)人員更加專注于面向業(yè)務(wù)流程、面向業(yè)務(wù)對(duì)象等較高層次的應(yīng)用。
由于采用JDO的映射機(jī)制,能降低了業(yè)務(wù)系統(tǒng)與數(shù)據(jù)存儲(chǔ)系統(tǒng)的耦合,使得業(yè)務(wù)系統(tǒng)相對(duì)于關(guān)系數(shù)據(jù)庫或?qū)ο笮蛿?shù)據(jù)庫,具有可移植性,同時(shí),由于采用面向?qū)ο螅ǘ莻鹘y(tǒng)的面向記錄)的持久化技術(shù),系統(tǒng)更為輕便、簡(jiǎn)潔,增強(qiáng)了可維護(hù)性。
JDO應(yīng)用示例及分析
以下將通過一些示例,由淺及深地講解JDO技術(shù)。
臨時(shí)對(duì)象與持久對(duì)象
這是一個(gè)普通的業(yè)務(wù)對(duì)象的代碼。
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;
}
} |
現(xiàn)在將它作為一個(gè)JDO中對(duì)象保存到數(shù)據(jù)庫中。代碼如下:
Book book = new Book();
book.setISBN(“isbn-1234567”);
book.setName(“Java設(shè)計(jì)模式”);
PersistenceManager
manager = persistenceManagerFactory.
getPersistenceManager();
manager.currentTransaction().begin();
manager.makePersistence(book);
manager.currentTransaction().commit(); |
Book類的實(shí)例book對(duì)JDO的API而言,就是一個(gè)持久對(duì)象。類Book是可持久類。那任何一個(gè)普通java類都是JDO的可持久類嗎?不是的。只有具備以下的條件,一個(gè)對(duì)象才可以被JDO持久到數(shù)據(jù)庫中。
它所屬類應(yīng)標(biāo)記為可持久的類,有以下兩種方法:
顯式:實(shí)現(xiàn)接口,javax.jdo.PersistenceCapable即可;
隱式:以Sun的JDO參考實(shí)現(xiàn)為例,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> |
并通過字節(jié)碼增強(qiáng)工具(本例采用Sun的字節(jié)碼增強(qiáng)工具)處理:
javac Book.java
java com.sun.jdori.enhancer.Main
Book.class Book.jdo |
通過上述兩種方法,獲得的Book.class才是一個(gè)可持久的類。
字節(jié)碼增強(qiáng)的有如下功能:當(dāng)應(yīng)用程序通過set方法修改某個(gè)字段1時(shí),由于通過增強(qiáng)過程,在其內(nèi)部插入了某些代碼,JDO會(huì)獲得數(shù)據(jù)狀態(tài)變化的信息,從而在持久過程中,進(jìn)行有選擇性的處理。
按照J(rèn)DO規(guī)范,增強(qiáng)后的類可以在不同的JDO實(shí)現(xiàn)上使用,而無需重新編譯或增強(qiáng)。
并不是所有Book對(duì)象都是持久對(duì)象,只有當(dāng)makePersistence后,該對(duì)象才是持久對(duì)象,并會(huì)通過JDO實(shí)現(xiàn)存儲(chǔ)到數(shù)據(jù)庫中。通過JDO的供應(yīng)商擴(kuò)展標(biāo)記符(vendor-extension),可詳細(xì)描述Book類的存儲(chǔ)特性,如為該可持久類指定數(shù)據(jù)庫表和對(duì)應(yīng)字段。
持久對(duì)象查詢
JDO查詢主要有以下兩種方式。
使用Extend查詢
Extend可以查詢指定類及子類的持久對(duì)象。
PersistenceManager
manager = persistenceManagerFactory.
getPersistenceManager();
manager.currentTransaction().begin();
Extend extend =
manager.getExtend(Book.class,true);
//true表明同時(shí)查詢子類
Iterator it = extend.iterator();
while(it.hasNext())
{
Book book = (Book)it.next();
System.out.println(book.getISBN());
}
extend.closeAll();
manager.currentTransaction().commit(); |
Extend查詢方法,提供了一種基于類的查詢途徑,它可以與下面的Query構(gòu)成更為強(qiáng)大的查詢。
使用Query查詢
Query可以指定過濾條件,是一種常用的查詢方式。
下例是查找條件為“書名以‘Java設(shè)計(jì)模式’開頭且出版日期小于今天”的書籍。
String filter =
“((String)name).startsWith(\”Java設(shè)計(jì)模式\”)
&& 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);
//傳入?yún)?shù)值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());
}
} |
注:該條件使用了一個(gè)變?cè)畉oday’,通過“declareParameters”來聲明該變量,并在“execute”方法中傳入該變量的實(shí)例。這種帶參數(shù)的查詢,很類似于我們以前采用JDBC的帶?的查詢方式。
其中startsWith(String s)是JDO提供的標(biāo)準(zhǔn)字符方法,類似的方法還有endsWith(String s)。
JDOQL:上述使用的就是一個(gè)JDOQL樣例,JDOQL是JDO規(guī)范一個(gè)組成部分。使用JDOQL可以使用應(yīng)用在不同的JDO實(shí)現(xiàn)上運(yùn)行。為了解決JDOQL的某些不足,JDO規(guī)范提供了支持特定JDO供應(yīng)商查詢語句接口。
查詢排序
下例是將查詢結(jié)果按“出版日期降序、書名升序”進(jìn)行排序。
Query query =
pm.newQuery(Book.class, filter);
String orderStr =
“publishDate decending, name ascending”;
query.setOrdering(orderStr);
results = query.execute(today); |
對(duì)象更新
當(dāng)客戶端對(duì)業(yè)務(wù)數(shù)據(jù)進(jìn)行了更新后,需要通過業(yè)務(wù)過程將其更新到持久層中。這有兩個(gè)過程,首先根據(jù)主鍵找到該實(shí)例,接著更新字段及提交。如下例,將指定書目編號(hào)的書本的出版日期進(jìn)行更改。
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()方法時(shí),JDO是如何知道通過書目編號(hào)ISBN來找到該對(duì)象呢?其實(shí)在本可持久類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采用程序標(biāo)識(shí)方式,即應(yīng)用程序傳入ID(字段isbn為“primary-key”)信息,JDO實(shí)現(xiàn)構(gòu)造出指定的“objectid-class”的實(shí)例(即newObjectIdInstance過程),并由JDO來檢索出指定的持久化對(duì)象(即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,并實(shí)現(xiàn) java.io.Serializable;
帶有一個(gè)公有且不帶參數(shù)的構(gòu)造方法;
當(dāng)字段作為主鍵時(shí),須有公有的,且名稱和類型與持久類的字段一致,如:public String isbn;
equals 和 hashCode 須使用全部(特指多字段的聯(lián)合主鍵)的主鍵字段值;
類必須有一個(gè)構(gòu)造方法,與 toString 方法的處理過程是逆向過程;即將 toString 的輸出值,作為該構(gòu)造方法的輸入值,又可以重新生成該實(shí)例(如構(gòu)造方法“public BookKey(String oid)”)。
綜上所述,如果Book由兩個(gè)字段作為主鍵,如isbn和name,則可能的代碼是pm.newObjectIdInstance(Book.class,isbn+“#”+name),且BookKey的構(gòu)造方法作相應(yīng)更改,并有兩個(gè)公有字段“isbn”和“name”。
對(duì)象刪除
對(duì)象刪除采用方法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實(shí)例
上述的所有操作與需要PersistenceManager實(shí)例,它可以在兩種環(huán)境方法下獲得:非受管環(huán)境和受管環(huán)境。
非受管環(huán)境
非受管環(huán)境是多指兩層開發(fā)模式,應(yīng)用程序直接獲得資源對(duì)象,進(jìn)行業(yè)務(wù)操作。一般事務(wù)管理、安全管理或資源管理都需要應(yīng)用程序自行維護(hù)。
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.getPersistence
ManagerFactory(properties);
PersistenceManager pm =
pmf.getPersistenceManager(); |
與JDBC獲取類似。
受管環(huán)境
受管環(huán)境一般是多層開發(fā)模式,尤其是處于J2EE應(yīng)用環(huán)境中,程序通過容器獲得資源對(duì)象,進(jìn)行業(yè)務(wù)操作,由于在容器環(huán)境下,事務(wù)、安全及資源管理都由容器進(jìn)行統(tǒng)一集中管理,從而簡(jiǎn)化了代碼結(jié)構(gòu)。
以下是EJB(EntityBean、SessionBean、MessageDrivenBean)中的setXXXContext中的代碼示例。
private PersistenceManagerFactory pmf;
public void setXXXContext
(XXXContext context)
{
try{
InitialContext ctx =
new InitialContext();
Object obj = ctx.lookup
(“java:compenvjdofactory”);
pmf = (PersistenceManagerFactory)
PortableRemoteObject.narrow
(o,PersistenceManagerFactory.class);
}catch(NamingException e)
{
throw new Exception(e);
}
} |
PMF是如何綁定到J2EE環(huán)境下的JNDI上,有興趣可參考JCA相關(guān)的技術(shù)文檔。
事務(wù)管理
事務(wù)管理及使用,主要有以下三種情形。
使用JDO事務(wù)的Bean管理情形
一般在非J2EE環(huán)境下,使用該事務(wù)管理模式。
PersistenceManager pm =
pmf.getPersistenceManager();
pm.currentTransaction().begin();
//do some business with jdo
pm.currentTransaction().commit();
pm.close(); |
使用JTA事務(wù)的Bean管理情形
一般在J2EE環(huán)境下,采用Bean管理的事務(wù)情形下,使用以下方式。
該方式可用在EJB的事務(wù)環(huán)境下。
xxxContext.getUser
Transaction().begin();
PersistenceManager pm =
pmf.getPersistenceManager();
//do some business with jdo
xxxContext.getUserTransaction().commit();
pm.close(); |
使用JTA事務(wù)的容器管理情形
一般在J2EE環(huán)境下,采用容器管理的事務(wù)情形下,使用如下方式。
如下是某個(gè)會(huì)話Bean的業(yè)務(wù)方法。
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);
}
}
} |
綜上,可以得出結(jié)論,JDO的操作完全與JDBC的操作相差無幾。
JDO高級(jí)應(yīng)用
復(fù)雜對(duì)象的持久化
在實(shí)際的應(yīng)用中,一個(gè)可持久化類要遠(yuǎn)比Book類復(fù)雜很多。它可能會(huì)引用其它的Java類型、類、集合或數(shù)組,及可能復(fù)雜的繼承關(guān)系。當(dāng)這些對(duì)象的狀態(tài)發(fā)生變化時(shí),JDO是如何感知及跟蹤狀態(tài)變化?
JDO提供了相應(yīng)的API及規(guī)范來實(shí)現(xiàn)該功能。
基本類型及引用
可持久化類的字段能被JDO實(shí)現(xiàn)進(jìn)行持久化的原則。原始類型、java.util.Date等被支持(其它較為復(fù)雜或可選特性,詳見JDO規(guī)范);如果引用是一個(gè)可持久類,則JDO進(jìn)行持久化處理;通過元數(shù)據(jù)(如jdo文件)聲明的字段,一般是非可持久化類的引用,JDO進(jìn)行持久化;
前面兩種情形,當(dāng)狀態(tài)發(fā)生變化時(shí),JDO會(huì)自動(dòng)感知,但如果引用是非可持久化類,則需要代碼進(jìn)行顯式通知,否則JDO不會(huì)將變化進(jìn)行存儲(chǔ)。如下例:
public class Book
{
……
private Object picture;
public void setPicture(Object pic)
{
picture = pic;
}
public Object getPicture()
{
Return picture;
}
} |
該類字段picture需要持久化,但java.lang.Object不是一個(gè)可持久類,故聲明如下:
<?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獲得對(duì)象,并在JDO不可感知的外部,修改對(duì)象,則變化不會(huì)存儲(chǔ),較好的辦法是修改setPicture方法,如下:
public void setPicture(Object pic)
{
JDOHelper.makeDirty(this, “picture”);
picture = pic;
} |
并通過setPicture方法來更新數(shù)據(jù)。JDO的“makeDirty”方法,主要負(fù)責(zé)通知JDO實(shí)現(xiàn),可持久化類Book某個(gè)實(shí)例(this)的“picture”字段發(fā)生了變化。
集合
可持久類的字段引用為集合時(shí),按照J(rèn)DO規(guī)范,強(qiáng)制支持java.util.HashSet,對(duì)HashMap、HashTable、TreeMap、TreeSet、LinkedList、ArrayList及Vector的支持對(duì)JDO實(shí)現(xiàn)而言是可選的,通過PersistenceManagerFactory的supportedOptions方法獲得實(shí)現(xiàn)特性。
數(shù)組
如果可持久類的引用是數(shù)組類型,當(dāng)數(shù)組單元發(fā)生變化時(shí),需要調(diào)用“makeDirty”來通知JDO實(shí)現(xiàn),該實(shí)例的數(shù)組引用內(nèi)容發(fā)生了變化。與引用是非可持久類實(shí)例不同,不需要進(jìn)行JDO的元數(shù)據(jù)聲明。
繼承
如果使用可持久性,一般繼承的子類與父類都為可持久類,以減少系統(tǒng)復(fù)雜性,這時(shí)需要在子類的元數(shù)據(jù)中指出其可持久超類,如下:
<
class name=“TechBook”
persistence-capable-superclass=“Book”/> |
可為非持久類擴(kuò)展持久類,或可為持久類擴(kuò)展非可持久類;這兩種情形JDO實(shí)現(xiàn)都將忽略非
可持久類的字段部分,而不保存到數(shù)據(jù)庫。
JDO的一些不足之處
JDO對(duì)數(shù)據(jù)的持久化技術(shù)相比于成熟的SQL,還有不足之處,這些不足在某些情況下將可能會(huì)影響數(shù)據(jù)處理部分的設(shè)計(jì)實(shí)現(xiàn)。以下列舉了常用數(shù)據(jù)訪問的必要功能
查詢?cè)鰪?qiáng)
如字符串不支持通配符、大小寫比較;
不支持聚合操作,無法實(shí)現(xiàn)MIN、MAX、SUM和AVG;
不支持投影操作,JDO查詢返回為對(duì)象,而不像SQL那樣返回字段;
不支持聯(lián)合、交叉(UNION/INTERSECT);
不支持JOIN、IN和EXISTS;
企業(yè)應(yīng)用探究
由于JDO采用面向?qū)ο蟮某志没幚砑夹g(shù),從而為解決企業(yè)業(yè)務(wù)系統(tǒng)的持久化問題提供了一個(gè)新技術(shù)解決方案。但是先進(jìn)的未必就最適用。在某些應(yīng)用場(chǎng)景下,需要結(jié)合各種因素,采取靈活的策略。
面向?qū)ο笈c面向記錄
現(xiàn)在大多業(yè)務(wù)系統(tǒng)都采用面向?qū)ο蠓治鲈O(shè)計(jì),這就需要每個(gè)應(yīng)用系統(tǒng)都自行實(shí)現(xiàn)將對(duì)象映射成記錄,并存儲(chǔ)到數(shù)據(jù)庫中,而有JDO這樣的面向?qū)ο蟮某志没夹g(shù),在某種程度上解放了這種轉(zhuǎn)化設(shè)計(jì)的不規(guī)范性,進(jìn)而獲得相對(duì)更優(yōu)的系統(tǒng)結(jié)構(gòu)。
另一方面,一個(gè)業(yè)務(wù)系統(tǒng)的數(shù)據(jù)持久化,一般都有這樣的過程:對(duì)象層->記錄層->物理層,JDO無疑使分析設(shè)計(jì)人員從記錄層的苦海中解脫出來,從而更加專注于對(duì)象層,這對(duì)開發(fā)人員無疑是一個(gè)令人歡欣鼓舞的技術(shù)。
JDO并不能完全代替JDBC。
根據(jù)經(jīng)典的“8-2原理”,如果用JDO來解決80%的問題,余下的20%由JDBC來實(shí)現(xiàn),這種相互補(bǔ)充,各取所長(zhǎng)的策略,是一個(gè)很有效的辦法。
這樣一方面可以獲得較好的結(jié)構(gòu)及提升開發(fā)質(zhì)量,另一方面解決了JDO的某些技術(shù)不足,并可根據(jù)以后的技術(shù)變化,再做適當(dāng)轉(zhuǎn)化。
性能問題
JDO與JDBC究竟誰的性能更優(yōu),目前還沒有一個(gè)權(quán)威、且令人滿意的答案。但對(duì)于一些JDO實(shí)現(xiàn)而言,通過采用緩存機(jī)制,使得性能有了較大提高。
跨數(shù)據(jù)庫
使用JDO的系統(tǒng)能更好地進(jìn)行數(shù)據(jù)庫移植,甚至可以在對(duì)象數(shù)據(jù)庫上運(yùn)行;當(dāng)然JDBC處理層如果完全遵循SQL-92標(biāo)準(zhǔn),也同樣具有很好的跨數(shù)據(jù)庫能力。
posted on 2005-11-04 13:53
Sung 閱讀(252)
評(píng)論(0) 編輯 收藏 所屬分類:
Java