用 Slice 扩展 OpenJPA 应用程序
各个查询结果在内存中被合并然后排序。如果 Li 是来自第 i 个片的排序列表,那么这个合并结果列表 L 就是:
Top-N 查询在 JPA 中是通过该查询中的 ORDER BY 子句与对结果设定限制的结合实现的。下面的这个查询要找出 5 个最年轻的员工:
这个样例查询是来自 Employee e 的 select SUM(e.age),其中 e.age > 30。如果 S 指定所有片上的年龄在 30 岁以上员工的年龄总和并且如果 S i 是第 i 片内的总和,那么很简单,S 就是 Si 的总和。因而,SUM() 就是可互换分区。Slice 可以计算所有可互换分区的聚集操作,比如 MAX()、MIN()、 SUM() 或 COUNT()。
并不是所有常见的聚集操作都是分区可互换的,比如 AVG()。目前,Slice 可以正确地估计一个聚集查询,不是分区可互换的。
?
对于一个本地资源 EntityManager,底层的资源管理器充当了物理数据库上的一个事务管理器,但使用的是一种较弱的事务保证,而不是合适的两阶段落实协议。在一个资源本地的事务中,首先分析作业单元来将管理实例分区到每个底层片的子集。然后每个子集被清仓到对应的数据库。如果一个片的子集是空的,它将被忽略。如果清仓对于所有数据库均失败,那么整个事务就会被回滚。
并置限制
之前,我们看到了,数据分布策略描述了 Slice 在级联 PERSIST 后 如何自动计算根实例 r 的传递闭包 C(r),并将整个闭包存储于单个片内的。值得注意的一点是闭包是在 persist() 调用的时候计算的。因而,在 persist() 后添加的关系不是此显式闭包的一部分。
在通过在根项的关系被分配好后持久化根项来确保满足并置限制的同时,也需要注意在不同的片存储相关的实例会故意违背并置限制的事实。让我们用一个实际例子来阐释这一点。
考虑这样一个例子:Person 和 Address 间存在简单的 1:1 的双向关系。Person.address 由 PERSIST 级联。在映射的术语中,Person 是此关系的所有者(即 ADDRESS 表的外键存在于 PERSON 表内)。
假设我们有两个片 — One 和 Two — 并且我们的分布策略是这样的:如果 Person 的名字以字母 A 到 M 开头,那么它就存储在片 One 内。否则,就存储在片 Two 内。同样地,如果 Address 的 ZIP 代码以偶数结尾,那么它就存储在片 One 内。否则,它存储在片 Two 内。
在这些简单的规则下,让我们创建一个 Person p 和一个 Address a 实例并存储它们。
清单 7. 有关并置限制效果的代码示例
Person p = new Person();p.setName(“Alan”); // slice One as name starts with letter AAddress a = new Address();a.setZipCode(12345); // slice Two as zip code is odd digitem.getTransaction().begin();p.setAddress(a);em.persist(p); // relation to address is set before persist // Address persisted by transitive persistenceem.getTransaction().commit();
?
在清单 7 中,Person p 与 Address a 相关,当调用 em.persist(p) 时,Address a 将被作为 Person p 存储在相同的片 One 中。根据分布策略,Address 实例 a 本应该存储在片 Two 内,因为该实例的 ZIP 代码以奇数结尾。但根本不会为 Address a 调用这个策略,这个策略只会为根实例 Person p 调用。Slice 觉察到 a 处在 p 的传递闭包内并且,随着 p 被分布策略分配给片 One,那么同一个片 One 也会自动分配给 Address a。
清单 8 内的代码展示了如果持久化调用排序不同时将会如何。
清单 8. 如何绕过并置限制的代码示例
em.getTransaction().begin();em.persist(p); // relation to address is not set before persistp.setAddress(a);em.persist(a); // a has to be persisted explicitlyem.getTransaction().commit();
?
分布策略将为 person p 和 Address a 单独调用,而二者位于两个不同的片中。
这样,尽管可能会违背并置限制,并将相关的示例存储在不同的片中,但这种存储策略的实用性很有限。当 Person 及其相关的 Address 位于不同的数据库时,能执行的操作只有几个。例如,可以从拥有侧迟钝加载此关系(即,如果这个关系是迟钝的,那么 Person.getAddress() 即便是从其他的数据库也可以获取正确的地址)。如果关系是迫切需要的或从非拥有侧导航来的或者是要求类似像 'select p from Person p where p.address.zipcode = 12345' 这样的连接的一个查询,那么它将生成一个错误结果。
?
结束语
数据分区是在有大量数据时进行扩展的一种有效策略,特别是在有自然分区存在的情况下(例如,根据名称的客户账号,按地区的家庭列表)或是在应用程序语义要求数据分离的情况下(例如,多租户托管平台)。标准 JPA 没有处理分区或分配的有效方式,因为规范已经隐式地假定单个数据库作为存储库。Slice 扩展了 OpenJPA 实现,使其能够无缝地支持数据分区。与其他的分区解决方案不同,Slice 并不需要向现有模式中添加任何额外的列以支持分区。使用 Slice 的分布和查询目标锁定是通过一个基于策略的插件接口提供的,确保了现有的 JPA 的应用程序无需进行代码修改(只需添加新的策略接口以及重新配置 persistence.xml)。
?
参考资料
学习
更多地了解 OpenJPA 及其功能。参见有关 Java Persistence API Specification V2.0 的规范。
在 Wikipedia 上更多地了解数据分区概念。
要收听面向软件开发人员的有趣访谈和讨论,请查看 developerWorks 播客。
查阅最近将在全球举办的面向 IBM 开放源码开发人员的研讨会、交易展览、网络广播和其他 活动。
访问 developerWorks Open source 专区获得丰富的 how-to 信息、工具和项目更新以及最受欢迎的文章和教程,帮助您用开放源码技术进行开发,并将它们与 IBM 产品结合使用。
随时关注 developerWorks 技术活动和网络广播。
查看免费的 developerWorks 演示中心,观看并了解 IBM 及开源技术和产品功能。
?
原文:http://www.ibm.com/developerworks/cn/opensource/os-openjpa/index.html?ca=drs-
?