Java开拓2.0: 利用Amazon SimpleDB实现云存储,第2部门
副标题#e#
Java开拓2.0: 利用Amazon SimpleDB实现云存储,第2部门:利用SimpleJPA实现简朴工具耐久化
利用诸如 Grails 的干系框架对险些所有范例的应用举办域工具建模是很容易的,可是利用 SimpleDB 又怎么样呢?在 Andrew Glover 的先容 SimpleDB 的系列文章的第 2 部门,他向您先容了如何利用 SimpleJPA,而非 Amazon SDK,在 SimpleDB 的云存储中实现工具耐久化。除了使您可以或许利用简朴 Java™ 工具举办域建模(通过 JPA)之外,SimpleJPA 还可以或许自动地将根基数据范例转换成兼容 Amazon 的字符串。您确实找不到比这更简朴的云存储要领了。
在先容 SimpleDB 文章的 第一部门 中,我向您先容了如何利用 Amazon 自己的 API 举办一个 CRUD 网络的赛跑应用的建模。除了对大大都 Java 开拓人员而言,Amazon 只利用字符串来描写数据范例的要领的明明奇特性之外,您大概发明本身对付 Amazon API 尚有一些疑虑。究竟,此刻利用干系数据库的 API 已经很是尺度且成熟了 — 并且更重要的是,他们已经很熟悉这些技能了。
除此之外,此刻有很多干系框架实现了 Java Persistence API。因此为各类 RDBMS 举办各类范例的 Java 应用举办域工具建模都长短常容易和常见的。当您已经把握了一种要领之后,很自然您会对付进修新的域工具建模要了解有一些抵触 — 而好动静是利用 SimpleDB 时,您不需要进修新对象。
在 SimpleDB 文章的第 2 部门中,我将向您先容如何重构第 1 部门的赛跑应用,使之切合 JPA 类型。然后我们将把应用移植到 SimpleJPA,而且探讨一些可以或许使这个创新的开放源码平台颠末调解而支持 NoSQL 域建模和基于云的存储的要领,这一样很简朴。
为什么利用 SimpleDB?
Amazon 的 SimpleDB 是一个简朴且极具可扩展性和靠得住性的基于云的数据存储要领。由于它本质上长短干系/NoSQL,SimpleDB 既机动又快速。作为 Amazon Web Service 家属的一部门,SimpleDB 利用 HTTP 作为底层通信机制,所以它可以或许支持多种语言,包罗 Java 语言、Ruby、C# 和 Perl。SimpleDB 价值也很自制:按照 SimpleDB 的授权方法,您只需要为您利用的资源付出用度,这跟按照估量利用和空间预先购置授权的传统要领很纷歧样。作为新兴的 NoSQL,或非干系数据存储的一部门,SimpleDB 是与 Google 的 Bigtable 或 CouchDB 相对应的,它们在 这些系列文章中 有相应的先容。
Hibernate 和 JPA:配景轮廓
此刻有许很多多的 Java 开拓人员都利用 Hibernate(和 Spring)实现数据耐久化。除了是最先乐成的开放源码项目,Hibernate 也彻底改变了 ORM 规模。在呈现 Hibernate 之前,Java 开拓人员必需处理惩罚巨大的 EJB 实体 Bean;而在这之前,我们只能本身实现 ORM 可能利用来自诸如 IBM® 等供给商的产物。Hibernate 去掉了 EJB 的所有巨大性和开销,转而利用我们此刻很多人都利用的基于 POJO 的建模平台。
Java Persistence API (JPA) 是由于 Hibernate 创新地利用 POJO 举办数据建模要领的风行而呈现的。此刻,EJB 3.0 实现了 JPA,Google App Engine 也一样实现了 JPA。甚至假如您利用 Hibernate EntityManager,那么 Hibernate 自己也是一个 JPA 实现,
既然 Java 开拓人员已经越来越熟悉利用 POJO 对以数据为中心的应用举办建模,那么可以说,SimpleDB 这样一个数据存储应该可以或许给我们提供一个雷同的选项。究竟,它与数据库有些相似,不是吗?
用工具举办数据建模
要利用 SimpleJPA,我们需要修改一下我们的 Racer 和 Runner 工具,使它们切合 JPA 类型。幸好,JPA 根基要素是很简朴的:给泛泛的 POJO 加上注释,而 EntityManager 实现会认真完成其他处理惩罚 — 不需要 XML。
JPA 所利用的两个主要的注释是 @Entity 和 @Id,这两个注释别离将一个 POJO 指定为耐久化类,同时确定它的标识键。为了将我们的赛跑应用转换为 JPA,我们也将利用别的两个打点干系的注释:@OneToMany 和 @ManyToOne。
在本文的第 1 部门中,我已经向您先容了如何耐久化选手和角逐工具了。然而,我没有利用工具来暗示这些实体 — 我只是利用了 Amazon 的原始 API 来存储这两个工具的属性。假如我但愿对一个角逐和角逐选手的干系举办建模,那么我可以编写如清单 1 所示的代码:
清单 1. 一个简朴的 Race 工具
public class Race {
private String name;
private String location;
private double distance;
private List<Runner> runners;
//setters and getters left out...
}
在 清单 1 中,我给 Race 工具配置了 4 个属性,最后一个是一个选手 Collection。接下来,我可以建设一个简朴的 Runner 工具(如清单 2 所示),它包括每位选手的姓名(此刻我将只管保持简朴),与他/她所介入的 Race 实例相关的 SSN。
清单 2. 与 Race 相关的一个简朴的 Runner
#p#分页标题#e#
public class Runner {
private String name;
private String ssn;
private Race race;
//setters and getters left out...
}
您可以从 清单 1 和 2 看到,我在选手和角逐之间逻辑上成立了一个多对一的干系。在实际环境中,大概多对多干系更精确些(选手一般会介入多个角逐),可是这里这样做是为了简朴起见。别的,此刻我也忽略结构函数、setter 和 getter。我将在后头向您先容。
#p#副标题#e#
JPA 中的注释
要使这两个工具可以或许利用 SimpleJPA 并不是很难。首先,我需要通过为每个工具添加 @Entity 注释来将它们酿成可耐久化的。我也需要在 Race 工具中利用 @OneToMany,在 Runner 工具中利用 @ManyToOne 来正确界说它们的干系。
@Entity 注释是在类上标注的,而干系注释是在 getter 函数上标注的。这些注释见清单 3 和 4:
清单 3. JPA 注释的 Race
@Entity
public class Race {
private String name;
private String location;
private double distance;
private List<Runner> runners;
@OneToMany(mappedBy = "race")
public List<Runner> getRunners() {
return runners;
}
//other setters and getters left out...
}
在 清单 3 中,我利用 @OneToMany 注释来标注 getRunners 函数。我也在实例 Runner 的 race 属性上界说了一个干系。
在清单 4 中,我用雷同的要领注释了 Runner 工具的 getRace 函数。
清单 4. JPA 注释的 Runner
@Entity
public class Runner {
private String name;
private String ssn;
private Race race;
@ManyToOne
public Race getRace() {
return race;
}
//other setters and getters left out...
}
大大都数据存储(干系型或非干系型)都需要某种描写数据独一性的要领。所以假如我将这两个工具存储到数据存储中,我至少需要给它们添加 ID。在清单 5 中,我添加一个范例为 BigInteger 的 id 属性到 Race 域工具。在 Runner 工具中我会利用沟通的做法。
清单 5. 给 Race 添加一个 ID
@Entity
public class Race {
private String name;
private String location;
private double distance;
private List<Runner> runners;
private BigInteger id;
@Id
public BigInteger getId() {
return id;
}
@OneToMany(mappedBy = "race")
public List<Runner> getRunners() {
return runners;
}
//other setters and getters left out...
}
清单 5 中的 @Id 注释并没有关于 ID 是如何打点的信息。这样措施就会假设由我手动打点这个 ID,而不是利用 EntityManager 来打点。
进入 SimpleJPA
到此刻为止,我还没有举办任何 SimpleDB 的设置。Race 和 Runner 工具都只是利用 JPA 注释举办标注,从而可以存储在任何由 JPA 实现所支持的数据存储中。可选的存储方法包罗 Oracle、DB2、MySQL 和(您大概已经猜到的)SimpleDB。
SimpleJPA 是 Amazon 的 SimpleDB 的开源实现。固然它并不支持完整的 JPA 类型(譬喻,您不能连系 JPA 查询),可是它支持大量很有用的一部门 JPA 类型。
利用 SimpleJPA 的一个很大的利益是它可以或许无缝地处理惩罚我在 本文的第 1 部门 中所接头的按字母的问题。SimpleJPA 会对依赖于数字范例的工具举办字符串转换和后续的填充(假如需要)。在大大都环境中,这意味着您不需要修改您的域模子来利用 String 范例。(个中只有一个破例环境,我将在后头举办接头。)
因为 SimpleJPA 是一个 JPA 实现,您可以很容易在个中利用切合 JPA 的域工具。SimpleJPA 只要求您利用 String ID,这意味着您的 id 属性必需是 java.lang.String。为了简化,SimpleJPA 提供了根基的类 IdedTimestampedBase,它认真打点域工具的 ID 属性,以及日期属性 created 和 updated。(在底层, SimpleDB 会生成一个独一的 Id。)
将应用移植到 SimpleJPA
为了使 Race 和 Runner 类兼容 SimpleJPA,我可以扩展 SimpleJPA 便利基本类,可能将每一个类的 id 属性从 BigInteger 修改为 String。我选择了第一种要领,如清单 6 所示:
清单 6. 修改 Race 类为利用 SimpleJPA 的 IdedTimestampedBase 基本类
#p#分页标题#e#
@Entity
public class Race extends IdedTimestampedBase{
private String name;
private String location;
private double distance;
private List<Runner> runners;
@OneToMany(mappedBy = "race")
public List<Runner> getRunners() {
return runners;
}
//other setters and getters left out...
}
固然我不会向您显示 Runner 中沟通的代码,可是您可以随时查察这些代码:扩展 IdedTimestampedBase,并删除 Runner 的 id 属性。
修改 Race 和 Runner 的 ID 是使赛跑应用切合 SimpleJPA 类型的第一步。接下来,我需要将根基数据范例(如,double、int 和 float)转换为诸如 Integer 和 BigDecimal 的工具。
我将从修改 Race 的 distance 属性开始。我发明(在当前版本的 SimpleJPA 中)利用 BigDecimal 比 Double 更靠得住,所以我将 Race 的 distance 修改为 BigDecimal,如清单 7 所示:
清单 7. 将 distance 修改为 BigDecimal
@Entity
public class Race extends IdedTimestampedBase{
private String name;
private String location;
private BigDecimal distance;
private List<Runner> runners;
@OneToMany(mappedBy = "race")
public List<Runner> getRunners() {
return runners;
}
//other setters and getters left out...
}
此刻 Runner 和 Race 都已经可以通过 SimpleJPA 实现举办耐久化了。
利用 SimpleJPA 操纵 SimpleDB
利用 SimpleJPA 来处理惩罚您的域工具在 SimpleDB 中的存储与利用 JPA 实现举办普通的干系数据库存储不同不大。纵然您从未利用过 JPA 开拓应用,那么对付您来说它也不会有太大的坚苦。独一的新对象是要设置 SimpleJPA 的 EntityManagerFactoryImpl,这要求利用您的 Amazon Web Services 证书和您的 SimpleDB 域的前缀名。(另一个要领是在编译路径上增加一个包括您的证书的属性文件。)
在建设一个 SimpleJPA EntityManagerFactoryImpl 实例时利用您指定的前缀名,这样发生的 SimpleDB 域会由您的前缀,加一根横杠,再加域工具名称构成。所以,假如我指定 “b50” 为前缀,而我在 SimpleDB 中建设一个 Race,那么这个域将会是 “b50-Race”。
一旦您建设一个 SimpleDB EntityManagerFactoryImpl 实例,其他方面就由这个接口完成了。您需要利用一个 EntityManager 实例,这个实例是从 EntityManagerFactoryImpl 获取的,如清单 8 所示:
清单 8. 得到一个 EntityManager
Map<String,String> props = new HashMap<String,String>();
props.put("accessKey","...");
props.put("secretKey","..");
EntityManagerFactoryImpl factory =
new EntityManagerFactoryImpl("b50", props);
EntityManager em = factory.createEntityManager();
处理惩罚域工具
一旦您拥有了一个 EntityManager 工具,您就可按照需要处理惩罚域工具了。譬喻,我可以像下面一样建设一个 Race 实例:
清单 9. 建设一个 Race
Race race = new Race();
race.setName("Charlottesville Marathon");
race.setLocation("Charlottesville, VA");
race.setDistance(new BigDecimal(26.2));
em.persist(race);
在 清单 9 中,SimpleJPA 处理惩罚了所有 HTTP 请求来在云中建设 Race。利用 SimpleJPA 意味着我也可以或许利用一个 JPA 查询来查询角逐,如清单 10 所示。(记着您不可以或许连系这些查询,可是我仍然可以利用数字举办搜索。)
清单 10. 按照实例查找一个角逐
Query query = em.createQuery("select o from Race o where o.distance = :dist");
query.setParameter("dist", new BigDecimal(26.2));
List<Race> races = query.getResultList();
for(Race race : races){
System.out.println(race);
}
从数字到字符串
譬喻,SimpleJPA 内部的数字到字符串转换的要领长短常有用的,假如您在 SimpleJPA 中启用查询输出,那么您可以看到有哪些查询发送到 SimpleDB。所提交的查询如清单 11 所示。留意 distance 是如何编码的。
清单 11. SimpleJPA 很好地处理惩罚数字!
amazonQuery: Domain=b50-Race, query=select * from `b50-Race`
where `distance` = '0922337203685477583419999999999999928946'
自动填充和编码使开拓越发简朴,您不以为吗?
SimpleJPA 中的干系
#p#分页标题#e#
纵然 SimpleDB 不答允查询中举办域连系,您也仍然可以在域中利用关联项。正如我在 第 1 部门 中先容的,您在一个工具中存储另一个相关工具的键,然后在您需要时查询这个工具。这也正是 SimpleJPA 所做的。譬喻,之前我向您先容了如何利用 JPA 注释将 Runner 链接到一个 Race。因此,我可以建设一个 Runner 实例,将现有的 race 添加到这个实例,然后再存储 Runner 实例,如清单 12 所示:
清单 12. 利用 SimpleJPA 处理惩罚干系
Runner runner = new Runner();
runner.setName("Mark Smith");
runner.setSsn("555-55-5555");
runner.setRace(race);
race.addRunner(runner);
em.persist(runner);
em.persist(race); //update the race now that it has a runner
从 清单 12 我们也可以看到,我需要更新 Race 实例,这样我所添加的 Runner 才会被存储起来(同时,我给 Race 函数添加了一个 addRunner 函数,它只是直接将一个 Runner 添加到 Runner 内部的 Collection。)
再一次,假如我通过它的实例搜索一个角逐,我也可以获得它的一组选手,如清单 13 所示:
清单 13. 更多有趣的干系操纵!
Query query = em.createQuery("select o from Race o where o.distance = :dist");
query.setParameter("dist", new BigDecimal(26.2));
List<Race> races = query.getResultList();
for(Race races : race){
System.out.println(race);
List<Runner> runners = race.getRunners();
for(Runner rnr : runners){
System.out.println(rnr);
}
}
利用 EntityManager 实例使我可以或许通过 remove 函数删除一些实体,如清单 14 所示:
清单 14. 删除一个类的实例
Query query = em.createQuery("select o from Race o where o.distance = :dist");
query.setParameter("dist", new BigDecimal(26.2));
List<Race> races = query.getResultList();
for(Race races : race){
em.remove(race);
}
当我在 清单 14 中删除了一个 Race 实例,所关联的 Runners 并没有删除。(虽然,我可以利用 JPA 的 EntityListeners 注释来处理惩罚这个问题,这意味着我可以监控删除事件,在事件产生时删除 Runner 实例。)
竣事语
这篇 SimpleDB 的快速教程向您先容了如何利用 Amazon Web Services API 和 SimpleJPA 来处理惩罚非干系数据存储的工具。SimpleJPA 实现了一部门的 Java Persistence API 来简化 SimpleDB 的工具耐久化。您已经看到,利用 SimpleJPA 的个中一个长处是它可以或许自动地将根基数据范例转换为 SimpleDB 能识此外字符串工具。SimpleJPA 也可以或许自动地处理惩罚 SimpleDB 中的非联正当则,从而简化它的干系建模。SimpleJPA 扩展的监听接口也使它可以或许实现逻辑数据统一法则,这是您在干系数据库所但愿利用的。
SimpleJPA 的要害是它可以或许辅佐您快速简朴且便宜地实现重要的可扩展性。通过 SimpleJPA,您可以在非干系的、基于云的存储情况中操作您在多年的事情中所打仗到的诸如 Hibernate 等框架的常识。