技术文章-使用OpenJPA持久存储服务数据对象:放宽类型

来源:百度文库 编辑:神马文学网 时间:2024/04/27 17:54:21
使用OpenJPA持久存储服务数据对象:放宽类型
时间:2007-11-09
作者:Pinaki Poddar
浏览次数: 259
本文关键字:JPA, SDO, Tuscany, OpenJPA, Persistence, Weblogic, 持久性, ALDSP, AquaLogic, Eclipse, EMF 文章工具
 推荐给朋友
 打印文章

服务数据对象是一种流动数据
服务数据对象(Service Data Object,SDO)介于强类型POJO和非验证XML流中间。SDO使用户应用程序能够使用可动态创建的数据结构。当数据必须在没有(或不能)共享相同的严格类型定义的环境(例如编译后的POJO类集合)中传播时,SDO这种灵活的动态数据结构就显得尤为重要。可以将它们比作静态(POJO)和动态(SDO)——由于这种比喻——很明显,SDO是SOA的重要组成部分。这就是为什么所有企业的IT部门内都存在不同形式的‘动态数据结构’主题。怀疑论者会说:这不就是名称值对吗?但是SDO远远超越了所谓的名称值对。例如,该规范认为数据(例如人员)是通过各种关系而富有意义,并解释了DataGraph的概念。还详细说明了如何通过ChangeSummary跟踪DataGraph的变化。更多详细内容请参阅参考资料 部分。
使用JPA持久性存储SDO
SDO是关于动态数据的高级规范。但是不具备持久性的数据是什么样的呢?我们如何将SDO DataGraph存储到数据库中,以及如何从数据库中检索它呢?最初的SDO规范(2004年11月发布)提到了Data Mediator Service但是没有给出其定义。最近再次查阅规范时,规范已经发展到了2.1版本,并且持久存储服务现在被称为数据访问服务,即DAS,但是仍然没有详细说明其细节。
在同一时期内,Java Persistence Architecture (JPA)已发展成熟,而且一些领先的应用程序服务供应商采用它作为其持久性提供者。BEA使用了Kodo,JBoss使用Hibernate,而OracleAS和GlassFish使用了Toplink。OpenJPA——Kodo的开源版本——很可能用于Geronimo和Websphere环境。自然,JPA是为SDO提供持久性存储服务的重要备选方案。

在Google中快速搜索一下“JPA”、“SDO”和“JPA+SDO”,分别返回397万、342万和16万的命中率。事实上,这些反应SDO和JPA讨论次数的数字可转换成左侧的图表。
我研究的第一个项目是TuscanyDAS。我大致查看了其中可用 的内容,但是似乎没有使用JPA,至少目前是这样。有关论坛一直在讨论 JPA的使用,但是其结果尚未报道出来。
下一个项目是ALDSP。这是来自BEA的较成熟的产品,它使用的是SDO。然而,ALDSP也没有使用基于JPA的持久性服务。
Eclipse EMF是首先具体实现SDO的项目之一。然而,EMF也没有实现持久性服务(SDO规范并没有向其授权)。
为什么JPA是持久性存储SDO的自然选择?
JPA解决了O-R映射中最棘手的问题,不过需要注意一些警告 标志。它还为持久性存储提供了一个简单的、设计良好的API,这个API通过实现EJB 3.0很好地证明了自己。除了已经证实的完善性,JPA逐渐成为为SDO提供持久性服务的自然选择,这是因为JPA还具有大量能与SDO良好结合的特性。
首要特性是断开或分离操作模式——在这种模式中,用户应用程序连接到数据库并从中取回数据后立刻释放连接。数据作为相关的记录集被应用程序查看——POJO图形或DataGraph——看您喜好哪种术语了。数据可以在远程进程中修改,而不需要对数据库的原始数据建立活动连接。稍后,当重新建立数据库连接时,这些修改可以合并到持久性数据中。这种断开操作模式对可伸缩性非常关键,例如10000个活动用户可以同时操作包含十个数据库连接的连接池。这种应用程序使用模式同样节省了大量的金钱,因为随着连接数量的增加,数据库安装开销将呈指数级增长。JAP支持这种断开操作模式,当持久性存储上下文关闭或事务提交后,取回的POJO实例将断开连接,并且可以远程修改断开的对象图并稍后合并到不同的持久性存储上下文中。另一方面,SDO定义了ChangeSummary概念,用于跟踪断开的DataGraph中的改动。因此,JPA实现自然可以使用ChangeSummary内容将改动有效地并入到数据库中。
JAP和SDO自然结合的另一点是:它们都使用取回的数据作为连接对象图。JPA目前还没有充分开发定义断开数据图的潜力,但是OpenJPA或Kodo支持一种名为FetchPlan的强大扩展——来自于JDO规范的贡献,该规范良好定义了这一概念,即指定从数据库取回对象闭包的哪一部分。这种控制非常重要,因为完整的实例闭包通常就是整个数据库。另一方面,SDO DataGraph定义了一种经过配置的闭包,即所谓的根DataObject。
使用JPA持久性存储SDO的关键挑战是什么?
需要解决的关键分歧是类型系统。JPA是针对严格类型的POJO设计的。JPA实现管理的对象实例是由Java类静态定义的,并由Java编译器进行编译。JPA(及其前身JDO)的美妙之处在于:这些实现能够在不产生干扰的情况下截取对持久性实例的改变(状态和/或关系)。对象仍然保持POJO特性,但JPA运行时可以知道用户应用程序何时会对持久性对象调用setter方法以修改其状态,或何时调用要求从数据库取回更多日期(通常称为Lazy Loading)的getter方法。实际的实现会根据截取改动的方式而有所变化——例如——OpenJPA或Kodo取决于增强——该过程将修改编译后的持久性Java类的字节码,而其他实现可能会使用不同的机制,例如代理原始实体。无论哪种情况,都需要对实现作出较大的改动,以脱离其基本假设。例如,存在编译后表示持久性数据的Java类。
另一方面,SDO倾向于松散类型的对象。SDO可以使用名称和年龄表示一个Person对象,而不需要定义一个Person.java类。Person的定义可存在于一个XML模式文档中,或者甚至可以通过编程的方式创建。
那么如何使用JPA存储一个Person DataObject而不用编写Person.java类呢?理论上讲,JPA怎么能采用不是强类型的数据表示呢?
类型转换系统仅仅是开始。还有很多问题要解决:
如何定义实例的身份? 如何定义到数据库模式的映射? 如何使用Java表示SDO类型之间的关系? 如何将断开的DataGraph的改动转换为Java实例的更新?
如何放宽JPA限制以使用松散类型数据?
为了不被答案不是特别明显的问题混淆,我决定逐步执行一些步骤。因此我尝试解决几个简单场景或用例。
用例:通过JPA API将SDO Types/DataObjetcs转化为持久性数据

该场景显示了一个客户如何持久存储SDO对象。下面列出了给定的输入:
a) 一个XML模式,定义一组SDO类型
b) 一个JPA配置,用于支持SDO的JPA运行时。
应用程序将调用SDO API定义SDO类型,然后将创建并填充一组相关的DataObjects,例如使用这些SDO类型的DataGraph。这个DataGraph将被提供给JPA API。支持SDO的JPA运行时将执行把数据图存储到RDBMS所需的工作。事实上,我们首先将SDO类型和对象作为输入,然后使用JPA实现持久性数据记录。
另一个场景正好相反。我们首先从持久性数据开始。我们通过JPA API使用Java Persistence Query Language (JPQL)执行一次查询。通常,该查询将产生一个Java实例列表——但是支持SDO的Java运行时会将SDO DataObjects返回给用户应用程序。
用例#2:通过JPQL查询将持久性数据转换为SDO Types/DataObjects

如何实现这种支持SDO的JPA运行时?其中一种方法是将SDO放置(overlay)在JPA之上。JPA API将接受SDO DataObject作为参数并将DataObject作为查询结果返回,但是SDO负责将DataObject实例转换为一个具体的Java实例,反之亦然。我决定沿这种思路继续深入研究,看看还能做些什么。
编写代码之前先进行测试
受测试驱动开发的启发,我首先编写了一个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()方法创建了一个SDO DataGraph,该方法用于返回根DataObject。这个根实例将作为EntityManager.persist()的输入。persist()操作必须将所有可从根实例中获得的DataObject存储到数据库中。
第二个测试用例使用Java Persistence Query Language (JPQL)查询数据库。查询结果为DataObject。必须取回查询得到的实例及其相关实例,因为我们要检验相关的USAddress DataObject shipTo是否将名称属性设置为Alice Smith,因为这是在createPurchaseOrder()方法中创建DataGraph时所设置的内容。
测试用例显示用户应用程序将调用标准的JPA EntityManager.persist()方法来存储实例,但是其参数为commonj.sdo.DataObject的实例。类似地,查询将生成一个DataObject实例列表,而不是强类型PurchaseOrderType.class的实例。
创建支持SDO的JPA运行时
经过几天的研究、下载并使用Google搜索,然后又花费了几天时间进行编码,我编写了一些类,将它们放入一个jar文件中并运行我的两个测试用例。测试成功。在下一篇文章中,我将讨论如何为SDO启用JPA运行时。我将使用几个便捷的步骤(但仍属于基本步骤),帮助希望扩展JPA进行其他应用的人员。
庆幸的是不需要对作为JPA实现的OpenJPA做任何改变——这展示了OpenJPA的不凡之处——它针对可扩展性进行了设计(与随意修改相反)。并且我已经开始订阅Tuscany用户组——我使用了它们的SDO实现作为实验原型——这个用户组非常活跃,我的邮件箱立刻填满了邮件。但是所有这些需要改天再讨论了……
[1]这篇最近的文章 详细介绍了SDO及其在SOA中的使用。
[2]完整的SDO 规范2.1版本
原文网址:http://dev2dev.bea.com/blog/pinaki.poddar/archive/2007/07/persisting_serv.html

作者简介
Pinaki Poddar 目前在BEA Systems负责J2EE Persistence Services。15年来,他为Versant Object数据库开发了基于Java的持久性解决方案,并为金融(Dresdner银行)和医疗卫生(Siemens Medical)行业开发中间件基础架构。他还是OpenAdaptor(第一个开源应用集成框架)的捐献者。在业余时间,他研究基于神经网络的自动语音识别技术。目前他对用于大型数据库和复杂事件处理的商业智能比较感兴趣。



作者其它文章插入式持久性提供者的承诺:Kodo、OpenJPA和Hibernate从EJB 3到AJAX从EJB 3到AJAX:第2部分——通过DWR集成Java与JavaScript将EJB 3应用程序迁移到GlassFish如何使用EJB 3和JPA编程模型加速和简化多层Web应用程序开发