我们中很少有人会低估 Java 2 Platform, Enterprise Edition (J2EE) 对于 IT 基础架构中应用程序编程和部署的影响。对这种基于 Java 的技术投资的公司需要与这种规范保持同步,以便获得投资时承诺的可伸缩性和灵活性。在 J2EE 全新的 Enterprise JavaBean (EJB) 3.0 规范中,出现了很多变化,但这些变化对于解决生产力和可伸缩性问题却是必需的,而这两个问题正是批评者抱怨,难于使用这些平台的重点所在。
让我们看一看 EJB 3.0 中的这些变化,并就它们如何与 Java Data Objects (JDO) 2.0 规范重叠提供一个远景。当新的 EJB 3.0 规范达到了您的生产应用程序所要求的成熟度时,您可能会准备使用它,而且现在您可以通过利用成熟的 JDO 标准来做到这一点。 JDO 和 EJB 的规范主管在的最近的声明中表示,这两个群体现在正在相互结合,为解决应用服务器容器内部和外部需要的 Java 持久性,提供一个通用的 API 。
值得注意的是,在撰写本文之际,新的 EJB 3.0 规范尚未完成,而且还处在早期的草案评审阶段。随着规范的逐渐成熟,对于行为中提出的许多变化,将会出现许多说明。然而,在新的 EJB 3.0 规范中,有一个变化从根本上来说十分突出。 EntityBean 将不再出现——嗯,至少在精神上是这样。出于明显的理由,业界肯定会继续提供一些支持,直到企业可以转变到 Plain Old Java Object (POJO) 持久性模型为止。
轻松的持久性
EJB 3.0 规范的主要目标是提供一个更加简单的持续机制,与 Java 作为一种面向对象语言的正确使用更加一致。更新、更轻量级的持久性机制,即 POJO 机制,是一种可以消除 EntityBean 的重要接口,并促进在现今的对象驱动设计中使用对象模型方法。无格式普通 Java 对象( plain old Java object )的行为方式是持久性的,并坚持数据库应用程序的事务性语义。
EJB 3.0 中提出的 POJO 方法与 JDO 自 1999 年开始采用的是同一种方法。这一点,以及 JDO 在 Java 社区中,作为一种可伸缩的、灵活的持久性解决方案,所表现出来的成熟性和可接受性,正是驱动这两种规范合并的动力所在。让我们看一看 EJB 3.0 中提出的特定功能,并注意这些功能是如何与 JDO 2.O 中的功能相重合的。
EntityBean 消失的关键在于,引入了一个负责管理域对象的生命周期的对象,并且这一对象展示出了它持久的特性。 EJB 3.0 中的这个新接口称为 EntityManager 。 EntityManager 将负责跟踪数据库事务上下文中,持久的 POJO 对象的状态。 EntityManager 把 JDBC 连接或数据源和接口,与应用服务器的事务管理器包装在一起,从而实现一个跨所有 POJO 访问的统一事务上下文。应用服务器将池化这些 EntityManager ,正如它现在池化数据源一样。
在 JDO 中有一个等价接口,它的职责与 EntityManager 相同,叫做 PersistenceManager 。正如 EJB 3.0 一样, JDO PersistenceManager 包装了 JDBC 连接和数据源,并与应用服务器的事务管理器一致,从而提供了事务上下文中的生命周期管理。 EntityManager 和 PersistenceManager 之间的主要区别是,在 EJB 3.0 中, EntityManager 与应用服务器容器绑定在一起,而在 JDO 中, PersistenceManager 可以同时在容器上下文的内部和外部管理 POJO 生命周期。
另外,关于跨事务边界,以及位于事务上下文外部的状态管理, PersistenceManager 提供了更加丰富的行为特性。您可以认为 EntityManager 是 PersistenceManager 中行为的一个子集。 表 1中列出了 EntityManager 中提出的行为,以及 PersistenceManager 中对应的行为。既然已经宣布了 JDO 和 EJB ( JSR 220 )专家组的结合,您将会看到这种行为的合并,因为 EntityManager 已经将其作用范围扩展到,解决应用服务器容器外部的持久性上了。
行为描述 |
EJB 3.0 EntityManager |
JDO 2.0 PersistenceManager |
注释 |
使对象成为数据库的一部分,并由管理程序进行跟踪。 |
create(obj) |
makePersistent() |
在 JDO 中,任何可被持久性的引用对象,都将在事务边界上隐含地变为持久性的,这就叫做可达性持久( persistence by reachability )。在 EJB 3.0 中,只有当可以被持久性的引用对象,拥有已定义的显示元数据时,才会在它们被持久性的地方,使用声明可达性持久。 |
允许对象与来自事务的身份分离,以作为另一层的 DTO 。 |
close() |
detach() |
在 EJB 中,关闭 EntityManager 或结束事务时,所有加载的对象都会变为分离的。在 JDO 中,只有作为业务逻辑目标的对象才是分离的,而且不必关闭 PersistenceManager 。 JDO 还允许使用 fetch group 来支持要沿着根对象图,向下分离的定制用例中的对象或域。 EJB 使用级联元数据来获得同样的效果。在 JDO 中,可以有许多命名的用例 fetch group 。在 EJB 中,只有一个级联关系的定义。 |
允许附加以前使用身份分离的对象,并使在分离期间对现有数据库对象所做的修改生效。 |
merge() |
attach() |
在 merge 或 attach 方法中,将使用时间戳,对所有被修改的对象进行比较,然后最优地更新这些对象。在 JDO 中,所有可达的对象都是自动附加的。在 EJB 中,只附加拥有显式元数据的对象。 |
通过身份在数据库中找到一个对象。 |
find() |
getObjectById() |
|
清除对由数据库管理程序托管的对象进行的修改。 |
flush() |
flush() |
在 EJB 和 JDO 二者中,提交时就会隐含地调用这个方法。这对 EJB 和 JDO 的效果来说,其差异是基于所选择的数据库隔离级别的。 |
创建一个 Query 对象,以便在数据库中查找对象。 |
createQuery() |
newQuery() |
Query 对象把 SQL 行为封装在一个对象中。 |
创建一个命名查询。 |
createNamedQuery() |
newNamedQuery() |
|
创建一个 SQL 查询,而不是 EJBQL 或 JDOQL 查询。 |
createNativeQuery("SQL STRING") |
newQuery("sql", "SQL STRING") |
JDO 使用相同的 newQuery() 方法,但是调用参数不同。 |
从数据库刷新管理程序缓存中的对象。 |
refresh() |
refresh() |
|
从 JVM 收回管理程序缓存中的对象。 |
evict() |
evict() |
|
查明一个对象当前是否由管理程序托管。 |
contains() |
isPersistent() |
|
表 1 行为比较
作为 PersistenceManager 中建立的行为子集, EntityManager 中提出的行为相当于 PersistenceManager 中的这些行为。
为了让您初步了解使用这个 API 编程时,可以期望获得什么结果,让我们看一个例子,即创建一个新对象,然后将其保存在数据库中。在这个例子中,我们设想了一个应用服务器环境,以便和过去使用 EJB 2.1 时的环境进行对比。
有争论的方法
像往常一样,您会拥有一个 SessionBean 和生命周期方法的典型主机。当然,这些条件当中的一些可能出现变化,但重点是在于当 SessionBean 开始存在于容器中时,它就有机会与和 EntityManager 相一致的托管事务上下文连接。一旦您进入了 SessionBean 的远程方法,容器会自动确保您处在一个事务中。然后,典型的 SessionBean 及其方法看起来就像下面这样:
public class AccountManager {
EntityManager manager;
public void setEntityManager(
EntityManager mngr) {
this.manager = mngr;
}
public void makeDeposit(
int acctID, long amount) {
Account acct = (
Account)manager.find(
"Account", acctID);
acct.deposit( amount );
}
}
仔细检查这段代码,就会发现一些还没有答案的问题:哪一类的 SessionBean 是 AccountManager 呢?生命周期方法在哪里?对于这些和其他问题的答案仍然有待讨论。借助初始的 JSR 220 早期草案规范,您可以使用注释和注入机制来获得这类说明。然而,这类方法的实现依赖于 JDK 1.5 ,而在相当一段时间内,绝大多数应用服务器厂商都不会采用 JDK 1.5 。
因此,我们对把 JDO 专家组包括在 JSR 220 内进行了一些思考,这需要使用注释,将被放宽为,允许通过在容器初始化时读取的 XML 元数据文件,使用注释和规范,以支持早期的采用。在正确的时间,即企业更新为与 JDK 1.5 兼容的部署平台时,厂商也可以很容易地,实现注释添加的自动化。
值得注意的是“实体”。 Account 对象本身看来似乎是无格式普通 Java 对象。您不再需要使用重量级的 home/remote 接口,来获得到工厂(可以调用工厂来查找实例)的引用。相反, SessionBean 和实体打交道时更像是内存中的 Java 对象,而新的实现正在后台为您处理细节。同样,从客户端的角度看,您不再需要从重量级接口查找 SessionBean 本身了。相反,您可以通过 JNDI 或者实现提供的注入机制,把它当作任何其他的 Java 资源进行查找。
EJB 3.0 进行改进的另一个主要领域是,消除了域模型的仅静态编译的访问方法,在 EJB 2.1 中这被称为“查找( finder )”方法。这些变化是和对 EJBQL 查询实现的新扩展相结合的。与已扩展的查询支持耦合在一起的,新生的命周期管理,将消除 EJB 2.1 实现中,声名狼藉的 N+1 装载问题。对于这个问题不熟悉的人会认为,它是由用于装载 N 个对象的集合的多次( N+1 )数据库调用,引起的一个性能问题。在新的 EJB 3.0 实现中,可以通过一次数据库调用装载一个集合中的所有对象,这与当前 JDO 规范中的行为是一致的。 表 2 列出了在 EJB 3.0 版的 EJBQL 中提出的新功能,并把它们与 JDO 2.0 版中可用的功能进行了比较。
查询功能 |
EJB 3.0 |
JDO 2.0 |
注释 |
Bulk 操作 ( 更新 / 删除 ) |
支持 |
支持 ? |
? JDO 没有用于批量更新的显式 API ,但是作为代替,厂商对大型的事务性更新使用了隐式的语句,来进行批处理。此外,您可以对批量更新使用直接的 SQL 。还有用于进行批量删除,同时无需从数据库装载对象的方法。 |
JOIN 操作 (inner/outer) |
支持 |
支持 |
|
GroupBy |
支持 |
支持 |
|
Having |
支持 |
支持 |
|
Projections |
支持 |
支持 |
|
多态查询 |
支持 |
支持 |
|
预定义查询 |
支持 |
支持 |
|
直接的 SQL 支持 |
支持 |
支持 |
|
Fetch JOIN |
支持 |
支持 |
EJB 支持仅通过一层关系的 Fetch JOIN 。 JDO 则支持多层关系。 |
函数支持 (Upper, Lower, Trim, Position, Char_lenth, Bit_Length, Current_Time) |
支持 |
支持 |
JDO 仅直接支持 Upper 和 Lower 命名方法;然而,通过使用直接的 SQL ,您可以调用任何数据库特有的函数。 |
查询上的数据库隔离级别 |
支持 |
? |
? 规范中并未显式定义,但所有大型厂商都将其实现为连接配置的一部分。 |
表 2 新的 EJBQL 特性
将 EJB 3.0 版本的 EJBQL 中提出的新特性与 JDO 2.0 的特性进行比较。
映射
EJB 3.0 出现显著变化的另一个领域是,其支持正确的面向对象概念(比如继承和多态)的能力。这些变化给,如何将模型映射为底层数据库的问题,带来了新的复杂性。在 EJB 3.0 的首个版本中,规格的目标是,把用不同的列,继承性地映射到单独的表的需求。这种方法通常被称为 Flat 映射。 EJB 3.0 定义了一个继承层次的可选 Vertical 映射,这种映射将定义被继承的类,到多个已联接的表的映射。规范只要求一种 Vertical 映射的形式,这种形式不支持判定列( PK 列用于形成超类表和子类表之间的连接)。
另外, EJB 3.0 规范提倡支持相关类,即没有独立身份,并借助自有类嵌入的类。因此,相关对象也就自动受到,发生在所有者对象上的状态变化的支配。例如,如果一个所有者被删除,那么相关对象也将被自动删除。这些相关对象不支持 EJB 3.0 中的继承性,而且您不能拥有它们的集合。
映射无疑是在 EJB 规范的修订版本中,进行了大量修改的一个领域,因为最初的规范并未真正接纳,许多现有数据库实现中,可见的数据模型。支持现有数据库的能力和预定义的数据模型,对于企业与现有系统的集成是必不可少的。
JDO 标准始终支持 Java 语言的完整功能,包括集成和多态。在 JDO 2.0 中,集成层次结构的映射包括 super-class-table(Flat), new-table(Vertical) 和 no-table(Horizontal) 。 Horizontal 映射把被继承的属性映射为具体的类表。这类映射推动了具备带有通用属性的,抽象超类的模型。此外, JDO 2.0 标准化了关系映射规范,从而确保了跨厂商的映射文件兼容性。 JDO 还支持相关对象,包括它们的集合。
可以把 EJB 3.0 映射看作是 JDO 2.O 中映射的一个子集。这两种技术之间的主要差别在于,当它们映射为相同的数据模型时,您该如何指定映射。在 EJB 3.0 中,映射是通过定义在 JDK 1.5 版本中的源代码注释指定的。 JDO 2.0 专家组不想只把 JDO 的使用关联到 JDK 1.5 ,所以当考虑到注释时,它们很可能是可选的。
相反, JDO 要求在与所有 JDO 2.0 兼容厂商兼容的、具有标准格式的 XML 文件中指定映射。一些 JDO 厂商将把注释作为一种可选功能,并予以支持,直到 JDK 1.5 被广泛采用为止(到这个时候, JDO 规范很可能把它当作必须实现的功能)。在 EJB 3.0 中,注释不仅在映射中发挥着作用,而且部署描述符也正在对注释提供支持,使其成为与容器上下文、环境打交道以及资源查找的一种方式。这条额外的元信息还被捕捉在 XML 属性文件中,或者在运行时被定义为 JDO 2.0 标准中, Property 对象的一部分。
生命周期管理
对象生命周期管理,是 EJB 3.0 规范进行重大调整的另一个领域。生命周期回叫接口的实现变为可选,这样即使是 SessionBean 也无需实现它们。如果 SessionBean 实现了它们,那么容器将适当地调用它们。例如, setSessionContext() 仍旧可以被实现,用以得知,何时把会话 bean 实例化到容器中,其他资源可以在哪些点连接到 bean 。另外,还加入了一些新的生命周期回叫方法。这些方法包括,通知何时把对象转化为 Data Access Object (DAO) 的回叫,或者何时把现有的 DAO 带回到事务上下文中的回叫,即 ejbMerge() 和 ejbDetach() 回叫。 表 3 总结了 EJB 3.0 生命周期回叫和相应的 JDO 2.O 等价回叫。
生命周期回叫 |
EJB 3.0 |
JDO 2.0 |
注释 |
创建新对象 |
ejbCreate() |
|
|
删除对象 |
ejbRemove() |
preDelete() |
|
分离对象 |
ejbDetach() |
preDetach() |
JDO 2.0 中的可选回叫 |
附加对象 |
ejbAttach() |
preAttach() |
JDO 2.0 中的可选回叫 |
把对象装入 JVM |
ejbLoad() |
postLoad() |
|
对象的状态与数据库同步 |
ejbStore() |
preStore() |
|
把托管对象从缓存中清除 |
|
preClear() |
|
表 3 新的回叫方法
这些生命周期回叫被添加给 EJB 3.0 ,而它们相应的 JDO 2.0 等价回叫也被列出。
EJB 3.0 进行修改的最后一个领域是安全性领域。 EJB 3.0 规范的早期草案指出,将支持元数据定义,它将允许您指定可以进行 Create , Read , Update 和 Delete (CRUD) 操作的安全角色。当时, JDO 2.0 规范没有安全属性的定义。
公司对支持它们的业务基础架构的 J2EE 技术进行了实质性的投资,而且它们的当务之急是在未来继续利用这些投资。在 EJB 3.0 规范的后续版本中,大规模的修改威胁到了平滑过渡的能力。甚至还有更坏的情况,现今许多企业需要这些修改,但是厂商迟迟没有开发出可用的实现,可能要等到 JDK 1.5 和 J2EE 1.5 被广泛采用的时候才行。
JDO 标准在最近 5 年已经趋于成熟,超过 15 家厂商提供标准的实现。 JDO 2.0 标准与 EJB 3.0 规范已经有了 85% 的重叠——重叠程度如此巨大,以至于 JDO 厂商将毫无疑问地成为 EJB 3.0 规范的第一批实现者。大多数厂商,比如 Versant Corporation ,表示它们将提供迁移战略,甚至在它们的产品中支持多个标准,直到对新的 JSR 的完全采用成熟为止。
现在,您可以使用 JDO ,并利用这项成熟的技术与 J2EE 平台中出现的变化保持同步。当 EJB 3.0 最终定稿时,您可以在下一次升级时进行转换,实际上您只需点击一下按钮即可。甚至还有更好的情况, JDO 已经工作于您的托管环境之外,这是 JSR 220 的新要求。针对您的持久性应用程序要求,您可以在今天采用明天的标准,无论应用程序是多层实现还是简单的客户端 / 服务器实现。
关于作者
Robert Greene 是 Versant Corporation 的一位解决方案架构师。您可以通过他的电子邮箱 rgreene@versant.com 与他取得联系。
原文出处
http://www.ftponline.com/channels/java/javapro/2005_03/magazine/features/rgreene/
|