本章内容
l 利用SQL数据库持久化对象
l 对象/关系范式不匹配
l 面向对象应用程序中的持久层
l ORM的背景
在所开发的所有软件项目中,管理持久化数据的方法一直都是关键的设计决策。考虑到持久化数据对于Java应用程序并不是什么新的或者不寻常的需求,我们当然希望能够在一些类似的、公认的(well-established)持久化解决方案中进行简单的选择,就像Web应用程序框架(Struts还是WebWork)、GUI组件框架(Swing还是SWT)或者模板引擎(JSP还是Velocity)那样。每一种竞争的解决方案都有自身的各种优缺点,但是它们的范围和总体倾向是相同的。糟糕的是,持久化技术的情况不是这样,这里我们会发现,同一个问题却有一些截然不同的解决方案。
几年来,持久化已经成了Java社区中一个热门的争论话题。许多开发人员甚至对这个问题的范围都存在不同意见。持久化是一个已经被关系技术及其扩展(例如存储过程,stored procedure)解决了的问题,还是一个必须用特别的Java组件模型(例如EJB实体bean)解决的更为普遍的问题?应该用SQL和JDBC手工编写哪怕是最原始的创建、读取、更新、删除(Create、Read、Update、Delete,CRUD)操作,还是应该让这项工作自动进行?如果每个数据库管理系统都有自己的SQL方言,那么如何实现可移植性?我们应该完全放弃SQL并采用一种不同的数据库技术(例如对象数据库系统)吗?争论还在继续,但是现在一种称作ORM的解决方案已经得到了广泛的认可。Hibernate就是一种开源的ORM服务实现。
Hibernate是一个目标远大的项目,旨在成为Java中管理持久化数据问题的一种完整的解决方案。它调解应用程序与关系数据库的交互,把开发人员解放出来,使他们把精力集中在手中的业务问题上。Hibernate是一种无干扰的解决方案。编写业务逻辑和持久化类时,不需要遵循许多Hibernate特定的规则和设计模式;因而,Hibernate可以顺利地与大多数新的和现有的应用程序进行整合,而不需要对应用程序的其他部分进行破坏性的改变。
本书是关于Hibernate的。我们将涵盖基础的和高级的特性,并介绍一些利用Hibernate开发新应用程序的方法。但这些建议经常不特定于Hibernate。有时候,它们可能是我们使用持久化数据时关于最佳做事方式的想法,只不过是在Hibernate的环境中进行了阐述。本书也是关于Java Persistence的,它是持久化的一种新标准,也是最新EJB 3.0规范的一部分。Hibernate实现Java Persistence,并支持所有标准的映射、查询和API。然而,在我们可以开始使用Hibernate之前,你需要理解对象持久化和ORM的核心问题。本章阐述了需要像Hibernate这样的工具,以及像Java Persistence和EJB 3.0这样的规范的原因。
首先,我们定义面向对象的应用程序环境中持久化数据的管理,并讨论SQL、JDBC和Java的关系,Hibernate就是构建在这些基础的技术和标准之上。随后讨论所谓的对象/关系范式不匹配和我们在利用关系数据库进行面向对象的软件开发时所遇到的一般性问题。这些问题清楚地表明,我们需要一些工具和模式,把必须花在应用程序的持久化相关代码上的时间减到最少。在查看了可选择的工具和持久化机制之后,你会发现ORM对于许多场景都是最好的解决方案。我们对ORM优缺点的讨论,为你在给自己的项目选择持久化方案时,提供了做出最好决策的完整背景。
我们也探讨各种各样的Hibernate软件模块,以及如何把它们合并到只使用Hibernate,或者使用Java Persistence和EJB 3.0兼容的特性。
学习Hibernate的最好方式不一定是线性的。我们理解你可能想要立即尝试一下Hibernate。如果这是你喜欢的方式,那就跳到本书的第2章,看看“Hello World”示例,并建立一个项目。但是我们建议你在循环通读本书的某一时刻再回到这一章。这样,你就准备妥当,具备了学习其余内容所需的所有背景概念。
1.1 什么是持久化
几乎所有的应用程序都需要持久化数据。持久化在应用程序开发中是基本概念之一。如果一个信息系统在断电时没有保存数据,这个系统就没有什么实用价值了。当我们在Java中谈到持久化时,一般是指利用SQL在关系数据库中存储数据。我们先简单地看看这项技术,以及如何在Java中使用它。有了这个信息基础,再接着讨论持久化,以及如何在面向对象的应用程序中实现它。
1.1.1 关系数据库
就像大部分其他的开发人员一样,你可能已经使用过关系数据库。我们大部分人每天都在使用关系数据库。关系技术是个已知数,仅此一点就成为许多组织选择它的一个充分理由。但是只提这一点有些贬低了它应得的尊重。关系数据库的地位如此根深蒂固,是因为它们是一种出奇灵活和稳健的数据管理方法。由于关系数据模型完整且一致的理论基础,关系数据库可以有效保证和保护数据的完整性,这是它众多的优良特性之一。有些人甚至会说计算领域的最后一项大发明就是用于数据管理的关系概念,它由E.F Codd(Codd,1970)于30多年前首先提出。
关系数据库管理系统既不特定于Java,也不是一种特定于某个特殊应用程序的关系数据库。这个重要的原理就是数据独立(data independence)。换句话说,我们无法充分强调这个重要的事实:数据比任何应用程序都存在得更长久。关系技术提供了一种在不同应用程序或者构成同一应用程序(例如事务引擎和报告引擎)的不同技术之间共享数据的方式。关系技术是许多异构的系统和技术平台的一个共同特性。因此,关系型数据模型经常是业务实体常用的企业级表示法。
关系数据库管理系统具有基于SQL的应用编程接口(Application Programming Interface ,API);因此,我们称当今的关系数据库产品为SQL数据库管理系统(database management system),或者当我们谈到特定系统时,称之为SQL数据库(database)。
在更详细地探讨SQL数据库应用程序方面之前,必须提到一个重要的问题:虽然有些产品也作为关系数据库销售,但是只提供SQL数据语言接口的数据库系统并不是真正的关系数据库,并且在很多方面甚至与原始概念相去甚远。自然,这样就导致了混乱。SQL从业者抱怨关系型数据模型在SQL语言方面的不足,而关系型数据管理专家则报怨SQL标准在关系模型和理念方面实现得不够。应用程序开发人员被夹在其中,承受着传送一些有效东西的压力。我们将在本书中始终强调有关这个问题的一些重要而有意义的方面,但是通常关注应用程序方面的。如果你有兴趣了解更多的背景资料,强烈推荐Fabian Pascal(Pascal,2000)所著的Practical Issues in Database Management: A Reference for the Thinking Practitioner。
1.1.2 理解SQL
要有效地使用Hibernate,扎实地理解关系模型和SQL是前提条件。你需要理解关系模型,以及像保证数据完整性的标准化这样的话题,还要利用你的SQL知识调优Hibernate应用程序的性能。Hibernate让许多重复的编码任务自动化,但是如果要利用现代SQL数据库的全部功能,你的持久化技术必须扩充至超越Hibernate本身。记住,根本的目标是稳健、高效的持久化数据管理。
回顾一些本书中用到的SQL术语。你用SQL作为数据定义语言(Data Definition Language,DDL),用CREATE和ALTER语句创建数据库Schema。创建了表(和索引、序列等)之后,又用SQL作为数据操作语言(Data Manipulation Language,DML)来操作和获取数据。操作数据的操作包括插入(insertion)、更新(update)和删除(deletion)。通过限制(restriction)、投影(projection)和联结(join)操作(包括笛卡儿积,Cartesian product)执行查询来获取数据。为了有效地生成报表,可视需要使用SQL对数据进行分组(group)、排序(order)和统计(aggregate)。甚至可以相互嵌套SQL语句;这种技术使用了子查询(subselect)。
你可能已经使用SQL多年,熟悉这门语言的基本操作和语句编写。但我们从自身的经验中知道,有时候SQL仍然难以记住,而且一些术语的用法也很不同。要理解这本书,我们必须使用相同的术语和概念,因此如果我们提到的有些术语对你来说是陌生的或者不够清楚,建议你读一下附录A。
如果需要更多的资料,尤其是有关任何性能方面和SQL如何执行的,去找一本Dan Tow在2003年出版的优秀著作SQL Tuning。也看看Chris Date在2003年出版的著作An Introduction to Database Systems,了解(关系)数据库系统的理论、概念和思想。对于你在数据库和数据管理方面可能遇到的所有问题,后者是一本极好的参考书(很厚)。
虽然关系数据库是ORM的一部分,但是,另一部分却由Java应用程序中的对象组成,它们需要用SQL持久化到数据库中和从数据库中加载。
1.1.3 在Java中使用SQL
在Java应用程序中使用SQL数据库时,Java代码通过Java数据库连通性(Java DataBase Connectivity,JDBC)API把SQL语句发到数据库。无论是手工编写SQL并嵌入到Java代码里面,还是由Java代码在运行中生成,都要用JDBC API绑定实参,来准备查询参数、执行查询、滚动查询结果表、从结果集中获取值,等等。这些都是底层的数据访问任务;作为应用程序开发人员,我们更关注需要这些数据访问的业务问题。我们真正想编写的是把对象的代码——类的实例——保存和获取到数据库,或者从数据库获取,使我们从这类底层的苦差事中解脱出来。
由于数据访问任务通常很单调乏味,我们不禁要问:关系型数据模型和(特别是)SQL都适合面向对象应用程序中的持久化吗?我们立即回答:是的!SQL数据库支配了计算行业有许多原因——关系数据库管理系统是唯一公认的数据管理技术,并且它们通常是任何Java项目的必要条件(requirement)。
然而,在过去的15年里,开发人员一直在谈论范式不匹配的问题。这种不匹配解释了为什么都要在每一个企业项目中与持久化相关的问题上付出如此巨大的努力。这里所说的范式(paradigm)是指对象模型和关系模型,或者可能是面向对象编程(Object-Oriented Programming,OOP)和SQL。
让我们通过询问在面向对象的应用程序开发环境中,持久化意味着什么,来开始对不匹配问题的探讨。首先,把本节开头所述的过分简化的持久化定义,扩展为在维护和使用持久化数据中对所涉及内容的一个更广泛、更成熟的理解。
1.1.4 面向对象应用程序中的持久化
在面向对象的应用程序中,持久化允许一个对象在创建之后依然存在。对象的这种状态可以被保存到磁盘,且相同状态的对象可以在未来的某个时候被重新创建。
这并非只限于单独的对象——整个关联对象网络也可以被持久化,且以后在一个新的进程中被重新创建。大多数对象并不是持化久的;瞬时(transient)对象的生命周期有限,由实例化它的进程的寿命所决定。几乎所有的Java应用程序都混合包含了持久对象和瞬时对象;因此,我们需要一个子系统来管理持久化数据。
现代的关系数据库为持久化数据提供了一个结构化的表示法,能够对数据进行操作、排序、搜索和统计。数据库管理系统负责管理并发性和数据的完整性;它们负责在多用户和多应用程序之间共享数据。它们通过已经利用约束实现的完整性规则来保证数据的完整性。数据库管理系统提供数据级的安全性。当我们在本书中讨论持久化时,考虑以下这些事情:
l 结构化数据的储存、组织和获取;
l 并发性和数据完整性;
l 数据共享。
特别是,我们正在使用领域模型的面向对象的应用程序环境中考虑这些问题。
使用领域模型的应用程序并不直接使用业务实体的表格式表示法;该应用程序有它自己的业务实体的面向对象模型。例如,如果一个在线拍卖系统的数据库有ITEM和BID表,Java应用程序就会定义Item和Bid类。
然后,业务逻辑并不直接在SQL结果集的行和列上进行工作,而是与这个面向对象的领域模型及其作为关联对象网络的运行时实现进行交互。Bid的每个实例都引用一个拍卖Item,而且每个Item都可以有一个对Bid实例的引用集合。业务逻辑并不在数据库中执行(作为SQL存储过程);而是在应用层的Java中实现的。这就允许业务逻辑使用高级的面向对象的概念,例如继承和多态。比如,我们可以使用众所周知的设计模式,如Strategy(策略)、Mediator(中介者)和Composite(组合)(Gamma等,1995),所有这些模式都依赖于多态的方法调用。
现在给你一个警告:并非所有的Java应用程序都以这种方式设计,它们也不应该只以这种方式设计。简单的应用程序不用领域模型可能更好。复杂的应用程序可能必须重用现有的存储过程。SQL和JDBC API对于纯表格式数据的处理堪称完美,并且JDBC的行集合(RowSet)使CRUD操作变得更容易了。使用持久化数据的表格式表示法很直接且易于理解。
然而,对于含有重要业务逻辑的应用程序来说,领域模型方法帮助明显改善代码的可重用性和可维护性。实际上,这两种策略都是常用和必需的。许多应用程序都需要执行修改大组数据、接近数据的过程。同时,在应用层中执行一般在线事务处理逻辑的面向对象的领域模型时,其他的应用程序模块可以从中受益。你需要一种有效地把持久化数据带近应用程序代码的方法。
如果我们再次考虑SQL和关系数据库,最终会发现两种范式之间的不匹配。SQL操作如投影和联结始终会导致结果数据的表格式表示法。[这就是传递闭包(transitive closure),关联操作的结果也始终是一种关联。]这与Java应用程序中用来执行业务逻辑的关联对象网络大不相同。这些是根本不同的模型,而不只是把同一模型形象化的不同方式。
带着这些认识,就可以开始看一些问题了——有些已经十分了解,有些则还不太了解——必须通过一个结合了这两种数据表示法的应用程序来解决:一个面向对象的领域模型和一个持久化的关系模型。让我们深入探讨一下所谓的范式不匹配。






