1. 简介
1.1. 目标
该文档的PDF版本可在此处获取 |HERE|。
文档示例可在此处获取 |HERE|。
本文旨在通过 API JPA(Java Persistence API)来探索数据持久化的主要概念。阅读本文并测试其中的示例后,读者应能掌握必要的基础知识,从而能够独立开展工作。
API JPA是新近推出的,仅从JDK 1.5版本起才提供。JPA层在多层架构中占据重要地位。 让我们考虑一种相当普遍的三层架构:
![]() |
- [1] 层(此处称为 [ui],即用户界面层)是通过 Swing 图形界面、控制台界面或 Web 界面与用户交互的层。 其作用是将来自用户的数据提供给 [2] 层,或者将 [2] 层提供的数据呈现给用户。
- [2] 层(此处称为 [metier])是应用所谓业务规则(c.a.d)的层。 应用程序的特定逻辑,无需关心所接收数据的来源,也无需关注所生成结果的去向。
- [3]层(此处称为[dao],即数据访问对象)负责向[2]层提供预先存储的数据(文件、数据库等), ……),并记录 [2] 层提供的部分结果。
- [JDBC]层是Java中用于访问数据库的标准层。这通常被称为SGBD的JDBC驱动程序。
为方便开发人员编写这些不同层,已付出了诸多努力。 其中,JPA旨在简化[dao]层的编写工作,该层负责管理所谓的持久化数据,因此得名API(Java Persistence API)。 近年来在该领域崭露头角的解决方案是 Hibernate:
![]() |
[Hibernate]层位于开发者编写的[dao]层与[Jdbc]层之间。 Hibernate 是一种 ORM(对象关系映射),它作为一座桥梁,连接了关系型数据库的世界与 Java 操作的对象世界。 [dao]层的开发人员不再直接接触[Jdbc]层,也不再直接操作其想要利用内容的数据库表。他所看到的只是数据库的对象映射,该映射由[Hibernate]层提供。 数据库表与 [dao] 层所操作的对象之间的桥梁主要通过两种方式建立:
- 通过 XML 类型的配置文件
- 通过代码中的 Java 注解,该技术仅在 JDK 1.5 版本及以后可用
[Hibernate] 层是一个抽象层,旨在实现尽可能高的透明度。 理想状态是,[dao] 层的开发人员可以完全忽略自己正在与数据库打交道。如果配置关系型世界与对象世界之间桥梁的任务并非由他负责,那么这种理想状态是可行的。该桥梁的配置相当复杂,需要一定的经验积累。
对象层 [4](即 BD 的映射)被称为“持久化上下文”。 基于 Hibernate 的 [dao] 层对持久化上下文中的对象执行持久化操作(CRUD,即创建、读取、更新、删除),这些操作由 Hibernate 转换为 SQL 指令。 对于数据库查询操作(即 SQL Select), Hibernate 为开发者提供了一种名为 HQL(Hibernate 查询语言)的语言,用于查询持久化上下文 [4],而非直接查询 BD 本身。
Hibernate 虽然广受欢迎,但掌握起来却颇为复杂。人们常说它的学习曲线平缓,实际上却相当陡峭。一旦数据库中包含具有“一对多”或“多对多”关系的表,关系型数据库与对象模型之间的桥接配置就绝非初学者所能轻易胜任。配置错误可能会导致应用程序性能不佳。
在商业领域,曾有一款名为 Toplink 的产品与 Hibernate 相当:
![]() |
鉴于 ORM 系列产品的成功,Java 的创建者 Sun 决定通过一项名为 ORM 的规范将其标准化,该规范与 Java 5 同期发布。 JPA规范已被Toplink和Hibernate两款产品实现。Toplink原本是一款商业产品,现已转变为开源产品。随着JPA的推出,原有的架构变为如下所示:
![]() |
[dao]层现与JPA规范(一组接口)进行交互。 这为开发者带来了标准化优势。此前,如果他修改了 ORM 层,还必须修改 [dao] 层——后者原本是为与特定的 ORM 层交互而编写的。 现在,他将编写一个 [dao] 层,该层将与 JPA 层进行交互。 无论由何种产品实现该接口,JPA 层向 [dao] 层展示的接口始终保持不变。
本文将介绍不同领域中的 JPA 示例:
- 首先,我们将关注 ORM 层构建的关系/对象桥接。该桥接将借助 Java 5 注解创建,适用于包含以下类型表间关系的数据库:
- 一对一
- 一对多
- 多对多
为说明这一领域,我们将构建以下测试架构:
![]() |
我们的测试程序将是直接调用 JPA 层的控制台应用程序。借此机会,我们将了解 JPA 层的主要方法。 我们将处于所谓的“Java SE”(标准版)环境中。JPA 既可在 Java SE 环境中运行,也可在 Java EE5(企业版)环境中运行。
- 当我们既掌握了关系型/对象桥接的配置,又掌握了 JPA 层方法的使用后,我们将回归到更经典的多层架构:
![]() |
[JPA]层将通过[metier]和[dao]组成的两层架构进行访问。 将使用 Spring 框架 [7],以及 JBoss 的容器 EJB3 来连接这些层。
我们前面提到,JPA 可在 SE 和 EE5 环境中使用。 Java 环境 EE5 在持久化数据访问领域提供了许多服务,特别是连接池、事务管理器等。对于开发人员来说,利用这些服务可能会很有帮助。 Java 环境 EE5 目前尚未广泛应用(截至 2007 年 5 月)。目前可在 Sun Application Server 9.x(Glassfish)上找到该环境。应用服务器本质上是一个 Web 应用服务器。 如果构建的是Swing类型的独立图形应用程序,则无法使用EE环境及其提供的服务。这确实是一个问题。目前已开始出现“独立”的EE和c.a.d环境。 这些环境可在应用服务器之外使用。本文将使用的 JBos 和 EJB3 便是此类环境。
在 EE5 环境中,各层由称为 EJB(企业 Java Bean)的对象实现。 在 EE 的早期版本中,EJB(EJB、2.x)被认为难以实现、难以测试,且有时性能不佳。 EJB2.x“实体”与EJB2.x“会话”有所区别。 简而言之,EJB2.x“实体”是数据库表中一行数据的映射,而EJB2.x“会话”则是用于实现多层架构中[metier]、 [dao] 层。 针对使用 EJB 实现的层级结构,主要批评之一是它们仅能在 EJB 容器内使用,而该容器是由 EE 环境提供的服务。这使得单元测试难以进行。 因此,在上图所示架构中, 针对基于 EJB 构建的 [metier] 和 [dao] 层的单元测试,需要部署一个应用服务器,这操作相当繁琐,难以激励开发者频繁进行测试。
Spring框架的诞生正是为了应对EJB2的复杂性。Spring在SE环境中提供了大量通常由EE环境提供的服务。 因此,在我们此处关注的“数据持久化”部分,Spring 提供了应用程序所需的连接池和事务管理器。Spring 的出现促进了单元测试文化的兴起,使得单元测试的实施变得简单得多。 Spring 允许通过经典的 Java 对象(POJO,即 Plain Old/Ordinary Java Object)来实现应用程序的各层,从而支持在其他上下文中复用这些对象。最后,它以相当透明的方式集成了许多第三方工具,特别是诸如 Hibernate、Ibatis 等持久化工具。
Java EE5 的设计旨在弥补先前 EE 规范中的不足。 EJB 和 2.x 已更名为 EJB3。 这些是带有注释标记的 POJOs,当它们位于 EJB3 容器中时,这些注释会使其成为特殊对象。 在此容器中,EJB3 将能够利用容器的服务(连接池、事务管理器等)。 在 EJB3 容器之外,EJB3 便成为一个普通的 Java 对象。其 EJB 注解将被忽略。
上文中,我们将 Spring 和 JBoss EJB3 作为多层架构的潜在基础设施(框架)进行了展示。正是这一基础设施将提供我们所需的服务:连接池和事务管理器。
- 在 Spring 框架下,各层将通过 POJOs 实现。这些组件将通过向 POJOs 注入依赖来访问 Spring 的服务(连接池、事务管理器):在构建这些组件时,Spring 会向它们注入所需服务的引用。
-
JBoss EJB3 是一个可在应用服务器外部运行的 EJB 容器。其工作原理(对开发者而言)与 Spring 的描述类似。两者之间差异甚微。
-
本文最后将通过一个三层Web应用程序示例作为结尾,该示例虽然简单,但具有代表性:
![]() |
1.2. 参考文献
[ref1]:《Java Persistence with Hibernate》,作者:Christian Bauer 和 Gavin King,Manning 出版社。
[ref1] 是本文后续内容的基础文献。这是一本超过 800 页的详尽著作,探讨了在两种不同场景下使用 Hibernate 的方法:即有 ORM 与无 JPA 的情况。 对于使用 JDK 1.4 或更低版本的开发者而言,不使用 JPA 的 Hibernate 方案确实仍然适用, 因为JPA是随JDK 1.5版本才出现的。
读完全书四分之三的内容并略读了其余部分后,我发现这本书中的所有内容都很有用。熟悉Hibernate的用户应该已经掌握了这800页中几乎所有的信息。Christian Bauer和Gavin King的阐述非常详尽,但很少会描述那些我们永远不会遇到的情况。全书都值得一读。 本书采用教学式写作风格:作者真正致力于将所有知识点都阐释得通俗易懂。 本书既适用于配合 JPA 使用,也适用于不使用该框架的 Hibernate 场景,这对仅对其中某一项技术感兴趣的读者来说可能造成困扰。例如,作者通过大量实例,分别在两种场景下阐述了关系型数据库与对象之间的桥梁。 由于JPA深受Hibernate启发,两者所采用的概念非常接近。但它们仍存在一些差异。因此,对Hibernate成立的某些情况在JPA中可能不再适用,这最终会导致读者产生困惑。
作者在 EJB3 容器的背景下展示了三层应用的示例。他们并未提及 Spring。 我们将通过一个示例看到,Spring 实际上比 [ref1] 中使用的 JBoss EJB3 容器更易于使用,且具有更全面的适用性。 尽管如此,《Java Persistence with Hibernate》仍是一本极佳的书籍,我推荐大家阅读,因为其中涵盖了关于ORM的所有基础知识。
对于初学者来说,使用 ORM 相当复杂。
- 配置关系型数据库与对象之间的桥接需要理解一些概念。
- 还有持久化上下文的概念,以及其中对象处于“持久化”、“脱离”和“新建”状态的概念
- 还涉及持久化相关的机制(事务、连接池),这些通常由容器提供的服务
- 还需要进行性能方面的配置(二级缓存)
- ……
我们将通过实例来介绍这些概念。我们不会过多地进行理论探讨。我们的目标很简单,就是每次都让读者能够理解该实例并将其内化,直到能够自己对其进行修改或在其他场景中重现它。
1.3. 使用工具
本文档中的示例使用了以下工具。部分工具在附录中进行了说明(下载、安装、配置、使用)。在此情况下,我们将提供段落编号和页码。
- JDK 1.6(第 5.1 段)
- Eclipse 3.2.2 Java开发版 IDE(第5.2节)
- Eclipse插件 WTP(Web Tools Package)(第5.2.3节)
- Eclipse 插件 SQL 资源管理器(第 5.2.6 节)
- Eclipse Hibernate Tools 插件(第 5.2.5 节)
- Eclipse 插件 TestNG(第 5.2.4 节)
- Tomcat 5.5.23 Servlet 容器(第 5.3 节)
- SGBD Firebird 2.1(第 5.4 节)
- SGBD MySQL5(第 5.5 段)
- SGBD PosgreSQL(第 5.6 段)
- SGBD Oracle 10g Express(第 5.7 段)
- SGBD SQL Server 2005 Express(第 5.8 段)
- SGBD HSQLDB(第 5.9 段)
- SGBD Apache Derby(第 5.10 节)
- Spring 2.1(第 5.11 节)
- EJB3 容器(来自 JBoss)(第 5.12 节)
1.4. 下载示例
在本文档的网站上,所研究的示例可下载为 zip 文件,解压后将生成以下文件夹:
![]() |
- [1]:示例的目录结构
- [2]:<annexes>文件夹包含ANNEXES第5段中介绍的内容。特别是,<jdbc>文件夹包含教程示例中使用的SGBD的JDBC驱动程序。
- 在 [3] 中:<lib> 文件夹将教程中使用的各种 .jar 存档文件分成了 5 个子文件夹
- 在 [4] 中:<lib/divers> 文件夹包含以下归档文件: - SGBD 中的 JDBC 驱动程序 - 单元测试工具 [testNG] - 日志工具 [log4j]
![]() |
- [5]:包含 JPA/Hibernate 实现以及 Hibernate 所需第三方工具的归档文件
- [6]:JPA/Toplink 实现的归档
- [7]:Spring 2.x 及 Spring 所需第三方工具的归档
- 在 [8] 中:EJB3 容器的归档文件,来自 JBoss
![]() |
- 在 [9] 中:<hibernate> 文件夹汇总了使用持久层 JPA/Hibernate 处理的示例
- 在 [10] 中:<hibernate/direct> 文件夹汇集了直接利用 JPA 层与 [Main] 类型程序配合使用的示例。
- 在 [11] 和 [12] 中: 这些示例展示了在多层架构中,JPA层通过[metier]和[dao]层进行调用的情况,这属于常规使用场景。[metier]和[dao]层所使用的服务(连接池、事务管理器) 由 [metier] 和 [dao] 层使用的服务,由 Spring [11] 或 JBoss EJB3 [12] 提供。
![]() |
- 在 [13] 中: <toplink>文件夹采用了<hibernate>文件夹中的示例 [9],但这次使用了 JPA/Toplink 持久层,而非 JPA/Hibernate。[13] 中没有 <jbossejb3> 文件夹,因为无法使一个示例正常运行,该示例中持久层由 Toplink 提供,而服务由 EJB3 容器(来自 JBoss)提供。
- 在 [14] 中:一个 <web> 文件夹包含三个 Web 应用程序示例,其持久层为 JPA:
- [15]:一个使用 Spring / JPA / Hibernate 的示例
- [16]:使用 Spring / JPA / Toplink 的相同示例
- [17]:使用 JBoss EJB3 / JPA / Hibernate 实现的相同示例。该示例无法运行,可能是由于尚未查明的配置问题。 但仍保留此示例,以便读者深入研究并可能找到解决此问题的方法。
本教程经常提及该目录结构,特别是在测试所学示例时。建议读者下载并安装这些示例。此后,我们将上述示例目录结构称为 <exemples>。
1.5. 示例的 Eclipse 项目配置( )
示例使用“用户”库。这些是以同一名称打包的 .jar 归档文件。当将此类库包含在 Java 项目的 classpath 中时,其中包含的所有归档文件都会被纳入该类路径。下面我们来看一下在 Eclipse 中如何操作:
![]() |
- 在 [1] 中:[Window / Preferences / Java / Buld Path / User Libraries]
- 在 [2] 中:创建一个新库
- 在 [3]:为其命名并确认
![]() |
- 在 [4]:选择将纳入该库的 JAR 文件[jpa-divers]
- 在 [5] 中:选择 <exemples>/lib/divers 文件夹中的所有 JAR 文件
![]() |
- 在 [6] 中:已定义用户库 [jpa-divers]
- 在 [7] 中:重复相同步骤创建另外 4 个库:
库 | 图书馆的公鹅档案 |
<示例>/lib/hibernate | |
<示例>/lib/toplink | |
<示例>/lib/spring | |
<示例>/lib/jbossejb3 |













