JDO持久 (jdbc ejb)

轉自:http://blog.csdn.net/liuzhigang1237/article/details/6305113java

 

JDO快速入門

Java數據對象(Java Data Objects,JDO)是一個應用程序接口(API),它是Java程序員可以間接地訪問數據庫,也就是說,不需使用直接的結構化查詢語言(SQL)語句。JDO是做爲Java數據庫鏈接(JDBC)的一個補充來介紹的,而JDBC是一個支持使用SOL語句對流行的數據庫程序進行訪問的接口。有了 JDO,程序員就可使用類來定義數據對象,而後支撐程序就會根據類的定義來管理對給定數據庫的實際的數據訪問了。  

      JDO是以Sun公司爲首所制定的Java Community Process(Java標準制定組織,JCP)的一部分。JDBC仍然保留使用是由於它比起JDO容許程序員在數據庫訪問上有更大的控制權。除JDO和 JDBC外的另外一個選擇是Enterprise JavaBeans (EJB)。

1、爲何要用JDO

Java開發人員已經有好幾種存取數據庫的方法:序列化,JDBC,面向對象映射工具,面向對象數據庫,以及實體EJB。那爲何還要介紹其餘的存儲架構呢?答案是,上面每一種實現存儲的方案都存在必定的限制。JDO正在嘗試解決這些限制。


序列化:是Java創建的一種傳輸機制,它可以把對象的信息轉換成一系列的字節碼,這些字節碼能夠被傳輸到網絡或者存儲到一個文件中。序列化的使用很是簡單,但他仍是有限制的。它必須當即存取對象的特徵,並且它不適合存取大批量的數據。在更改一個對象的屬性時若是有錯誤發生它沒法實現「回滾」,所以不適於應用程序對數據完整性的要求,並且不能實現多個線程或程序異步讀寫數據。全部這些不足都使得序列化沒法知足大多數數據存儲要求。


JDBC:許多程序員使用 JDBC API來操做關係數據庫。JDBC克服了許多序列化中存在的缺點:它能夠操做大批量的數據,有確保數據一致性的機制,支持信息的併發存取,可使用已經很是成熟的SQL語言。不幸的是,JDBC使用起來並不像序列化那麼簡單。JDBC使用的關係範例沒法用於存儲對象,所以你不得不放棄在代碼中使用面向對象原則存儲數據。


面向對象映射工具:由軟件廠商建立的架構能夠爲你實現對象和關係數據庫之間的映射。這種對象-關係映射支持使你專一於對象模型的設計而沒必要關心面向對象和關係數據庫之間的匹配。不幸的是每一種對象-關係映射產品都有一套他本身廠商實現的標準。你不得不使本身的代碼遷就於某一個單獨廠商的實現。假如這個廠商提升產品價格或者中止對bug更改的支持,使你準備放棄它而用其餘的廠商實現架構時,你就必須重寫你的代碼。


面向對象的數據庫:比對象關係數據庫映射更好的選擇使使用一些軟件廠商開發了一種新的把對象存儲到數據庫的方法。這種面向對象的數據庫使用起來經常比對象關係映射軟件簡單。ODMG組織成立的目的之一就是建立一種訪問對象數據庫的標準API。多數廠商都遵崇ODMG組織的要求,所以因爲廠商實現不一樣帶來的麻煩也解決了。可是,一些企業對於從關係數據庫轉向對象數據庫顯得猶豫不決,由於有大量的數據存儲在傳統的關係數據庫中。雖然一些數據庫分析工具能夠用於面向對象數據庫與關係數據庫之間的移植,然而大量的數據存儲使用的仍然是關係數據庫。


實體EJB:Java平臺的企業級應用中引入了實體EJB。實體EJB是一個組件,他描述了數據庫中的持久性數據信息。EJB使用相似於對象-關係映射的辦法,它提供了一個持久性數據的面向對象的表示。不一樣於對象關係軟件,EJB對於關係數據庫沒有限制;它描述的持久性信息能夠來自一個企業信息系統(EIS)或者其餘的存儲設備。並且,EJB要求遵循一個嚴格標準,實現它的廠商必須遵循這個標準。不幸的是,EJB標準在面向對象方面稍微有些欠缺,好比一些高級的特性:繼承、多態和複合關係等。另外,EJB的代碼編寫很複雜,並且它是一個重量級組建須要消耗應用服務器不少的資源來運行。可是,EJB中的會話 Bean和消息驅動Bean有不少優點,因此JDO規範詳細定義了JDO如何與他們進行集成。


JDO:JDO集成了不少上述持久性機制的特性,這使得在JDO中建立一個持久化(persistence)類就像建立一個序列化類同樣簡單。JDO支持批量數據的存儲,數據一致性,併發處理和JDBC的查詢功能。就像對象-關係映射軟件和對象數據庫同樣,它容許使用面向對象的高級特性好比「繼承」。它避免了像EJB中實體Bean同樣必須依賴於來自廠商定義的嚴格規範。同EJB同樣,JDO也不規定任何特定的後端數據庫。

可是,這裏仍是要說一下,世界上沒有「萬靈丹」。因此,使用JDO並非對於每個應用程序都是有好處的。不少應用程序徹底可使用其餘更理想的存儲機制。

2、JDO架構

下面我開始對JDO的架構做一個簡單的介紹。

下圖顯示了JDO架構主要的幾部分:

JDO快速入門 - shirnie - shirnie的博客



JDOHelper :javax.jdo.JDOHelper類擁有一些靜態的助手(helper)方法。這個方法能夠得到一個持久對象的生命週期還能夠用來建立一個與具體實現廠商無關的PersistenceManagerFactory的實例,這裏使用了工廠(factory)模式。

PersistenceManagerFactory:javax.jdo.PersistenceManagerFactory類能夠經過JDOHelper類的助手方法得到,這是一個標準的工廠類,他能夠建立PersistenceManager類。

PersistenceManager:javax.jdo.PersistenceManager接口是應用程序常常要使用的一個主要的JDO接口。每個PersistenceManager負責控制一組持久化對象並且他還能夠建立新的持久化對象或刪除現有的持久化對象。Transaction和 PersistenceManager之間存在這一對一的關係,同時PersistenceManager又是Extent和Query的工廠類,也就是說這兩個對象能夠經過PersistenceManager建立。

PersistenceCapable
:用戶定義的持久化類都必須擴展實現PersistenceCapable接口。大多數JDO實現的供應商都提供一種「加強器」(enhancer)的功能,它能夠向你要實現的持久化類中增長PersistenceCapable接口的實現。也就是說,其實你根本不會本身去實現這個接口。

Transaction:每個PersistemceManager和javax.jdo.Transaction都是一一對應的。Transactions用來處理事務,它使得持久化數據能夠成批的一次性添加到數據表中,若是出現異常就將數據回滾。

Extent:java.jdo.Extent是映射數據庫中具體表的類的一個邏輯視圖。Extent能夠擁有本身的子類,它經過PersistenceManager得到。

Query:java.jdo.Query接口用具體的廠商JDO來實現,它負責處理JDO查詢語言(JDOQL),這些JDOQL最終被解釋爲實際的數據庫SQL語言。一樣這個接口也是經過PersistenceManager得到的。

下面的例子顯示的JDO接口如何操做並執行一個查詢並更新持久化對象。


例子:JDO接口的交互

//經過助手類得到PersistenceManagerFactory

PersistenceManagerFactory factory=
JDOHelper.getPersistenceManagerFactory(System.getProperties());

//經過PersistenceManagerFactory得到PersistenceManager對象

PersistenceManager pm=factory.getPersistenceManager();

//建立並開始一個事務

Transaction tx=pm.currentTransaction();
tx.begin();

//查詢employee表中每週工做時間大於40小時的研究人員

Extent ex=pm.getExtent(Employee.class,false);

//得到一個Query

Query query=pm.newQuery();

//設置這個query做用的範圍,即查詢的是那個表或記錄集

query.setCandidates(ex);
query.setFilter(division.name == /Research/ + && avgHours > 40);
Collection result=(Collection)query.execute();
Employee emp;
for(Iterator itr=result.iterator();itr.hasNext();){
emp=(Employee)itr.next();
emp.setSalary(emp.getSalary()*2);
}

//提交記錄釋放資源

tx.commit();
pm.close();
factory.close();
 


上面的代碼片段包括了JDO幾個主要的接口,在此你能夠對JDO各個接口的使用方法有一個粗略的印象,之後實際的應用中JDO接口也都是這樣使用的。


3、JDO的異常

JDO不會拋出一般的運行時異常,好比NullPointerExceptions、 IllegalArgumentException等它只拋出JDOException異常。JDOExcetion的結構以下圖所示,這是一個繼承的層次結構,從他們的字面含義就能夠看出它們的用途,在這裏就不詳細說了,要想了解JDO異常的層次結構能夠參考它們的JavaDoc。

4、使用JDO的好處

·簡便性(Portability):使用JDO API編寫的程序能夠在不一樣開發商的多種可用的實現上運行,不用修改一行代碼,甚至不用從新編譯。

·透明地訪問數據庫(Transparent database access):應用程序開發者編寫代碼透明地訪問底層數據存儲,而不須要使用任何數據庫特定代碼。

·易用性(Ease of use):JDO API容許開發者只須要關注他們本身範圍內的數據模型(Domain Object Model,DOM),而持久化的細節就留給JDO實現。

·高性能(High Performance):Java應用程序開發者不須要擔憂數據訪問的性能優化,由於這個任務已經委派給了JDO實現,它經過改善數據訪問的模式以得到最佳性能。

·和EJB集成(Integration with EJB):應用程序能夠利用EJB的特徵,例如遠程信息處理、自動分佈式事務協調和貫穿整個企業級應用使用一樣的DOMs實現安全性。

5、使用JDO,vs. EJB和JDBC

JDO並不意味着要取代JDBC。它們是兩種以各自獨一無二的能力互相補充的技術,具備不一樣技術背景和開發目的開發者可使用兩者中的一個。例如。JDBC經過直接的數據庫訪問控制和緩存管理,提供給開發者更大的彈性。JDBC是一種在工業界被普遍承認的成熟技術。另外一方面,JDO,經過隱藏SQL提供給開發者更大的簡便性。它將Java平臺開發者從必須熟悉或學習SQL中解脫出來,而將精力集中在 DOM上,同時JDO管理在持久存儲中對象存儲的字段到字段的細節。

JDO被設計成EJB的補充。CMP爲容器提供簡便的持久化,而JDO能夠以兩種方式集成到EJB 中:

(1)經過會話Bean,它含有JDO Persistence-capable類(會話Bean的持久化助手類)用來實現依賴對象;

(2)經過實體Bean,它含有被用做BMP和CMP代理的 JDO Persistence-capable類。

你能夠學習更多關於JDO和JDBC之間的關係,還有EJB2.0 CMP和JDO之間的關係。

6、POJO之路

JDO和 EJB之間在持久化模型上顯著的差異曾經在開發者中間引發了混亂。做爲迴應,Sun微系統正領導一個社區項目爲Java技術社區建立POJO持久化模型。這個項目在JSR-220的贊助下執行,由Linda DeMichiel領導。JDO2.0(JSR-243)的專家組成員被邀請加入到EJB3.0(JSR-220)專家組中。

建立POJO持久化模型的目的是爲全部使用Java SE和Java EE平臺的Java應用程序開發者提供一個對象—關係(object-relational)映射工具。值得注意的是Oracle正以co- specification lead的身份加入到Sun EJB3.0。EJB3.0的公衆評論草案已經能夠獲得。

JSR-243(JDO2.0)遵循了那些來自於JSRs220和243規範的領導寫給Java技術社區的信件所描述的輪廓。

JDO2.0並不打算做爲EJB3.0持久化特定API的集中,而是做爲JDO1.0.2的發展。可是JDO的POJO持久化模型和EJB3.0之間的相似處,使得JDO的客戶當使用JDO2.0知足當即的需求時,能夠很容易的接受EJB3.0持久化模型。另外,JSR-243打算將JDOQL用做一種關於EJB3.0持久化數據的可選查詢語言。這種語言了已經被更新從而能夠更好地對EJB3.0使用。

要了解更多關於持久化模型的知識,請查看EJB/JDO持久化FAQ。

7、JDO Class類型

在JDO中一共有三種類型的類:

·Persistence-capable:這種類型表明那些實例能夠被持久化到一個數據存儲中的類。請注意,這些類在JDO環境中被使用以前,須要經過JDO元數據規範進行增強。

·Persistence-aware:這些類操縱persistence-capable類。JDOHelper類包含了一些方法,它們容許詢問一個persistence-capable類的實例的持久化狀態。請注意,這些類使用最小化的JDO元數據增強。

·Normal:這些不可被持久化,而且對持久化一無所知。另外它們不須要JDO元數據。

8、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 表明一個瞬時事務處理實例的持久對象,它們的數據在當前事務中已經被改變。


下圖顯示了JDO實例各狀態之間的轉換。

JDO快速入門 - shirnie - shirnie的博客


本文稍後的代碼片段,將示範如何執行咱們剛剛討論的操做。

9、JDO參考實現

JDO參考實現,來自於Sun微系統,已經可用,一同發行的還有一種被稱爲fstore的基於文件的存儲機制。Sun已經把JDO捐獻給開源社區。JDO1.0和JDO2.0將會做爲Apache JDO項目的一部分進行開發。可是因爲時間的限制,JDO2.0的參考實現並非做爲Apache項目創建的,而是做爲一個JPOX 發行。一些商業實現也是可用的。

10、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)方法。

11、查詢對象

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">
<field name="name" primary-key="true"/>
<field name="emps">
<collection element-type="Employee">
<extension vendor-name="sunw" key="element-inverse" value="dept"/>
</collection>
</field>
</class>
</package>
</jdo>



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中。

12、JDO對開發的幫助有哪些 - 實例解析

1.權責劃分:業務開發組和數據庫管理組

對一個項目來說,開發團隊在邏輯上劃分爲兩塊:業務開發組和數據庫管理組。二者各有特色,各有責任,但相互之間界限很清晰,不會有什麼糾纏。下面用表格說明一下兩者的區別:

人員構成

業務開發組系統分析員、程序員。

數據庫管理組DBA、運行維護人員。通常一到兩我的就能夠,並可橫跨多個項目

工做內容

業務開發組設計數據類圖,設計業務邏輯接口並用代碼實現(通常在相似sessionbean的類中)

數據庫管理組經過數據類圖映射數據表,通常只需在JDO自動生成的表結構上做少量調整

所需知識和工具

業務開發組UML、Java、JSP、Ant。工具可爲任何IDE。

數據庫管理組JDO原理、UML中的類圖、鏈接池配置。工具包括PowerDesigner、數據庫訪問工具、具體使用的JDO產品的一些細節

相互的責任範圍

業務開發組向數據庫管理組提交數據部分的UML實體類圖,及某些細節(如某屬性是否很長的字符串)。在對方配置好PersistenceManagerFactory鏈接池後在代碼中調用。

數據庫管理組根據UML類圖及業務開發組對某些細節的建議創建相應的數據庫(基本上自動完成),在服務器上配置相應的JDOPersistenceManagerFactory(一個J2EEConnector,就象配置數據庫鏈接池同樣)

工做量

1).業務開發組與業務相關,由於主要代碼量在業務邏輯上。

2).數據庫管理組通常狀況下不大,但在從舊數據庫中導入數據時可能須要一些功夫,不過是一次性的工做。

3).涉及數據結構變更的功能變動時所需工做。

4).業務開發組一方面調整UML實體類圖,提交給數據庫開發組;另外一方面根據新功能需求改寫業務邏輯代碼。

5).數據庫管理組根據新的UML實體類圖調整數據庫結構(有的JDO產品可自動完成)。服務器配置不變。

6).因爲面向數據庫管理組的工做內容比較簡單,只是量的問題,下面的介紹就儘可能不涉及數據庫管理組的工做,而只面向業務開發組。

2.UML實體類圖

UML實體類圖是項目中涉及到數據的部分,這些數據不會隨着程序停止而丟失,稱做可持續的(Persistent),全部數據庫中的數據都是可持續的。

而咱們在設計的時候,最開始應該分析出系統有哪些實體類(便可持續的數據類),從而畫出實體類圖。在這個最初級的類圖上面,能夠不包含任何屬性,但必須包含實體類之間的關係,這樣才能一眼看出系統的大概輪廓。

下面就是一個簡單的示範實體類圖,是一個論壇中的主要實體的關係圖(原圖沒有有,用孫賓同窗的做品抵擋一下。後來發現,這個文章原本就是孫賓同窗的做品!)。

JDO快速入門 - shirnie - shirnie的博客


簡單地說,項目能夠說是一些具備相互關係的實體類加上處理業務邏輯的控制類,以及輸入/輸出數據的邊界類組成,另外可能附加一些接口或特殊服務,如短信/郵件發送或面向第三方的數據訪問接口等等。

有了上面這個圖,DBA就比較清楚數據庫中會有什麼樣的數據表,表之間如何關聯了。但數據庫中的表與實體類並非一一對應的。好比對實體類圖中的某個多多對應關係,數據庫中必須有一個額外的表來對應,有些實體的某部分屬性可能會放在另外一個額外表中以增強性能。

下一步就是在這個圖的基礎上爲實體類添加屬性,而後給每一個屬性加上 訪問器(accessors,即getXXX()/isXXX()和setXXX() 等),以及一些必須的方法(好比getAge(),經過當前日期和生日得出年齡)。這樣,才成爲一個完整的實體類圖。下圖就是一個增添了普通屬性的實體類圖(真的找不到圖了-_-)。

接下來,加入對普通屬性的訪問器方法,可能再給加一個Member.getAge()方法,這個實體類圖就算是完成了。這些過程都比較簡單,而且有不少工具能夠自動完成,這裏再也不多說。

有一點要着重說明的是,對實體類,只要給出這個圖,而後用工具生成對應的Java類代碼,這些類的代碼就算是完成了,之後不用再在其中寫代碼了。

3 透明的存儲

對開發人員來講,主要工做集中在業務邏輯的實現上,這就須要寫一些控制類,來實現這些邏輯。這些控制類通常能夠 XxxSession的方式來命名,表示面向某一類使用者的控制類,好比MemberSession,完成會員登陸後的一些功能; AdminSession用於完成管理員登陸後的一些功能。

在這些控制類中的一個方法中,只須要經過JDO規範的接口類(javax.jdo.*)來獲取對前面的實體的訪問,從而完成業務功能。一個典型的方法以下:

MemberSession的發表主題貼的方法:

public Topic postTopic(String title,String content, String forumId) {
//業務邏輯過程開始
javax.jdo.PersistenceManager pm = getPersistenceManagerFactory().getPersistenceManager();
pm.currentTransaction().begin();

//先生成一個主題,設置基本屬性
Topic topic = new Topic();
topic.setTitle(title);
topic.setContent(content);
topic.setPostTime(new Date());

//獲取相關的論壇和當前登陸的會員
//下面用到的this.logonMemberId是本MemberSession對象生成時必須提供的會員標識。
//本MemberSession對象通常是在登陸的時候生成的。
Forum forum = (Forum)pm.getObjectById(pm.newObjectIdInstance(Forum.class,forumId));
Member author = (Member)pm.getObjectById(pm.newObjectIdInstance(Member.class, this.logonMemberId));

//設置該主題的論壇和做者
topic.setForum(forum);
topic.setAuthor(author);

//標記爲須要存儲
pm.makePersistent(topic);

//順便更改論壇和做者的一些相關屬性
forum.setTopicCount(forum.getTopicCount()+1);
author.setPostCount(author.getPostCount()+1);

//業務邏輯過程完成
pm.currentTransaction().commit();
pm.close();
}

這樣,這個方法就算寫完了。咱們能夠看到,只要將與實體類相關的代碼放在pm.currentTransaction()的開始和提交之間就能夠了。

惟 一中間須要與JDO打交道的就是對新生成的對象(topic)須要調用一下pm.makePersistent(),但實際上在不少狀況下,只要從pm中 取出的對象指向這個對象(好比:author.getPostTopics().add(topic)),就根本不須要這條語句(固然寫上也沒錯),由於 pm會根據可達性(Reachability)的原則將當前已經在數據庫中的對象能直接或間接指到的新生成的那些對象都存儲起來。

以上的代碼說明了咱們沒必要對每一個發生變化的對象調用更新函數,由於JDO的pm會自動跟蹤這些變化,並將確實發生改變的對象同步到數據庫。這就是「透明的存儲」。

4 靈活的查詢:JDOQL vs SQL


JDOQL是JDO中使用的查詢語言,是對象式的查詢語言,很象OQL,也很象EJBQL,但沒有EJBQL那種只能靜態存在的缺點。

對象式查詢語言的優勢有不少文章都有介紹,這裏再也不說明。只說明一點:JDOQL徹底基於UML實體類圖,沒必要理會具體數據庫中的任何內容。
下面舉一些例子,說明這種靈活性。

4.1 例:查找某做者發表過貼子的全部論壇


咱們給出的參數只有做者的姓名,但願獲得的是全部的他發表過主題或回覆過主題的論壇。咱們須要這樣的JDOQL條件:首先查詢的目標是Forum類,而後是JDOQL的過濾串
this == _topic.forum && (_topic.author.name == 「<做者姓名>」 || _topic.contains(_reply) && _reply.author.name == 「<做者姓名>」)

而後,聲明用到的變量:Topic _topic; Reply _reply;
再執行SQL便可。通常的JDO產品會將這個查詢儘量優化地翻譯爲:

select a.<可預約義的最經常使用字段組> from FORUM a, TOPIC b, REPLY c, MEMBER d

where a.FORUM_ID = b. FORUM_ID and (b.MEMBER_ID = d. MEMBER_ID and d.NAME=’<做者姓名>’ or b.TOPIC_ID = c. TOPIC_ID and c.MEMBER_ID = d.MEMBER_ID and d.NAME = ‘<做者姓名>’)

從上面,咱們能夠看到,JDOQL不管在可讀性仍是可維護性上都遠遠好於SQL。咱們還能夠將做者姓名做爲一個綁定參數,這樣會更簡單。

若是直接操做SQL的話會變得很麻煩,一方面要注意實體類中的屬性名,一方面又要注意在數據庫中的對應字段,由於多數狀況下,二者的拼寫因爲各類因素(如數據庫關鍵字衝突等)會是不同的。
從這個例子擴展開去,咱們能夠進一步:

4.2 例:查找某做者發表過貼子的全部論壇中,總貼數大於100而且被做者收入本身的收藏夾的那些論壇


很簡單,將過濾串這樣寫:

this == _topic.forum && (_topic.author == _author || _topic.contains(_reply) && _reply.author == _author) && _author.name == ‘<做者姓名>’ && postCount > 100 && _author.favoriteForums.contains(this)
這一次多了一個用到的變量:Member _author。其底層的SQL你們能夠本身去模擬。

5 長字符串


咱們常常會遇到用戶輸入的某個信息文字串超出了規定的數據字段的大小,致使很麻煩的處理,尤爲是一些沒有必要限制長度的字符串,好比一篇主題文章的內容,有可能幾萬字,這迫使咱們將其分做不少子記錄,每條子記錄中放一部分。全部這些,都使咱們的代碼量加大,維護量加大。

如今有了JDO,咱們的代碼就簡單多了,咱們可能儘可能利用JDO提供的透明存儲功能,經過一些簡單的工具類實現:原理是將其分割爲字符串子串。

package jdo_util;
import java.util.*;

public class StringHelper {
public static List setLongString(String value) {
if(value == null) return null;
int len = value.length();
int count = (len+partSize-1)/partSize;
List list = new ArrayList(count);
for(int i = 0; i < count; i++) {
int from = i*partSize;
list.add(value.substring(from,Math.min(from+partSize,len)));
}
return list;
}

public static String getLongString(List list) {
if(list == null) return null;
StringBuffer sb = new StringBuffer();
for(Iterator itr = list.iterator(); itr.hasNext(); ) sb.append(itr.next());
s = sb.toString();
return s;
}

private static int partSize = 127; //字符串片段的大小。針對不一樣的數據庫能夠不一樣,如Oracle用2000
}

有了這個類之後,咱們只須要將Topic.content的類型換成List,而其訪問器的接口不變,還是String,只是內容變一下:(並在JDO描述符中指明該List的元素類型是String)

public class Topic {

List content; //原先是String類型

public String getContent() {
return StringHelper.getLongString(content);
}

public void setContent(String value) {
content = StringHelper.setLongString(value);
}
}

這樣,就解決了長字符串的問題,而其它相關的代碼徹底不須要改,這就支持了無限長的主題內容。
最後,惟一的缺陷是對內容進行關鍵字查詢的時候須要將
content.startsWith(‘%<關鍵字>’)
變爲
content.contains(s) && s.startsWith(‘%<關鍵字>’)
而且,可能查詢結果不太準(好比正好跨越兩個子串部分)。慶幸的是,通常這種對很長的字符串字段的查詢需求不是太多。

須要說明的是,採用傳統的SQL一樣也會須要對拆分的字符串進行額外的查詢,並具備一樣的缺點。
另外,這個功能須要JDO產品支持規範中的一個可選選項:javax.jdo.option.List,主要的幾個JDO產品都支持。好比KodoJDO和JDOGenie。

6 資源回收:pm.close()


咱們採用傳統SQL寫代碼時,最危險的就是資源釋放問題,這在基於WEB的應用中尤爲重要。由於與JDBC相關的資源不是在Java虛擬機中分配的,而是在系統底層分配的,Java的垃圾回收機制鞭長莫及,致使系統內存慢慢耗光而死機。

在JDBC中須要主動釋放的資源有:Connection、Statement、PreparedStatement、ResultSet,在每一個對這些類型的變量賦值的時候,都必須將先前的資源釋放掉。無疑是一件繁瑣而又容易被忽略的事情。
在JDO 中,事情變得簡單多了,全部的資源在pm.close()的時候會自動釋放(除非JDO產品增長了一些對PreparedStatement和 ResultSet的Cache),這是JDO規範的要求。所以,只要咱們記住在對實體類處理完畢時調用pm.close()就好了。好比下面的代碼:

PersistenceManager pm = null
try {
pm = getPersistenceManagerFactory().getPersistenceManager();
//作一些數據類的處理工做

} finally{
pm.close();
}

有些人可能就是不喜歡調用它,以爲煩,由於每次要用時都要打開一個PM,而用完時都要關閉,若是JDO產品沒有PM鏈接池的話,性能可能受到影響。這樣,咱們能夠利用下面的繼承java.lang.ThreadLocal的工具類完成這一點:

public class PersistenceManagerRetriever extends ThreadLocal {
/**
* 根據配置信息初始化一個PersistenceManager獲取器
* @param p
*/

public PersistenceManagerRetriever(java.util.Properties p) {
pmf = JDOHelper.getPersistenceManagerFactory(p);
}

/**
* 獲取相關的PersistenceManagerFactory
* @return 一個PersistenceManagerFactory對象
*/

public PersistenceManagerFactory pmf() {
return pmf;
}

/**
* 獲取一個與當前線程相關的PersistenceManager
* @return 一個PersistenceManager對象
*/

public PersistenceManager pm() {
return (PersistenceManager)get();
}

/**
* 釋放全部與本線程相關的JDO資源
*/

public void cleanup() {
PersistenceManager pm = pm();
if(pm == null) return;

try {
if(!pm.isClosed()) {
Transaction ts = pm.currentTransaction();
if(ts.isActive()) {
log.warn("發現一個未完成的Transaction ["+pmf.getConnectionURL()+"]!"+ts);
ts.rollback();
}
pm.close();
}

} catch(Exception ex) {
log.error("釋放JDO資源時出錯:"+ex,ex);

} finally {
set(null);
}
}

public Object get() {
PersistenceManager pm = (PersistenceManager)super.get();
if(pm == null || pm.isClosed()) {
pm = pmf.getPersistenceManager();
set(pm);
if(log.isDebugEnabled()) log.debug("retrieved new PM: "+pm);
}
return pm;
}

public static final Logger log = Logger.getLogger(PersistenceManagerRetriever.class);
private PersistenceManagerFactory pmf;
}

這樣,只要在一個線程中(好比一次頁面請求),在全部的須要PM的地方,都只需直接調用

persistenceManagerRetriever.pm();

便可,而且,只在最後用完後才調用一次persistenceManagerRetriever.cleanup()以關閉它。

這個persistenceManagerRetriever能夠在某個系統類的初始化代碼中加入:

PersistenceManagerRetriever persistenceManagerRetriever = new PersistenceManagerRetriever(properties);


而關閉當前線程相關的PM的語句(persistenceManagerRetriever.cleanup())能夠配置一個JspFilter來完成它,好比:

public static class JspFilter implements javax.servlet.Filter {
public void doFilter(
javax.servlet.ServletRequest request,
javax.servlet.ServletResponse response,
javax.servlet.FilterChain chain)
throws javax.servlet.ServletException,java.io.IOException {
try {
chain.doFilter(request,response);
} finally {
if(pmRetriever != null) pmRetriever.cleanup();
}
}
public void init(javax.servlet.FilterConfig filterConfig) throws javax.servlet.ServletException {}
public javax.servlet.FilterConfig getFilterConfig() { return null; }
public void setFilterConfig(javax.servlet.FilterConfig fc) {}
public void destroy() {}
}

而後咱們將其配置在WebApp的描述符中:

<filter>
<filter-name>jdo_JspFilter</filter-name>
<filter-class>…xxx.jdo_util.JspFilter</filter-class>
</filter>

<filter-mapping>
<filter-name>jdo_JspFilter</filter-name>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>

這樣,咱們在JSP中的代碼更簡單:



persistenceManagerRetriever.pm().currentTransaction().begin();


//調用一些處理業務邏輯的XxxSession.someMethodThatUsesPM()方法,這些方法中直接用persistenceManagerRetriever.pm()來取得PM。

persistenceManagerRetriever.pm().currentTransaction().commit();

不用處理異常,JspFilter自會處理。

7 ID與對象模型


對 象標識字段,實際上只是一個數據庫範疇的字段,在對象模型中其實是不須要這些屬性的。也就是說,在Java應用中,一個對象的標識就是在內存中的地址, 不併是這個對象自己的屬性,由於根據這個內存地址就能夠惟一地肯定這個對象。好比一個編輯矢量地圖的Java程序,從文件中讀入各個地圖元素(對象)後, 這些對象就有了一個惟一的內存地址,因此不須要給每一個對象加一個相似「ID」之類的屬性並寫入文件。

JDO也採用了這樣的概念,ID獨立於對象以外,並不屬於對象的一部分。前面的論壇實體類圖中咱們能夠看到,每一個類中都沒有相似「id」之類的屬性。那麼,JDO怎樣控制與數據庫中的主鍵的對應呢?這就是兩個經常使用的工具類方法:

Object PersistenceManager.getObjectId(Object obj)
Object PersistenceManager.getObjectById(Object obj, boolean validate)

這樣,能夠隨時得到某個實體對象的ID,也能夠在任什麼時候候經過一個ID找出該對象。第一個方法還能夠用javax.jdo.JDOHelper.getObjectId()代替。

JDO 規範建議的模式中,這些ID都是由JDO產品自動生成的,項目應用中只在須要傳遞對象的引用的時候才使用,好比在兩個頁面間傳送。而且,這些ID類都是可 以與String互轉的,這就方便了JSP間的傳遞。這種由JDO產品來控制的ID叫作datastore identity,在數據表中的字段名通常是「JDO_ID」。
若是實在是想本身控制對象在數據庫中的ID,JDO也提供用戶自定義的ID,這 時,該ID做爲對象的一個屬性存在,能夠是任何類型,int, Date, String, 或其它自定義的複合類型(如兩個屬性合起來做ID)。這種類型的ID叫作application identity。

就我的而言,我建議在新的項目中採用datastore identity,這樣可省下不少時間。而在實體類中,也能夠寫一些替代的方法來保持與application identity保持兼容,如:

public class SomePersistentClass {

public String getId() {
return JDOHelper.getObjectById(this).toString();
}

public static SomePersistentClass getById(String id) {
PersistenceManager pm = persistenceManagerRetriever.pm();
return pm.getObjectById(pm.newObjectIdInstance(SomePersistentClass.class, id));
}
}

這種方式對兩種類型的ID都有效。注意,這個類自己有這兩個方法,但並無一個ID屬性。

8 緩衝與Optimistic Transaction


緩衝是JDO中的一個亮點。雖然JDO規範並無嚴格要求一個JDO產品必須實現什麼樣的緩衝,但幾乎每個JDO產品,尤爲是商業化產品,都有比較完善的緩衝體系,這個體系是不一樣的JDO產品相互競爭的重點之一。

主要的JDO產品包含下列緩衝:

1. PM鏈接池。對PersistenceManager進行緩衝,相似JDBC鏈接池,在調用pm.close()的時候並不關閉它,而是等待下一次調用或超時。

2. PreparedStatement緩衝。若是JDO底層發現一個JDOQL語句與前面用過的某句相同,則不會從新分析並生成一個新的 PreparedStatement,而是採用緩衝池中的已有的語句。對PreparedStatement的緩衝也是JDBC3.0規範中的一項功能。 而JDO底層發現若是配置的是符合JDBC3.0規範的驅動時,會採用驅動的緩衝,不然採用本身的緩衝。

3. ResultSet緩衝。這種緩衝的實現的JDO產品很少,目前好象只有KodoJDO 2.5.0 beta實現了。其機制是若是第二次請求執行一樣JDOQL語句、一樣參數的查詢時,JDO底層從上一次執行結果中取出該集合,直接返回,大大加強性能。 不過比較耗資源,由於是採用JDBC2.0中的ScrollableResultSet實現。

通常咱們在對數據庫進行更新操做時,都會對 數據庫進行鎖定操做,設定不一樣的隔離級別,能夠完成不一樣程度的鎖定,好比鎖記錄、鎖字段、鎖表、鎖庫等等。而JDO中能夠在具體JDO產品的廠商擴展 (Vendor Extension)標記中設定。另外,JDO規範還提供了一種對數據庫徹底沒有鎖定的方式: javax.jdo.option.OptimisticTransaction,它是一項可選選項,也就是說,並不強制JDO廠商實現它,不過主要的幾 個廠商的JDO產品都實現了這個功能。

OptimisticTransaction的機制原理是:在每一個對象的數據庫記錄中增長一個交易控制字 段,而後全部的對象更改在Java虛擬機的內存中完成,當提交的時候,會檢查每一個被改過的對象的在從數據庫中取出後是否被其它外部程序改過,這就是經過這 個控制字段完成的。通常這個字段的實現方式有如下幾種:

1. 存放最近一次更改的時間,字段名多取做「JDO_LAST_UPDATE_TIME」

2. 存放歷史上被更改過的次數,字段名多取做「JDO_VERSION」

在OptimisticTransaction 的一次Transaction中,JDO底層不會對數據庫進行鎖定,這就保證了時間跨度較長的transaction不會影響其它線程(請求)的執行,只 是若是更新操做比較多,訪問量又比較大的話,Transaction提交失敗的的概率也會相應變大。

9 JDBC2.0和JDBC3.0

JDO只是一種對象級的包裝,是創建在JDBC的基礎上的,二者不能相互替代。實際上,JDBC的規範從1.0到2.0,再到3.0,一直在作功能和性能方面的改進。 JDO 產品固然不會放過這些,通常的JDO產品,會檢測底層配置的JDBC驅動是符合哪一個規範,並會盡可能採用驅動自己的功能來實現具體的操做。對代碼開發人員來 說,咱們大多數狀況下只能掌握JDBC1.0的操做,和少許的2.0的操做,只有一些很精通JDBC的高手纔會用到JDBC3.0中的高級功能。所以,採 用JDO也能夠幫助咱們在不瞭解JDBC3.0規範的狀況下提升性能和效率。 換句話說,JDBC技術自己就是一件很複雜的東西,要想優化性能的 話,不少JDBC技術和數據庫技術是須要使用的,好比inner join, left/right outer join, Batch update,等等。這些對開發人員的技術要求很高,一方面要精確理解每種技術的應用範圍和實際使用的注意事項,另外一方面代碼也會比較複雜。所以,既然有 衆多的有經驗的JDO廠商在作這些事情,咱們又何須再花功夫呢? 以上我介紹了JDO對咱們的數據庫項目開發的比較明顯的幾個好處,之後的文章中,我會繼續寫關於JDO使用中的概念性的問題和具體JDO產品的配置與使用,以及一些技巧
相關文章
相關標籤/搜索