在任何 JavaTM 技术应用程序中,持久性框架都是一个极其重要的部分。作出选择是令人头疼的一件事。因此,开发人员常常选择流行的框架,不论是企业级 JavaBeans 还是 Hibernate。通常,您不需要完整的对象关系映射层。即使您确实需要,其他的持久方案都有提供。Hibernate 是免费的,而且功能丰富。Kodo JDO 有优秀的管理和映射支持。iBATIS 是一种混合解决方案,它把对象映射到 SQL 查询的结果,而非表格。甚至 JDO 也有它的地位。针对这一情况,我找了些资料,总结了一下,讨论一些能让您有一个选择最佳方案的方法。
选择合适的工具
当处理持久性时,为找到合适的框架可以把一个艰难挣扎的、沮丧的团队转变为一个运转良好的机器。但是,必须小心。改变框架可能是昂贵的。后面第三部分又展示了 Spring 如何帮助您优化诸如持久性(persistence)之类的服务。这里主要关注寻找合适的持久性框架。首先,来破除几个神话。
神话 1:技术总是最要紧的。
不论您是在谈论 RDBMS 还是持久性框架,持久关系都是长期的。您需要知道您选择的方案将长时间拥有市场份额。如果不是,您将冒着被迫离开指定框架的风险,而这并非您的意愿。
神话 2:对象关系映射程序(Object Relational Mappers,ORM)隐藏了所有的数据库问题。
持久性框架简化了一些问题,但引入了一些其他的问题。如果您不理解底层架构,那么找懂的人问,或者不要使用对象关系框架。
神话 3:JDBC 总是比 ORM 要快。
当然了,一个非常协调的特定 Java 实现总是会打败普通的 ORM,但是您很难找到一个非常协调的 JDBC 应用程序。JDO 和 Hibernate 之类的框架提供许多方法来提升性能。
神话 4:ORM 总是合适的工具。
常常,您会发现要求更好的访问 SQL 的问题。您也会发现面向批处理的而非交互式的问题,或要求直接访问数据库行,而非完整的对象模型。这些问题并没有充分利用 ORM。更糟糕的是,一些解决方案也许会碍事。
神话 5:Java 的 ORM 解决方案是处理持久性的惟一的好方法。
其他语言同样有具有竞争力的持久性策略。Microsoft? 策略倾向于接受关系数据库并与行集一起工作。您让应用程序与关系模式紧密结合,但是您获得了一种能力,可以绑定行集到用户界面控制,并整个应用程序中将它们与缓存策略集成,甚至脱离防火墙与缓存代理。Ruby 语言有 Rails,后者有一个有效的记录框架。对于许多类型的问题,记录框架比 Java 技术解决方案更加动态和高效。
注意:这些神话都是绝对的。持久性框架是各有千秋。一起来看看主要的解决方案分类。
本地 JDBC 框架
JDBC 是基本的 Java 技术 API,允许访问数据库。它代表最低级别的持久性策略。本地框架各不相同,但大多数有着相似的特征。
大多数 JDBC 解决方案把所有的数据访问包装在数据访问对象中,对象中包装有一个关系表。从那里,您可以把数据留在结果集或者选择把结果集中的每一行映射到一个对象。Java 技术开发人员倾向于映射到轻值对象(light value objects)。一个特殊的替代方案是 Martin Fowler 的有效记录设计模式,这种设计模式提供针对数据表中行的包装程序。每一个有效记录都有访问每一列的方法,以及保存、删除或更新行的方法。
长处
基于 JDBC 的解决方案提供良好的控制能力。您要写更多的代码来解决预料中的问题,但是您有访问数据库的所有权利,并且可以让它只做您想要它做的事。纯 JDBC 给予您非常好的灵活性。
弱点
关系数据库和对象并不是一回事 —— 这里有一点分歧。您不得不处理一些面向对象的概念,如继承。您还必须自己管理每一个对象,写查询程序来完成创建、读取、更新或删除操作。如果要提升性能,需要提供您自己的缓存。
总结
对于那些技术不好但了解 SQL 的 Java 程序员来说,JDBC 是一个很好的选择。如果您需要更好的访问 SQL 或者在进行批处理计算或报告,那么 JDBC 也能胜任。
变种
有几个框架和工具可以帮助您扩展 JDBC。一些工具,像 Velocity 和 MiddleGen,会生成数据访问对象,给出数据库表的描述。Spring 提供依赖注入和面向方面的编程,这使得服务和依赖关系的集成更加容易。但是总得来说,应用程序结构和策略应该保持一致。
对象关系映射程序
OOP 和关系数据库基于根本不同的基础。通常,很难把两者混合起来。如果您有一个现有的关系模式或一个可能频繁改变的关系模式,那么 ORM 框架也许正是您需要的。大多数的 ORM 试图让您透明地处理对象。您提供一个 POJO,然后,通过使用代码生成(EJB)、字节码操纵(JDO)或反射(Hibernate),框架与持久性相关联。每种技术都有它的长处和弱点,因此大多数的框架使用多种方法来完成工作。
您需要告诉应用程序如何映射数据库表到应用程序的类。您可以用一个独立的 XML 文件或程序代码中的注释来达到目的。这些注释可以采取 Java 5 注释的形式(使用 XDoclet 之类的工具)。如果模式和对象模型不会走到一起,我一般会将代码和配置文件分离。
持久性框架让您从数据库载入一系列的对象。您可以显示它们,或操纵它们并把它们存回到关系数据库。大多数的 ORM 框架提供扩展,比如两级缓存。一般来说,第一级缓存保证事务的完整性,第二级保证跨集群中机器的一致性。尽管有多种实现,但是您应该准确理解缓存策略。
您需要用于管理配置和依赖性的策略。您的应用程序可能需要选择一个事务策略并使用数据源和连接池。正如在前面的章节中所学到的那样,Spring 和 ORM 能很好地为您处理这些问题。这里有一些可用的 ORM。
EJB
企业级 JavaBean 提供两种标准化的持久性策略,EJB 1.x 标准 和 2.x 标准。第二个标准做的更好,但仍然过于复杂。EJB 专家组承认这一事实并将提供第三个标准,即 EJB 3 JSR;但是该标准将对所有的 Java Enterprise Edition (JEE) 用户可用,不仅仅是对 EJB 用户可用。因此,EJB 持久性标准实际上是一条死路,因为新的应用程序想要目标方案接近期望的 JSR 220 标准。
Hibernate
Hibernate 很快变成了持久性的事实上的标准。它快速,有效,而且是免费的。因为 Hibernate 让您制定任意的 POJO 持久性,所以它必须有一种方法把持久性关联到一个对象而不必改动代码。Hibernate 主要通过反射来提供透明性,但是它通过动态代理混合在一些运行时字节码操纵中。使用反射,Hibernate 可以在事务完成前后查看对象的状态。如果状态发生改变,Hibernate 可以把它保存到数据库中。代理帮助 Hibernate 实现一些其他的特性,比如懒散加载(lazy loading)。(把动态代理想像成一个坐在目标对象前面的对象,它有一个与目标对象相同的接口。每当您调用某些方法或访问实例变量时,代理都可以自由地调用持久层。)
Hibernate 仅支持关系数据库,而且它与 SQL 结合的紧密程度比大多数其他的持久性框架要高。Hibernate 使用类似于 SQL 的查询语言,这种相似性对用户的帮助很大。如果需要,您也可以在 Hibernate 中直接使用 SQL。
像 JDO 一样,Hibernate 拥有两级缓存。第一级缓存叫做会话,给您一个存放持久性对象的地方。您可以把对象载入缓存并操纵它们。然后决定何时通过在会话上调用刷新(flush)或提交(commit)来将更改持久存储到数据库中。
Hibernate 帮助您管理关系。如果您定义一个关系,比如雇员属于部门,Hibernate 将对其进行管理。如果您载入一个部门的信息,您可以决定是在载入一个部门时载入所有的雇员信息(热切加载),还是等待载入雇员信息(懒散加载)。
长处
Hibernate 有一个灵活的映射机制。一些场景比其他场景付出更多的努力来映射,但是如果您能在一个关系模式中表示它,那么也许在 Hibernate 中有一种方法来映射到它。Hibernate 的性能比大多数的框架要好而且还在不断提升。文档很优秀,收购 JBoss 后,支持也在改善。JBoss 小组也把 Hibernate 放置在一个合适的位置以抢在竞争者之前实现 JSR 200 持久性标准。
对 Hibernate 来说,与其他开放源码框架和商业框架的集成比其他的替代框架要好。一般来说,Spring 与 Hibernate 的集成比与其他任何一个持久性框架的集成要好。
Hibernate 是一个创新的框架。在推动与 SQL 的集成上,它比大多数其他的框架走的更远。它具有一些其他框架不支持的特性,比如会话过滤。还有一支强大的公共和商业开发人员团队为其工作。
弱点
如果您是一家大公司,那么您可能要用不支付许可费来弥补支持上的欠缺。Hibernate 比替代框架更加难以管理。例如,您没有 SolarMetric 的 Kodo JDO 产品提供的丰富的管理控制台。您也没有 Versant 的 JDP 产品提供的丰富的用户界面工具。
最后,Hibernate 不像一些持久性框架那么专业。例如,对于一些边缘情况,比如管理懒散加载,Kodo JDO 有非常好的错误信息和更加可预测的行为。
JDO
如果您想要带持久性框架的 Betamax,JDO 1.x 就足够了 —— 虽然时运似乎在转向 JDO 2,更不用说 JSR 220 持久性标准了。在过去三年左右的时间里,最好的技术持久性框架来自 JDO 社区。JDO 通过字节码增强机制实现了透明性。JDO 2 多少会放松这个限制。
JDO 提供一种叫做 JDO QL 的查询语言。对缓存和提取定义了懒散/热切提取场景的组(可以在每次查询的基础上定义这样的场景),它有正式的性能扩展。JDO 还为分离式处理(detached processing)提供一个模型,以便您能够从一个 JDO 会话(叫做 PersistenceManager)分离一个对象,改变并重新附加该对象。然后数据库应用所有更改。
JDO 还为任意的数据存储提供透明的持久性。在现实世界中,大多数的数据是非关系型的,JDO 自己就能很好地为该社区提供解决方案。
长处
JDO 的各种供应商各有长处。Kodo 产品在需要极限持久性场景的利基市场卖得很好。Kodo 执行快速,并得到广泛的认同:它对任何一个 JDO 产品都有最好的映射支持。(我要说,就现在来看,它的映射支持是业界最好的。)Kodo 还在可管理性方面领先。(Versant 产品也有着非常快的速度。)显然,它的最大优点是针对映射支持的用户界面。
弱点
对 JDO 弱点的任何处理都必须从市场份额开始。作为一个标准,JDO 应当得到更好的保护,但是到目前为止,这个标准并不是非常成功。具有讽刺意味的是,JSR 220 持久性标准的出现很可能会对 JDO 造成打击。许多 JDO 供应商已经宣布在他们的产品中支持 JSR 220。您将会看到一个更强大的标准,而且好的 JDO 供应商将能够在那些市场中占有一席之地。JSR 220 标准将会开放,而且新的标准将从那里快速产生,您将会看到顾客使用同样的标准来尝试 JDO。由于疲弱的市场表现, JDO 需要在开放源码社区中更好地表现。Versant 向 Eclipse 小组捐赠它的产品是在正确的方向上前进了一步。
其他
Top Link、OJB 和 Cayenne 正在追赶 Hibernate,但是它们不可能赶上了(出于许多因素)。在接下来的几期文章中,我将用其他的语言如 Ruby 或 Python 来探索几个框架。
混合解决方案
本文将在介绍完一种混合解决方案后结束。大多数的 ORM 解决方案把一个类映射到一个关系数据库模式,而诸如 iBATIS 之类的混合框架则把类映射到一个 SQL 查询的结果。
对于 iBATIS,您提供一个 XML 文件,它指定查询和从那些查询到对象的映射。您能得到 ORM 的一些好处,比如一致缓存策略、独立于代码库的 SQL 和限定的关系管理。
长处
iBATIS 还有一些 ORM 框架没有的优点。您有对 SQL 的严格控制的权利,不必担心对象/关系的不匹配,也不必投入几个月来学习一个对象/关系映射框架。
弱点
iBATIS 不会给予您 ORM 所做的一切。您需要编码查询来完成每一次的数据库访问,而不是您自己来完成访问。与数据库的结合更加紧密。并依靠您选择的 SQL 方言。然而这并不是 iBATIS 的直接局限,这些是这一类解决方案固有的本性。
结束语
您不必机械地去套用 EJB 或 Hibernate,下一次您将开发一个需要持久性的企业解决方案。有许多的解决安可供选择。如果有任何问题,尝试您喜欢的。如果您仍然不能确定使用哪种解决方案,那就寻求帮助。
选择合适的工具
当处理持久性时,为找到合适的框架可以把一个艰难挣扎的、沮丧的团队转变为一个运转良好的机器。但是,必须小心。改变框架可能是昂贵的。后面第三部分又展示了 Spring 如何帮助您优化诸如持久性(persistence)之类的服务。这里主要关注寻找合适的持久性框架。首先,来破除几个神话。
神话 1:技术总是最要紧的。
不论您是在谈论 RDBMS 还是持久性框架,持久关系都是长期的。您需要知道您选择的方案将长时间拥有市场份额。如果不是,您将冒着被迫离开指定框架的风险,而这并非您的意愿。
神话 2:对象关系映射程序(Object Relational Mappers,ORM)隐藏了所有的数据库问题。
持久性框架简化了一些问题,但引入了一些其他的问题。如果您不理解底层架构,那么找懂的人问,或者不要使用对象关系框架。
神话 3:JDBC 总是比 ORM 要快。
当然了,一个非常协调的特定 Java 实现总是会打败普通的 ORM,但是您很难找到一个非常协调的 JDBC 应用程序。JDO 和 Hibernate 之类的框架提供许多方法来提升性能。
神话 4:ORM 总是合适的工具。
常常,您会发现要求更好的访问 SQL 的问题。您也会发现面向批处理的而非交互式的问题,或要求直接访问数据库行,而非完整的对象模型。这些问题并没有充分利用 ORM。更糟糕的是,一些解决方案也许会碍事。
神话 5:Java 的 ORM 解决方案是处理持久性的惟一的好方法。
其他语言同样有具有竞争力的持久性策略。Microsoft? 策略倾向于接受关系数据库并与行集一起工作。您让应用程序与关系模式紧密结合,但是您获得了一种能力,可以绑定行集到用户界面控制,并整个应用程序中将它们与缓存策略集成,甚至脱离防火墙与缓存代理。Ruby 语言有 Rails,后者有一个有效的记录框架。对于许多类型的问题,记录框架比 Java 技术解决方案更加动态和高效。
注意:这些神话都是绝对的。持久性框架是各有千秋。一起来看看主要的解决方案分类。
本地 JDBC 框架
JDBC 是基本的 Java 技术 API,允许访问数据库。它代表最低级别的持久性策略。本地框架各不相同,但大多数有着相似的特征。
大多数 JDBC 解决方案把所有的数据访问包装在数据访问对象中,对象中包装有一个关系表。从那里,您可以把数据留在结果集或者选择把结果集中的每一行映射到一个对象。Java 技术开发人员倾向于映射到轻值对象(light value objects)。一个特殊的替代方案是 Martin Fowler 的有效记录设计模式,这种设计模式提供针对数据表中行的包装程序。每一个有效记录都有访问每一列的方法,以及保存、删除或更新行的方法。
长处
基于 JDBC 的解决方案提供良好的控制能力。您要写更多的代码来解决预料中的问题,但是您有访问数据库的所有权利,并且可以让它只做您想要它做的事。纯 JDBC 给予您非常好的灵活性。
弱点
关系数据库和对象并不是一回事 —— 这里有一点分歧。您不得不处理一些面向对象的概念,如继承。您还必须自己管理每一个对象,写查询程序来完成创建、读取、更新或删除操作。如果要提升性能,需要提供您自己的缓存。
总结
对于那些技术不好但了解 SQL 的 Java 程序员来说,JDBC 是一个很好的选择。如果您需要更好的访问 SQL 或者在进行批处理计算或报告,那么 JDBC 也能胜任。
变种
有几个框架和工具可以帮助您扩展 JDBC。一些工具,像 Velocity 和 MiddleGen,会生成数据访问对象,给出数据库表的描述。Spring 提供依赖注入和面向方面的编程,这使得服务和依赖关系的集成更加容易。但是总得来说,应用程序结构和策略应该保持一致。
对象关系映射程序
OOP 和关系数据库基于根本不同的基础。通常,很难把两者混合起来。如果您有一个现有的关系模式或一个可能频繁改变的关系模式,那么 ORM 框架也许正是您需要的。大多数的 ORM 试图让您透明地处理对象。您提供一个 POJO,然后,通过使用代码生成(EJB)、字节码操纵(JDO)或反射(Hibernate),框架与持久性相关联。每种技术都有它的长处和弱点,因此大多数的框架使用多种方法来完成工作。
您需要告诉应用程序如何映射数据库表到应用程序的类。您可以用一个独立的 XML 文件或程序代码中的注释来达到目的。这些注释可以采取 Java 5 注释的形式(使用 XDoclet 之类的工具)。如果模式和对象模型不会走到一起,我一般会将代码和配置文件分离。
持久性框架让您从数据库载入一系列的对象。您可以显示它们,或操纵它们并把它们存回到关系数据库。大多数的 ORM 框架提供扩展,比如两级缓存。一般来说,第一级缓存保证事务的完整性,第二级保证跨集群中机器的一致性。尽管有多种实现,但是您应该准确理解缓存策略。
您需要用于管理配置和依赖性的策略。您的应用程序可能需要选择一个事务策略并使用数据源和连接池。正如在前面的章节中所学到的那样,Spring 和 ORM 能很好地为您处理这些问题。这里有一些可用的 ORM。
EJB
企业级 JavaBean 提供两种标准化的持久性策略,EJB 1.x 标准 和 2.x 标准。第二个标准做的更好,但仍然过于复杂。EJB 专家组承认这一事实并将提供第三个标准,即 EJB 3 JSR;但是该标准将对所有的 Java Enterprise Edition (JEE) 用户可用,不仅仅是对 EJB 用户可用。因此,EJB 持久性标准实际上是一条死路,因为新的应用程序想要目标方案接近期望的 JSR 220 标准。
Hibernate
Hibernate 很快变成了持久性的事实上的标准。它快速,有效,而且是免费的。因为 Hibernate 让您制定任意的 POJO 持久性,所以它必须有一种方法把持久性关联到一个对象而不必改动代码。Hibernate 主要通过反射来提供透明性,但是它通过动态代理混合在一些运行时字节码操纵中。使用反射,Hibernate 可以在事务完成前后查看对象的状态。如果状态发生改变,Hibernate 可以把它保存到数据库中。代理帮助 Hibernate 实现一些其他的特性,比如懒散加载(lazy loading)。(把动态代理想像成一个坐在目标对象前面的对象,它有一个与目标对象相同的接口。每当您调用某些方法或访问实例变量时,代理都可以自由地调用持久层。)
Hibernate 仅支持关系数据库,而且它与 SQL 结合的紧密程度比大多数其他的持久性框架要高。Hibernate 使用类似于 SQL 的查询语言,这种相似性对用户的帮助很大。如果需要,您也可以在 Hibernate 中直接使用 SQL。
像 JDO 一样,Hibernate 拥有两级缓存。第一级缓存叫做会话,给您一个存放持久性对象的地方。您可以把对象载入缓存并操纵它们。然后决定何时通过在会话上调用刷新(flush)或提交(commit)来将更改持久存储到数据库中。
Hibernate 帮助您管理关系。如果您定义一个关系,比如雇员属于部门,Hibernate 将对其进行管理。如果您载入一个部门的信息,您可以决定是在载入一个部门时载入所有的雇员信息(热切加载),还是等待载入雇员信息(懒散加载)。
长处
Hibernate 有一个灵活的映射机制。一些场景比其他场景付出更多的努力来映射,但是如果您能在一个关系模式中表示它,那么也许在 Hibernate 中有一种方法来映射到它。Hibernate 的性能比大多数的框架要好而且还在不断提升。文档很优秀,收购 JBoss 后,支持也在改善。JBoss 小组也把 Hibernate 放置在一个合适的位置以抢在竞争者之前实现 JSR 200 持久性标准。
对 Hibernate 来说,与其他开放源码框架和商业框架的集成比其他的替代框架要好。一般来说,Spring 与 Hibernate 的集成比与其他任何一个持久性框架的集成要好。
Hibernate 是一个创新的框架。在推动与 SQL 的集成上,它比大多数其他的框架走的更远。它具有一些其他框架不支持的特性,比如会话过滤。还有一支强大的公共和商业开发人员团队为其工作。
弱点
如果您是一家大公司,那么您可能要用不支付许可费来弥补支持上的欠缺。Hibernate 比替代框架更加难以管理。例如,您没有 SolarMetric 的 Kodo JDO 产品提供的丰富的管理控制台。您也没有 Versant 的 JDP 产品提供的丰富的用户界面工具。
最后,Hibernate 不像一些持久性框架那么专业。例如,对于一些边缘情况,比如管理懒散加载,Kodo JDO 有非常好的错误信息和更加可预测的行为。
JDO
如果您想要带持久性框架的 Betamax,JDO 1.x 就足够了 —— 虽然时运似乎在转向 JDO 2,更不用说 JSR 220 持久性标准了。在过去三年左右的时间里,最好的技术持久性框架来自 JDO 社区。JDO 通过字节码增强机制实现了透明性。JDO 2 多少会放松这个限制。
JDO 提供一种叫做 JDO QL 的查询语言。对缓存和提取定义了懒散/热切提取场景的组(可以在每次查询的基础上定义这样的场景),它有正式的性能扩展。JDO 还为分离式处理(detached processing)提供一个模型,以便您能够从一个 JDO 会话(叫做 PersistenceManager)分离一个对象,改变并重新附加该对象。然后数据库应用所有更改。
JDO 还为任意的数据存储提供透明的持久性。在现实世界中,大多数的数据是非关系型的,JDO 自己就能很好地为该社区提供解决方案。
长处
JDO 的各种供应商各有长处。Kodo 产品在需要极限持久性场景的利基市场卖得很好。Kodo 执行快速,并得到广泛的认同:它对任何一个 JDO 产品都有最好的映射支持。(我要说,就现在来看,它的映射支持是业界最好的。)Kodo 还在可管理性方面领先。(Versant 产品也有着非常快的速度。)显然,它的最大优点是针对映射支持的用户界面。
弱点
对 JDO 弱点的任何处理都必须从市场份额开始。作为一个标准,JDO 应当得到更好的保护,但是到目前为止,这个标准并不是非常成功。具有讽刺意味的是,JSR 220 持久性标准的出现很可能会对 JDO 造成打击。许多 JDO 供应商已经宣布在他们的产品中支持 JSR 220。您将会看到一个更强大的标准,而且好的 JDO 供应商将能够在那些市场中占有一席之地。JSR 220 标准将会开放,而且新的标准将从那里快速产生,您将会看到顾客使用同样的标准来尝试 JDO。由于疲弱的市场表现, JDO 需要在开放源码社区中更好地表现。Versant 向 Eclipse 小组捐赠它的产品是在正确的方向上前进了一步。
其他
Top Link、OJB 和 Cayenne 正在追赶 Hibernate,但是它们不可能赶上了(出于许多因素)。在接下来的几期文章中,我将用其他的语言如 Ruby 或 Python 来探索几个框架。
混合解决方案
本文将在介绍完一种混合解决方案后结束。大多数的 ORM 解决方案把一个类映射到一个关系数据库模式,而诸如 iBATIS 之类的混合框架则把类映射到一个 SQL 查询的结果。
对于 iBATIS,您提供一个 XML 文件,它指定查询和从那些查询到对象的映射。您能得到 ORM 的一些好处,比如一致缓存策略、独立于代码库的 SQL 和限定的关系管理。
长处
iBATIS 还有一些 ORM 框架没有的优点。您有对 SQL 的严格控制的权利,不必担心对象/关系的不匹配,也不必投入几个月来学习一个对象/关系映射框架。
弱点
iBATIS 不会给予您 ORM 所做的一切。您需要编码查询来完成每一次的数据库访问,而不是您自己来完成访问。与数据库的结合更加紧密。并依靠您选择的 SQL 方言。然而这并不是 iBATIS 的直接局限,这些是这一类解决方案固有的本性。
结束语
您不必机械地去套用 EJB 或 Hibernate,下一次您将开发一个需要持久性的企业解决方案。有许多的解决安可供选择。如果有任何问题,尝试您喜欢的。如果您仍然不能确定使用哪种解决方案,那就寻求帮助。