为了正常的体验网站,请在浏览器设置里面开启Javascript功能!

Google App Engine for Java,第 3 部分 持久性和关系

2010-11-03 9页 pdf 171KB 54阅读

用户头像

is_306059

暂无简介

举报
Google App Engine for Java,第 3 部分 持久性和关系 Google App Engine for Java,第 3 部分: 持久性和 关系 基于 Java 的持久性和 Google App Engine 数据存储 Richard Hightower, CTO, Mammatus Inc. 简介: 在企业环境中,数据持久性是交付可伸缩应用程序的基础。Rick Hightower 在他撰写的有关 Google App Engine for Java™ 的系列文章的最后一篇中,介绍了 App Engine 当前基于 Java 的持久性框 架。让我们学习一些基础知识,了解为...
Google App Engine for Java,第 3 部分 持久性和关系
Google App Engine for Java,第 3 部分: 持久性和 关系 基于 Java 的持久性和 Google App Engine 数据存储 Richard Hightower, CTO, Mammatus Inc. 简介: 在企业环境中,数据持久性是交付可伸缩应用程序的基础。Rick Hightower 在他撰写的有关 Google App Engine for Java™ 的系列文章的最后一篇中,介绍了 App Engine 当前基于 Java 的持久性框 架。让我们学习一些基础知识,了解为什么当前预览版中的 Java 持久性还未到发布的最佳时间,同时 获得一个良好的演示,看看您如何在 App Engine for Java 应用程序中保存数据。注意,您将需要启动并 运行来自 第 2 部分 的联系人管理应用程序,在此过程中学习如何使用 JDO API 保存、查询、更新和 删除 Contact 对象。 发布日期: 2009 年 9 月 14 日 级别: 初级 其他语言版本: 英文 访问情况 18 次浏览 建议: 0 (添加评论) 平均分 (共 0 个评分 ) App Engine for Java 力求为可伸缩的 Web 应用程序成功地编写一个持久层,可这个目标的达成情况又 如何呢?在本文中,我将概述 App Engine for Java 的持久性框架,从而结束本系列文章。该框架以 Java Data Objects(JDO)和 Java Persistence API(JPA)为基础。尽管在刚刚出现时前景良好,但是 App Engine 的基于 Java 的持久性目前存在一些严重的缺陷,我将对此进行解释和演示。您将学习 App Engine for Java 持久性是如何运作以及有着哪些挑战,还将学习在使用面向 Java 开发人员的 Google 云 平台时,您具有哪些持久性选择。 云计算空间 您是否希望随时获取最新的云计算消息?是否想得到云计算相关的技术知识?developerWorks 云计算空 间就是这样一个云计算信息资源的门户,在这里您可以了解来自 IBM 和业界其他媒体的最新信息,并 且得到如何在云环境中使用 IBM 软件的入门知识。 IBM 在 Amazon EC2 云计算环境中提供了 DB2、Informix、Lotus、WebSphere 等方面的 AMI 镜像资 源。您只需按使用量支付少量费用,就可以使用到云上的数据、门户、Web 内容管理、情景应用等服 务。欢迎您随时访问 云计算空间,获取更多信息。 在阅读本文并遍览这些示例时,您要牢记这样的事实:现在的 App Engine for Java 是一个预览 版。基 于 Java 的持久性目前也许并不是您所希望或者需要的全部,可能并且应该会在未来发生变化。现如 今,使用 App Engine for Java 进行可伸缩的、数据密集型的 Java 应用程序开发不合适胆小者或者保守 派,这就是我在撰写本文时所学到的。这更像跳入了游泳池的最深处:看不到任何救生员,项目要沉下 去还是往前游,取决于您自己。 注意,本文中的示例应用程序以 第 2 部分 中开发的联系人管理应用程序为基础。您需要构建该应用 程序,确保它是可运行的,这样才能继续学习本文的示例。 基础知识和抽象泄漏(leaky abstraction) 与原始的 Google App Engine 一样,App Engine for Java 依靠 Google 的内部基础设施,实现可伸缩的应 用程序开发的 Big Three:分布、复制和负载均衡。由于使用的是 Google 基础设施,因此所有神奇的 地方大都发生在后台,可以通过 App Engine for Java 的基于标准的 API 获得。数据存储接口是以 JDO 和 JPA 为基础的,而它们自身又是以开源的 DataNucleus 项目为基础。AppEngine for Java 还提供了一 个低级别的适配器 API,用来直接处理基于 Google 的 BigTable 实现的 App Engine for Java 数据存储 页码,1/9(W)w 2010/10/28http://www.ibm.com/developerworks/cn/java/j-gaej3.html (要了解更多有关 BigTable 的信息,请参见 第 1 部分)。 然而,App Engine for Java 数据持久性并不像纯 Google App Engine 中的持久性那样简单。由于 BigTable 不是一个关系数据库,JDO 和 JPA 的接口出现了一些抽象泄漏。例如,在 App Engine for Java 中,您无法进行那些执行连接的查询。您可以在 JPA 和 JDO 间设置关系,但它们只能用来持久化 关系。并且在持久化对象时,如果它们在相同的实体群中,那么它们只能被持久化到相同的原子事务 中。根据惯例,具有所有权的关系位于与父类相同的实体群中。相反,不具有所有权的关系可以在不同 的实体群中。 重新考虑数据化 要使用 App Engine 的可伸缩的数据存储,需要重新考虑有关规范化数据的优点的教导。当然,如果您 在真实的环境中工作了足够长的时间,那么,您可能已经为了追求性能而牺牲过规范化了。区别在于, 在处理 App Engine 数据存储时,您必须尽早且经常进行反规范化。反规范化 不再是一个忌讳的字眼, 相反,它是一个工具,您可以把它应用在 App Engine for Java 应用程序的许多方面。 当您尝试把为 RDBMS 编写的应用程序移植到 App Engine for Java 时,App Engine for Java 的持久性泄 漏的主要缺陷就会显露出来。App Engine for Java 数据存储并不是关系数据库的临时替代物,因此,要 把您对 App Engine for Java 所做的工作移植到 RDBMS 端口并不容易。采用现有的模式并把它移植到 数据存储中,这种场景则更为少见。如果您决定把一个遗留的 Java 企业应用程序移植到 App 引擎中, 建议您要小心谨慎,并进行备份。Google App Engine 是一个针对专门为它设计的应用程序的平 台。Google App Engine for Java 支持 JDO 和 JPA,这使得这些应用程序能够被移植回更传统的、未进 行规范化的企业应用程序。 关系的问 App Engine for Java 目前的预览版的另外一个缺点是它对关系的处理。为了创建关系,现在您必须对 JDO 使用 App Engine for Java 特有的扩展。假设键是在 BigTable 的工件的基础上生成 — 也就是说, “主键” 将父对象键编码到其所有子键中 — 您将不得不在一个非关系数据库中管理数据。另外一个 限制是持久化数据。如果您使用非标准的 AppEngine for Java Key 类,事情将会变得复杂。首先,把模 型移植到 RDBMS 时,如何使用非标准 Key? 其次,由于无法使用 GWT 引擎转换 Key 类,因此,任 何使用这个类的模型对象都无法被作为 GWT 应用程序的一部分进行使用。 当然,撰写这篇文章时,Google App Engine for Java 还是纯粹的预览模式,没有到发布的最佳时间。学 习 JDO 中的关系文档(很少,而且包含一些不完整的示例)时,这点就变得显而易见了。 App Engine for Java 开发包提供了一系列的示例程序。许多示例都使用 JDO,没有一个使用 JPA。这些 示例中没有一个示例(包括一个名为 jdoexamples 的示例)演示了关系,即使是简单的关系。相反,所 有的示例都只使用一个对象把数据保存到数据存储中。Google App Engine for Java 讨论组 充斥着有关 如何使简单关系起作用的问题,但却鲜有答案。很显然,有些开发人员有办法使其起作用,但是实现起 来都很困难,而且遇到了一些复杂情况。 App Engine for Java 中的关系的底线是,无需从 JDO 或 JPA 获得大量支持就能够管理它们。 Google 的 BigTable 是一种已经经过检验的技术,可用来生成可伸缩的应用程序,然而,您还可以在此基础上 进行构建。在 BigTable 上进行构建,您就不必处理还不完善的 API 层面。另一方面,您只要处理一个 较低级别的 API。 App Engine for Java 中的 Java Data Objects 把传统的 Java 应用程序移植到 App Engine for Java 中,甚至是给出关系挑战,这些可能都没有什么意 义,然而,持久性场景还是存在的,这时使用这个平台就有意义了。我将使用一个可行的示例来结束本 文,您将体验 App Engine for Java 持久性是如何工作的。我们将以 第 2 部分 中建立的联系人管理应 用程序为基础,介绍如何添加支持,以使用 App Engine for Java 数据存储工具持久化 Contact 对象。 在前面的文章中,您创建了一个简单的 GWT GUI,对 Contact 对象进行 CRUD 操作。您定义了简单 的接口,如清单 1 所示: 页码,2/9(W)w 2010/10/28http://www.ibm.com/developerworks/cn/java/j-gaej3.html 清单 1. 简单的 ContactDAO 接口 接下来,创建一个模拟版本,与内存集合中的数据进行交互,如清单 2 所示: 清单 2. 模拟 DAO 的 ContactDAOMock 现在,使用与 Google App Engine 数据存储交互的应用程序替换模拟实现,看看会发生什么。在这个示 例中,您将使用 JDO 持久化 Contact 类。使用 Google Eclipse Plugin 编写的应用程序已经拥有了使用 JDO 所需的所有库。它还包含了一个 jdoconfig.xml 文件,因此,一旦对 Contact 类进行了注释,您就 已经准备好开始使用 JDO。 package gaej.example.contact.server; import java.util.List; import gaej.example.contact.client.Contact; public interface ContactDAO { void addContact(Contact contact); void removeContact(Contact contact); void updateContact(Contact contact); List listContacts(); } package gaej.example.contact.server; import gaej.example.contact.client.Contact; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; public class ContactDAOMock implements ContactDAO { Map map = new LinkedHashMap(); { map.put("rhightower@mammatus.com", new Contact("Rick Hightower", "rhightower@mammatus.com", "520-555-1212")); map.put("scott@mammatus.com", new Contact("Scott Fauerbach", "scott@mammatus.com", "520-555-1213")); map.put("bob@mammatus.com", new Contact("Bob Dean", "bob@mammatus.com", "520-555-1214")); } public void addContact(Contact contact) { String email = contact.getEmail(); map.put(email, contact); } public List listContacts() { return Collections.unmodifiableList(new ArrayList(map.values())); } public void removeContact(Contact contact) { map.remove(contact.getEmail()); } public void updateContact(Contact contact) { map.put(contact.getEmail(), contact); } } 页码,3/9(W)w 2010/10/28http://www.ibm.com/developerworks/cn/java/j-gaej3.html 清单 3 显示扩展后的 ContactDAO 接口,可使用 JDO API 进行持久化、查询、更新和删除对象: 清单 3. 使用 JDO 的 ContactDAO package gaej.example.contact.server; import gaej.example.contact.client.Contact; import java.util.List; import javax.jdo.JDOHelper; import javax.jdo.PersistenceManager; import javax.jdo.PersistenceManagerFactory; public class ContactJdoDAO implements ContactDAO { private static final PersistenceManagerFactory pmfInstance = JDOHelper .getPersistenceManagerFactory("transactions-optional"); public static PersistenceManagerFactory getPersistenceManagerFactory() { return pmfInstance; } public void addContact(Contact contact) { PersistenceManager pm = getPersistenceManagerFactory() .getPersistenceManager(); try { pm.makePersistent(contact); } finally { pm.close(); } } @SuppressWarnings("unchecked") public List listContacts() { PersistenceManager pm = getPersistenceManagerFactory() .getPersistenceManager(); String query = "select from " + Contact.class.getName(); return (List) pm.newQuery(query).execute(); } public void removeContact(Contact contact) { PersistenceManager pm = getPersistenceManagerFactory() .getPersistenceManager(); try { pm.currentTransaction().begin(); // We don't have a reference to the selected Product. // So we have to look it up first, contact = pm.getObjectById(Contact.class, contact.getId()); pm.deletePersistent(contact); pm.currentTransaction().commit(); } catch (Exception ex) { pm.currentTransaction().rollback(); throw new RuntimeException(ex); } finally { pm.close(); } } public void updateContact(Contact contact) { PersistenceManager pm = getPersistenceManagerFactory() .getPersistenceManager(); String name = contact.getName(); String phone = contact.getPhone(); String email = contact.getEmail(); try { pm.currentTransaction().begin(); // We don't have a reference to the selected Product. 页码,4/9(W)w 2010/10/28http://www.ibm.com/developerworks/cn/java/j-gaej3.html 逐一比对方法 现在,考虑一下使用清单 3 中的每个方法时发生的情况。您将会发现,方法的名字可能是新的,但它 们的动作大部分情况下都应该感到熟悉。 首先,为了获取 PersistenceManager,创建了一个静态的 PersistenceManagerFactory。如果您以前 使用过 JPA,PersistenceManager 与 JPA 中的 EntityManager 很相似。如果您使用过 Hibernate, PersistenceManager 与 Hibernate Session 很相似。基本上,PersistenceManager 是 JDO 持久性系统 的主接口。它代表了与数据库的会话。getPersistenceManagerFactory() 方法返回静态初始化的 PersistenceManagerFactory,如清单 4 所示: 清单 4. getPersistenceManagerFactory() 返回 PersistenceManagerFactory addContact() 方法把新的联系人添加到数据存储中。为了做到这点,需要创建一个 PersistenceManager 实例,然后,调用 PersistenceManager 的 makePersistence() 方法。 makePersistence() 方法采用临时的 Contact 对象(用户将在 GWT GUI 中填充),并且使其成为一个 持久的对象。所有这些如清单 5 所示: 清单 5. addContact() 注意在清单 5 中,persistenceManager 是如何被封入在 finally 块中。这确保能够把与 persistenceManager 关联的资源清除干净。 如清单 6 所示,listContact() 方法从它所查找的 persistenceManager 中创建一个查询对象。它调用 了 execute() 方法,从数据存储中返回 Contact 列表。 // So we have to look it up first, contact = pm.getObjectById(Contact.class, contact.getId()); contact.setName(name); contact.setPhone(phone); contact.setEmail(email); pm.makePersistent(contact); pm.currentTransaction().commit(); } catch (Exception ex) { pm.currentTransaction().rollback(); throw new RuntimeException(ex); } finally { pm.close(); } } } private static final PersistenceManagerFactory pmfInstance = JDOHelper .getPersistenceManagerFactory("transactions-optional"); public static PersistenceManagerFactory getPersistenceManagerFactory() { return pmfInstance; } public void addContact(Contact contact) { PersistenceManager pm = getPersistenceManagerFactory() .getPersistenceManager(); try { pm.makePersistent(contact); } finally { pm.close(); } } 页码,5/9(W)w 2010/10/28http://www.ibm.com/developerworks/cn/java/j-gaej3.html 清单 6. listContact() 在从数据存储中删除联系人之前,removeContact() 通过 ID 查找联系人,如清单 7 所示。它必须这么 做,而不仅仅是把联系人直接删除,这是因为来自 GWT GUI 的 Contact 对 JDO 一无所知。在删除 前,您必须获得与 PersistenceManager 缓存关联的 Contact。 清单 7. removeContact() 清单 8 中的 updateContact() 方法与 removeContact() 方法类似,用来查找 Contact。然后, updateContact() 方法从 Contact 中复制属性。这些属性被当作实参(Argument)传送到 Contact,后 者由持久性管理器查找。使用 PersistenceManager 检查所查找的对象发生的变化。如果对象发生了变 化,当事务进行提交时,这些变化会被 PersistenceManager 刷新到数据库。 清单 8. updateContact() @SuppressWarnings("unchecked") public List listContacts() { PersistenceManager pm = getPersistenceManagerFactory() .getPersistenceManager(); String query = "select from " + Contact.class.getName(); return (List) pm.newQuery(query).execute(); } public void removeContact(Contact contact) { PersistenceManager pm = getPersistenceManagerFactory() .getPersistenceManager(); try { pm.currentTransaction().begin(); // We don't have a reference to the selected Product. // So we have to look it up first, contact = pm.getObjectById(Contact.class, contact.getId()); pm.deletePersistent(contact); pm.currentTransaction().commit(); } catch (Exception ex) { pm.currentTransaction().rollback(); throw new RuntimeException(ex); } finally { pm.close(); } } public void updateContact(Contact contact) { PersistenceManager pm = getPersistenceManagerFactory() .getPersistenceManager(); String name = contact.getName(); String phone = contact.getPhone(); String email = contact.getEmail(); try { pm.currentTransaction().begin(); // We don't have a reference to the selected Product. // So we have to look it up first, contact = pm.getObjectById(Contact.class, contact.getId()); contact.setName(name); contact.setPhone(phone); contact.setEmail(email); pm.makePersistent(contact); pm.currentTransaction().commit(); } catch (Exception ex) { pm.currentTransaction().rollback(); throw new RuntimeException(ex); } finally { pm.close(); 页码,6/9(W)w 2010/10/28http://www.ibm.com/developerworks/cn/java/j-gaej3.html 对象持久性注释 为了使 Contact 能够具有持久性,必须把它识别为一个具有 @PersistenceCapable 注释的可持久性对 象。然后,需要对它所有的可持久性字段进行注释,如清单 9 所示: 清单 9. Contact 具有可持久性 } } package gaej.example.contact.client; import java.io.Serializable; import javax.jdo.annotations.IdGeneratorStrategy; import javax.jdo.annotations.IdentityType; import javax.jdo.annotations.PersistenceCapable; import javax.jdo.annotations.Persistent; import javax.jdo.annotations.PrimaryKey; @PersistenceCapable(identityType = IdentityType.APPLICATION) public class Contact implements Serializable { private static final long serialVersionUID = 1L; @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) private Long id; @Persistent private String name; @Persistent private String email; @Persistent private String phone; public Contact() { } public Contact(String name, String email, String phone) { super(); this.name = name; this.email = email; this.phone = phone; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } 页码,7/9(W)w 2010/10/28http://www.ibm.com/developerworks/cn/java/j-gaej3.html 通过面向对象的编程和接口设计原则,您只需使用新的 ContactJdoDAO 替代原始的 ContactDAOMock。 然后 GWT GUI 无需任何修改就可处理 JDO。 最后,在这种替换中,真正改变 的是 DAO 在服务中被实例化的方式。如清单 10 所示: 清单 10. RemoteServiceServlet 结束语 在这篇由三部分组成的文章中,介绍了 Google App Engine for Java 目前为持久性提供的支持,这是交 付可伸缩应用程序的基础。总的结论令人失望,但是要注意这是一个正在发展中的平台。为 App Engine for Java 预览版编写的应用程序被连接到 App Engine 的持久性基础设施,即使是用 JDO 或 JPA 编写。App Engine for Java 预览版几乎没有为它的持久性框架提供任何文档,而且 App Engine for Java 提供的示例几乎无法演示即使是最简单的关系。 即使 JDO 和 JPA 实现已经完全成熟,目前您仍然不可能编写一个 App Engine for Java 应用程序并轻松 地把它移植到一个基于 RDBMS 的企业应用程序。要使移植能够起作用,至少要编写大量的代码。 我希望持久性能随着时间的推移而成熟起来。如果现在必须使用 App Engine for Java,您可能需要绕过 Java API,直接编写低级别的 Datastore API。使用 App Engine for Java 平台是可能的,但是,如果习惯 了使用 JPA 和/或 JDO,那么将出现一条学习曲线,因为存在本文前面描述的抽象泄漏,并且目前的功 能要么还无法正常运行,要么还没有进行很好的文档记录。 参考资料 学习 l “Google App Engine for Java:第 1 部分:运转起来!”(Rick Hightower,developerWorks, August 2009 年 8 月):开始使用 Google Plugin for Eclipse,并了解如何快速地构建可伸缩的简单 应用程序。 l “Google App Engine for Java:第 2 部分:构建杀手级应用程序”(Rick Hightower, developerWorks,August 2009 年 8 月):增强您对 App Engine 的兴趣,通过这个简短的教程, 为联系人管理应用程序构建一个定制的 GWT GUI。这一部分是完成本文练习的前提条件。 l “连接到云,第 1 部分:在应用程序中使用云”(Mark O'Neill,developerWorks,2009 年 4 月):一份介绍来自 Amazon、Google、Microsoft® 和 SalesForce.com 三家主要供应商的云计算 平台的概述,共包含 3 部分。 l Google IO 视频课程:介绍模式的温柔一面:Google 工程师 Max Ross 解释了 JDO 和 JPA 标准如 何与 Google App Engine 数据存储进行交互。 l JDO Tutorial,the Apache DB Project:学习如何在一个简单的应用程序场景中使用 public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } } public class ContactServiceImpl extends RemoteServiceServlet implements ContactService { private static final long serialVersionUID = 1L; //private ContactDAO contactDAO = new ContactDAOMock(); private ContactDAO contactDAO = new ContactJdoDAO(); ... 页码,8/9(W)w 2010/10/28http://www.ibm.com/developerworks/cn/java/j-gaej3.html ObjectRelationalBridge 和 JDO API。 l 什么是 BigTable?:阅读 Google 研究性出版物找到答案。 l The DataNucleus Project:一个开放源码项目,提供一系列关于使用 Java 编程语言进行数据管理 的软件产品;以前的名称为 Java Persistent Objects(JPOX)。 l Google App Engine 主页:了解有关 App Engine 的更多信息。 l Google App Engine Java 文档:更多使用 App Engine for Java 进行 Java 开发的信息。 l Will it play in App Engine?:获得与 App Engine 兼容的标准 Java API 和框架。 l 在 技术店 浏览关于这些主题和其他技术主题的图书。 l developerWorks Java 技术专区:在这里可以找到数百篇关于 Java 编程的各个方面的文章。 获得产品和技术 l Google Plugin for Eclipse:从下载插件开始入手。 l Google App Engine 帐户:您需要一个帐户来使用 Google App Engine 基础设施部署您的杀手级应 用程序。 讨论 l Google App Engine for Java 讨论组:订阅这个讨论组,以便在学习 Google App Engine 时提供和接 受帮助与建议。 l 加入 My developerWorks 社区。 关于作者 Rick Hightower 是 Mammatus Inc. 的首席技术官,Mammatus Inc. 是一家从事云计算、GWT、Java EE、 Spring 和 Hibernate 开发的培训公司。他是畅销书 Java Tools for Extreme Programming 的合著者,并且 撰写了 Struts Live 的第一版 — 该书多年来在 TheServerSide.com 上的下载次数一直位列第一。他还为 IBM developerWorks 撰写文章和教程,并且是 Java Developer's Journal 的编委会的成员,他还经常在 DZone 上针对 Java 和 Groovy 主题发表文章。 商标 | My developerWorks 使用条款与条件 页码,9/9(W)w 2010/10/28http://www.ibm.com/developerworks/cn/java/j-gaej3.html
/
本文档为【Google App Engine for Java,第 3 部分 持久性和关系】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。 本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。 网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。

历史搜索

    清空历史搜索