服務數(shù)據(jù)對象(Service Data Object,SDO)介于強類型POJO和非驗證XML流中間。SDO使用戶應用程序能夠使用可動態(tài)創(chuàng)建的數(shù)據(jù)結構。當數(shù)據(jù)必須在沒有(或不能)共享相同的嚴格類型定義的環(huán)境(例如編譯后的POJO類集合)中傳播時,SDO這種靈活的動態(tài)數(shù)據(jù)結構就顯得尤為重要??梢詫⑺鼈儽茸黛o態(tài)(POJO)和動態(tài)(SDO)——由于這種比喻——很明顯,SDO是SOA的重要組成部分。這就是為什么所有企業(yè)的IT部門內(nèi)都存在不同形式的‘動態(tài)數(shù)據(jù)結構’主題。懷疑論者會說:這不就是名稱值對嗎?但是SDO遠遠超越了所謂的名稱值對。例如,該規(guī)范認為數(shù)據(jù)(例如人員)是通過各種關系而富有意義,并解釋了DataGraph的概念。還詳細說明了如何通過ChangeSummary跟蹤DataGraph的變化。更多詳細內(nèi)容請參閱 參考資料 部分。
SDO是關于動態(tài)數(shù)據(jù)的高級規(guī)范。但是不具備持久性的數(shù)據(jù)是什么樣的呢?我們?nèi)绾螌DO DataGraph存儲到數(shù)據(jù)庫中,以及如何從數(shù)據(jù)庫中檢索它呢?最初的SDO規(guī)范(2004年11月發(fā)布)提到了Data Mediator Service但是沒有給出其定義。最近再次查閱規(guī)范時,規(guī)范已經(jīng)發(fā)展到了2.1版本,并且持久存儲服務現(xiàn)在被稱為數(shù)據(jù)訪問服務,即DAS,但是仍然沒有詳細說明其細節(jié)。
在同一時期內(nèi),Java Persistence Architecture (JPA)已發(fā)展成熟,而且一些領先的應用程序服務供應商采用它作為其持久性提供者。BEA使用了Kodo,JBoss使用Hibernate,而OracleAS和GlassFish使用了Toplink。OpenJPA——Kodo的開源版本——很可能用于Geronimo和Websphere環(huán)境。自然,JPA是為SDO提供持久性存儲服務的重要備選方案。
在Google中快速搜索一下“JPA”、“SDO”和“JPA+SDO”,分別返回397萬、342萬和16萬的命中率。事實上,這些反應SDO和JPA討論次數(shù)的數(shù)字可轉換成左側的圖表。
我研究的第一個項目是Tuscany DAS。我大致查看了其中 可用 的內(nèi)容,但是似乎沒有使用JPA,至少目前是這樣。有關論壇一直在 討論 JPA的使用,但是其結果尚未報道出來。
下一個項目是 ALDSP。這是來自BEA的較成熟的產(chǎn)品,它使用的是SDO。然而,ALDSP也沒有使用基于JPA的持久性服務。
Eclipse EMF是首先具體實現(xiàn)SDO的項目之一。然而,EMF也沒有實現(xiàn)持久性服務(SDO規(guī)范并沒有向其授權)。
JPA解決了O-R映射中最棘手的問題,不過需要注意一些 警告 標志。它還為持久性存儲提供了一個簡單的、設計良好的API,這個API通過實現(xiàn)EJB 3.0很好地證明了自己。除了已經(jīng)證實的完善性,JPA逐漸成為為SDO提供持久性服務的自然選擇,這是因為JPA還具有大量能與SDO良好結合的特性。
首要特性是斷開或分離操作模式——在這種模式中,用戶應用程序連接到數(shù)據(jù)庫并從中取回數(shù)據(jù)后立刻釋放連接。數(shù)據(jù)作為相關的記錄集被應用程序查看——POJO圖形或DataGraph——看您喜好哪種術語了。數(shù)據(jù)可以在遠程進程中修改,而不需要對數(shù)據(jù)庫的原始數(shù)據(jù)建立活動連接。稍后,當重新建立數(shù)據(jù)庫連接時,這些修改可以合并到持久性數(shù)據(jù)中。這種斷開操作模式對可伸縮性非常關鍵,例如10000個活動用戶可以同時操作包含十個數(shù)據(jù)庫連接的連接池。這種應用程序使用模式同樣節(jié)省了大量的金錢,因為隨著連接數(shù)量的增加,數(shù)據(jù)庫安裝開銷將呈指數(shù)級增長。JAP支持這種斷開操作模式,當持久性存儲上下文關閉或事務提交后,取回的POJO實例將斷開連接,并且可以遠程修改斷開的對象圖并稍后合并到不同的持久性存儲上下文中。另一方面,SDO定義了ChangeSummary概念,用于跟蹤斷開的DataGraph中的改動。因此,JPA實現(xiàn)自然可以使用ChangeSummary內(nèi)容將改動有效地并入到數(shù)據(jù)庫中。
JAP和SDO自然結合的另一點是:它們都使用取回的數(shù)據(jù)作為連接對象圖。JPA目前還沒有充分開發(fā)定義斷開數(shù)據(jù)圖的潛力,但是OpenJPA或Kodo支持一種名為FetchPlan的強大擴展——來自于JDO規(guī)范的貢獻,該規(guī)范良好定義了這一概念,即指定從數(shù)據(jù)庫取回對象閉包的哪一部分。這種控制非常重要,因為完整的實例閉包通常就是整個數(shù)據(jù)庫。另一方面,SDO DataGraph定義了一種經(jīng)過配置的閉包,即所謂的根DataObject。
需要解決的關鍵分歧是類型系統(tǒng)。JPA是針對嚴格類型的POJO設計的。JPA實現(xiàn)管理的對象實例是由Java類靜態(tài)定義的,并由Java編譯器進行編譯。JPA(及其前身JDO)的美妙之處在于:這些實現(xiàn)能夠在不產(chǎn)生干擾的情況下截取對持久性實例的改變(狀態(tài)和/或關系)。對象仍然保持POJO特性,但JPA運行時可以知道用戶應用程序何時會對持久性對象調用setter方法以修改其狀態(tài),或何時調用要求從數(shù)據(jù)庫取回更多日期(通常稱為Lazy Loading)的getter方法。實際的實現(xiàn)會根據(jù)截取改動的方式而有所變化——例如——OpenJPA或Kodo取決于增強——該過程將修改編譯后的持久性Java類的字節(jié)碼,而其他實現(xiàn)可能會使用不同的機制,例如代理原始實體。無論哪種情況,都需要對實現(xiàn)作出較大的改動,以脫離其基本假設。例如,存在編譯后表示持久性數(shù)據(jù)的Java類。
另一方面,SDO傾向于松散類型的對象。SDO可以使用名稱和年齡表示一個Person對象,而不需要定義一個Person.java類。Person的定義可存在于一個XML模式文檔中,或者甚至可以通過編程的方式創(chuàng)建。
那么如何使用JPA存儲一個Person DataObject而不用編寫Person.java類呢?理論上講,JPA怎么能采用不是強類型的數(shù)據(jù)表示呢?
類型轉換系統(tǒng)僅僅是開始。還有很多問題要解決:
為了不被答案不是特別明顯的問題混淆,我決定逐步執(zhí)行一些步驟。因此我嘗試解決幾個簡單場景或用例。
該場景顯示了一個客戶如何持久存儲SDO對象。下面列出了給定的輸入:
a) 一個XML模式,定義一組SDO類型
b) 一個JPA配置,用于支持SDO的JPA運行時。
應用程序將調用SDO API定義SDO類型,然后將創(chuàng)建并填充一組相關的DataObjects,例如使用這些SDO類型的DataGraph。這個DataGraph將被提供給JPA API。支持SDO的JPA運行時將執(zhí)行把數(shù)據(jù)圖存儲到RDBMS所需的工作。事實上,我們首先將SDO類型和對象作為輸入,然后使用JPA實現(xiàn)持久性數(shù)據(jù)記錄。
另一個場景正好相反。我們首先從持久性數(shù)據(jù)開始。我們通過JPA API使用Java Persistence Query Language (JPQL)執(zhí)行一次查詢。通常,該查詢將產(chǎn)生一個Java實例列表——但是支持SDO的Java運行時會將SDO DataObjects返回給用戶應用程序。
如何實現(xiàn)這種支持SDO的JPA運行時?其中一種方法是將SDO放置(overlay)在JPA之上。JPA API將接受SDO DataObject作為參數(shù)并將DataObject作為查詢結果返回,但是SDO負責將DataObject實例轉換為一個具體的Java實例,反之亦然。我決定沿這種思路繼續(xù)深入研究,看看還能做些什么。
受測試驅動開發(fā)的啟發(fā),我首先編寫了一個JUnit測試用例。它展示了客戶應用程序如何結合使用SDO和JPA。
/*** Persist a DataObject. The operation should cascade to the closure of the* given DataObject.*/public void testPersist() {DataObject purchaseOrder = createPurchaseOrder();EntityManager em = emf.createEntityManager();em.getTransaction().begin();em.persist(purchaseOrder);em.getTransaction().commit();}/*** Query using JPQL. The query results should be DataObjects. The related* DataObjects should also be fetched.*/public void testQuery() {EntityManager em = emf.createEntityManager();String jpql = "SELECT p FROM PurchaseOrderType p";List result = em.createQuery(jpql).getResultList();for (Object o:result) {assertTrue(o instanceof DataObject);DataObject dataObject = (DataObject)o;assertEquals("PurchaseOrderType", dataObject.getType().getName());String orderDate = dataObject.getString("orderDate");assertEquals("1999-10-20", orderDate);DataObject shipTo = (DataObject)dataObject.get("shipTo");assertNotNull(shipTo);assertEquals("Alice Smith", shipTo.get("name"));}}
第一個測試用例通過createPurchaseOrder()方法創(chuàng)建了一個SDO DataGraph,該方法用于返回根DataObject。這個根實例將作為EntityManager.persist()的輸入。persist()操作必須將所有可從根實例中獲得的DataObject存儲到數(shù)據(jù)庫中。
第二個測試用例使用Java Persistence Query Language (JPQL)查詢數(shù)據(jù)庫。查詢結果為DataObject。必須取回查詢得到的實例及其相關實例,因為我們要檢驗相關的USAddress DataObject shipTo是否將名稱屬性設置為Alice Smith,因為這是在createPurchaseOrder()方法中創(chuàng)建DataGraph時所設置的內(nèi)容。
測試用例顯示用戶應用程序將調用標準的JPA EntityManager.persist()方法來存儲實例,但是其參數(shù)為commonj.sdo.DataObject的實例。類似地,查詢將生成一個DataObject實例列表,而不是強類型PurchaseOrderType.class的實例。
經(jīng)過幾天的研究、下載并使用Google搜索,然后又花費了幾天時間進行編碼,我編寫了一些類,將它們放入一個jar文件中并運行我的兩個測試用例。測試成功。在下一篇文章中,我將討論如何為SDO啟用JPA運行時。我將使用幾個便捷的步驟(但仍屬于基本步驟),幫助希望擴展JPA進行其他應用的人員。
慶幸的是不需要對作為JPA實現(xiàn)的OpenJPA做任何改變——這展示了OpenJPA的不凡之處——它針對可擴展性進行了設計(與隨意修改相反)。并且我已經(jīng)開始訂閱Tuscany用戶組——我使用了它們的SDO實現(xiàn)作為實驗原型——這個用戶組非?;钴S,我的郵件箱立刻填滿了郵件。但是所有這些需要改天再討論了……
[1]這篇最近的 文章 詳細介紹了SDO及其在SOA中的使用。
[2]完整的 SDO 規(guī)范2.1版本
原文網(wǎng)址:http://dev2dev.bea.com/blog/pinaki.poddar/archive/2007/07/persisting_serv.html
作者簡介 | |
Pinaki Poddar 目前在BEA Systems負責J2EE Persistence Services。15年來,他為Versant Object數(shù)據(jù)庫開發(fā)了基于Java的持久性解決方案,并為金融(Dresdner銀行)和醫(yī)療衛(wèi)生(Siemens Medical)行業(yè)開發(fā)中間件基礎架構。他還是OpenAdaptor(第一個開源應用集成框架)的捐獻者。在業(yè)余時間,他研究基于神經(jīng)網(wǎng)絡的自動語音識別技術。目前他對用于大型數(shù)據(jù)庫和復雜事件處理的商業(yè)智能比較感興趣。 |
![]() | ![]() | ![]() |