使用 Apache OpenJPA 开发 EJB 3.0 应用,第 1 部分: OpenJPA 与 EJB 3.0
2010-04-19 00:00:00 来源:WEB开发网关系型数据库与面向对象
几乎所有的企业应用都需要持久化数据,没有数据持久化需求的企业应用在现在的市场环境下几乎是不可能出现的。由于关系型数据的普及,通常我们提到数据持久化时,一般指的是将数据持久化到关系型数据库中。关系型数据是一种结构化的数据管理方式,开发者只能通过 SQL 来操作数据库。
Java 语言天生就是一门面向对象的编程语言,在 Java 的世界中,被处理的内容都被组织成一个一个的对象,对象和对象之间存在着继承、引用关系,这样的关系无法通过简单的方式直接反应到关系型数据库中。因此在关系型数据库与面向对象之间便存在着阻抗失谐(impedance mismatch)。
我们通过一个简单的例子来说明这种阻抗失谐给企业应用开发者带来的困难。假设在企业应用中存在三个 Java 类:Animal、Fish 和 Dog,其中 Fish、Dog 都是 Animal 的子类。在 Java 世界中,Fish、Dog 都可以被作为 Animal 对象处理。但是如果我们换到关系型数据库中,这三个对象通常都保存在各自对应的表中,假设分别对应 Animal 表、Fish 表和 Dog 表,如果要维护 Animal 和 Fish 的继承关系,我们就需要使用 SQL 的联合查询语句查出 Animal 的所有属性和 Fish 的所有属性,并且使用某种外键进行关联:
Select animal.*,fish.* form animal,fish where animal.id = fish.id
从这个简单的例子中我们就可以看出,一个企业应用开发者需要同时掌握面向对象和关系型数据库的两种思想,而且还必须保证它们之间的映射是正确的,否则无法保证企业应用的正确性,这对于企业应用开发者是个挑战,因此 Java 社区一直在寻求如何将面向对象和关系型数据库思想简单的统一起来的途径,这方面的努力促进了持久化技术的发展。
发展中的持久化技术
持久化是企业应用开发的核心需求之一,最近几年以来,它也成为 Java 社区中最热门的话题之一,在 Java 社区努力解决持久化数据管理的过程中,曾经涌现出了非常多的技术方案试图解决这个问题,从最早的序列化,到 JDBC、JDO、ORM、对象数据库、EJB 2.X,然而这些技术都存在着各种各样的局限,影响他们成为最好的选择。下面我们简单的回顾一下 Java 社区中那些曾经试图为持久化数据管理提供完整解决方案的技术。
序列化
序列化是最早出现的、管理持久化数据的实现方案,也是 Java 语言中内置的数据持久化解决方案。它的工作原理是将对象转化为字节流,生成的字节流能够通过网络传输或者保存在文件中。序列化非常易于使用,但是局限性也非常大,由于序列化必须一次将所有对象全部取出,这限制了它在处理大量数据情形下的应用,同时它也无法在更新失败的情况下撤销对对象的修改,这使它无法用于对数据一致性要求严格的应用中。多线程或者多个应用不能同时并发地、互不冲突地读写同一个序列化数据,也不能提供查询功能。
JDBC
很多企业应用的开发者选择使用 JDBC 管理关系型数据库中的数据。相对序列化而言,JDBC 克服了很多缺点:它支持处理大量的数据,能够保证数据的一致性,支持信息的并发访问,提供 SQL 查询语言查找数据。不幸的是,JDBC 没有提供序列化所具有的易用性。JDBC 所使用的关系模型不是为保存对象而设计的,因此迫使开发者选择在处理持久数据时放弃面向对象编程,或者自己去开发将面向对象特性(比如:类之间的继承)和关系型数据库进行映射的专有解决方案。
关系对象映射(Object Relational Mapping,ORM)
ORM 是目前完成对象和关系数据表之间的映射最好的一种技术, 这些 ORM 框架处理对象和关系数据库之间的协调工作,将开发者从这部分工作中解脱出来,集中精力处理对象模型。阻碍 ORM 发展的问题是,现有的每一种 ORM 产品都有自己特有的 API,开发者只能将自己的代码绑定到某一个框架提供商的接口上,这种状况形成了厂商锁定,意味着一旦该框架提供商无法解决系统中出现的严重错误,或者因为其它的原因转而采用其它的框架,将会给开发者的企业应用带来极大的困难,唯一的解决办法是重写所有的持久化代码。
对象数据库(Object DataBase)
已经有一些软件公司选择了开发为保存对象而特别设计的对象数据库,而不是选择将对象映射到关系型数据库上。这种解决方案通常比使用对象/关系映射更加易于使用。和 ORM 相同的问题是,对象数据库的访问接口并没有标准化,因此非常容易形成厂商锁定的局面。与此同时,放弃已经成熟的关系数据库而转向未知的对象数据库让非常多的企业决策者犹豫不决。而且目前为对象数据库而设计的分析工具太少,无法满足企业的需求。而且现实情况下,每一个企业基本上都有大量的已有数据保存在关系数据库中,要从关系数据库转向对象数据库对企业而言也需要大量工作。
EJB 2.X
EJB 2.X 实体 Bean 是管理持久化数据的组件框架,和 ORM 解决方案一样,EJB 2.X 实体 Bean 提供持久化数据的面向对象视图。和 ORM 解决方案不一样的是,EJB 2.X 实体 Bean 不仅仅局限于数据库,它展示的信息可能来自于 EIS(Enterprise Information System)或者其他持久化设备。EJB 2.X 实体 Bean 最大的局限是规定了太过于严格的标准,这些标准保证了企业应用能够在不同的 EJB 容器之间可以移植,但是也让 EJB2.X 实体 Bean 规范变得非常复杂并难于使用。而且 EJB 2.X 标准在面向对象特性处理方面的支持非常有限,无法支持继承、多态和复杂关系等面向对象的高级特性。EJB 2.X 实体 Bean 只能在重量级的、价格昂贵的 EJB 容器中运行,这对应用 EJB 2.X 实体 Bean 开发企业应用提出了更高的要求,加重了企业的经济压力。
Java 数据对象(Java Data Object,JDO)
JDO 是 Java EE 标准中另外一个支持管理持久化数据的规范,JDO 规范使用和 JPA 非常类似的 API,只是通常是通过 JCA 技术集成到应用服务器上。但是 JDO 是针对轻量级容器而设计的,不能够支持容器级别的声明式安全、事务特性,也无法对远程方法调用提供支持。
EJB 3.0 规范
2006 年 5 月 2 日,EJB 3.0 规范最终版由 JCP(Java Community Process) 正式公布,标准号为 JSR(Java Specification Request)220。EJB 3.0 规范的发布为企业应用开发者提供了一种全新的、简化的 API。制定这组 API 的目标是让开发变得更加容易,相对于以前版本的 EJB 规范,这组 API 也更加简单。Java Persistence API 是 EJB 3.0 中负责处理持久化数据管理的部分,目标是为开发者处理持久化数据库管理提供标准支持,也成为 Java EE 容器提供商必须遵守的标准。
EJB 3.0 规范由三部分组成:EJB3.0 Simplified API、EJB 核心规范(EJB Core Contracts and Requirements)和 JPA(Java Persistence API)。
Simplified API
Simplified API 部分主要规定了基于 EJB 3.0 标准开发企业应用时所需要遵守的 Bean 类和接口要求、这些 API 的使用方式以及容器支持等多方面的内容。还详细的规定了 EJB3.0 中除 Java Persistence API 部分之外的 EJB 实现所支持的注释(Annotation)。规范中还有专门章节讲解 EJB 3.0 和此前的 EJB 规范如何同时工作,以及如何将此前已经开发好的企业应用移植到 EJB 3.0 容器中。其中的 Persistence 的内容放在了 JPA 规范中。
EJB 核心规范
EJB 核心规范中首先描述了 EJB 在企业应用中的角色、EJB 规范的体系结构,确定了支持 EJB 标准的容器应该遵守的准则和要求。随后从多个角度详细的介绍了 EJB 体系中各部分的功能需求和实现要求,包括 Session Bean、消息驱动 Bean(Message-Driven Bean)、事务、安全管理、部署描述符等。其中的 Persistence 的内容放在了 JPA 规范中。由于 EJB 3.0 规范并不排斥之前的 EJB 规范,因此 EJB 2.X 和 EJB 1.X 中的内容也保留在了 EJB 核心规范中。
Java Persistence API(JPA)
EJB 2.X 和 EJB 1.X 规范中的实体 Bean(EntityBean)部分都难以使用,使持久化成为 EJB 规范的一个软肋,影响了 EJB 标准发挥更大的作用,自然而然的,JPA 成为了 EJB3.0 规范中被关注最多的部分。JPA 规范部分详细的介绍了 JPA 中实体 Bean 新的定义,并介绍了实体 Bean 支持的注释、全新的查询语言、实体管理接口、容器实现规范等内容。
JPA 标准中引入了新的实体概念,每一个实体都是一个普通的 Java 类,不需要继承任何其他的接口或者扩展某个指定类,这个 Java 类必须使用 javax.persistence.Entity 进行注释。JPA 标准中还提供了包括 javax.persistence.Table、javax.persistence.Id 等在内的多个注释,用于完成实体和数据库之前的映射。JPA 中引入了新的查询语言 JPQL(Java Persistence Query Language),JPQL 允许开发者采用面向对象的查询语言来查找实体,这些实体持久化在关系型的数据库中,”select a from Animal a where a.name=’a’” 是一个 JPQL 的例子。其中的 Animal 是一个 Java 类,而不是关系型数据库中的一个表或者视图。除了简单的查询功能之外,JPQL 中还能够支持 Group、Order 等通常只有 SQL 才能提供的高级功能。JPA 标准中还规定了在 Java EE 环境中和非 Java EE 环境中使用 JPA 时的差异,以及 Java EE 环境中容器的职责等。
JPA 体系架构
JPA 中定义一套类和接口用于实现持久化管理和对象/关系的映射,下面这张图中显示了 JPA 的主要组件以及它们之间的相互关系。
图1 JPA 主要组件和相互关系
EntityManagerFactory
EntityManagerFactory 是 EntityManager 的工厂类,负责创建 EntityManager 对象。
EntityManager
EntityManager 是 JPA 应用中使用的基本对象,通过它提供的相应方法可以管理持久化对象,也可以新建或者删除持久化对象。EntityManager 还负责创建 Query 实例。在容器外使用时,EntityManagerFactory 和 EntityManager 之间是一对一的关系。
Entity
EntityTransaction 提供 Entity 操作时需要的事务管理,和 EntityManager 是一对一的关系。在查询操作时不需要使用 EntityTransaction,而在对象持久化、状态更新、对象删除等情况下则必须使用显式的使用 EntityTransaction 的相关方法管理事务。
Query
Query 是查询实体的接口,Query 对象可以从 EntityManager 中获得。根据 EJB 3.0 规范中的描述,Query 接口需要同时支持 JPQL 和原生态 SQL 两种语法。
Persistence
Persistence 是一个工具类,负责根据配置文件提供的参数创建 EntityManagerFactory 对象。
下面的代码演示了如何通过 JPA 提供的接口和 JPQL 查询语言完成实体查询和更新的例子,例子中的代码假定运行在非 Java EE 环境中。
清单 1 在非 Java EE 环境使用 JPA 接口的例子
1. /*
2. * Persistence 类获取 EntityManagerFactory 实例;
3. * 一般 EntityManagerFactory 实例被缓存起来重复使用,
4. * 避免重复创建 EntityManagerFactory 实例引起的性能影响
5. */
6. EntityManagerFactory factory =
7. Persistence.createEntityManagerFactory (“mysql”);
8.
9. // 从 EntityManagerFactory 实例 factory 中获取 EntityManager
10. EntityManager em = factory.
11. createEntityManager(PersistenceContextType.EXTENDED);
12.
13. // 实体的更新需要在事务中运行
14. EntityTransaction tx = em.getTransaction ();
15. tx.begin ();
16.
17. // 查找所有公司中的女性雇员
18. Query query = em.createQuery ("select e from Employee e "
19. + " where e.sex = 'femail'");
20. List results = query.getResultList ();
21.
22. // 给所有女性雇员增加半天假期
23. for (Object res : results){
24. Employee emp = (Employee) res;
25. emp.setHoliday (emp.getHoliday () +0.5);}
26.
27. // 提交事务(持久化所有更新)
28. tx.commit ();
29. em.close ();
30. factory.close ();
下面的代码显示了在 EJB 容器中开发 JPA 应用时的接口使用情况,由于容器中的 EntityManager 是注入的,事务也是声明式的,因此在容器中完成上面的业务逻辑要简单得多。
清单 2 在容器中运行的 JPA 例子
1. /*
2. * 在容器中运行 JPA 应用时,EntityManager 接口的实例”em”
3. * 是通过 @Resource 注释注入的。事务也通常是声明式的。
4. */
5. // 查找所有公司中的女性雇员
6. Query query = em.createQuery ("select e from Employee e "
7. + " where e.sex = 'femail'");
8. List results = query.getResultList ();
9.
10. // 给所有女性雇员增加半天假期
11. for (Object res : results){
12. Employee emp = (Employee) res;
13. emp.setHoliday (emp.getHoliday () +0.5);}
JPA 的优势
JPA 标准制定过程中充分吸收了目前已经出现的所有持久化技术的所有优点,摒弃了它们存在的局限,使 JPA 在简单易用、查询能力等方面表现突出。
标准化
JPA 是 JCP 组织发布的 Java EE 标准之一,因此任何声称符合 JPA 标准的框架都遵循同样的架构,提供相同的访问 API,这保证了基于 JPA 开发的企业应用能够经过少量的修改就能够在不同的 JPA 框架下运行。
对容器级特性的支持
JPA 框架中支持大数据集、事务、并发等容器级事务,这使得 JPA 超越了简单持久化框架的局限,在企业应用发挥更大的作用。
简单易用,集成方便
JPA 的主要目标之一就是提供更加简单的编程模型:在 JPA 框架下创建实体和创建 Java 类一样简单,没有任何的约束和限制,只需要使用 javax.persistence.Entity 进行注释;JPA 的框架和接口也都非常简单,没有太多特别的规则和设计模式的要求,开发者可以很容易的掌握。JPA 基于非侵入式原则设计,因此可以很容易的和其它框架或者容器集成。
可媲美 JDBC 的查询能力
JPA 定义了独特的 JPQL(Java Persistence Query Language),JPQL 是 EJB QL 的一种扩展,它是针对实体的一种查询语言,操作对象是实体,而不是关系数据库的表,而且能够支持批量更新和修改、JOIN、GROUP BY、HAVING 等通常只有 SQL 才能够提供的高级查询特性,甚至还能够支持子查询。
支持面向对象的高级特性
JPA 中能够支持面向对象的高级特性,比如类之间的继承、多态和类之间的复杂关系,这样的支持能够让开发者最大限度的使用面向对象的模型设计企业应用,而不需要自行处理这些特性在关系数据库的持久化。
下面的这个表格中列出了当前常用持久化技术的优缺点。
表 1 持久化技术的优缺点
支持内容: | 序列化 | JDBC | ORM | ODB | EJB 2.X | JDO | EJB 3 (JPA) |
Java 对象 | Yes | No | Yes | Yes | Yes | Yes | Yes |
高级 OO 原理 | Yes | No | Yes | Yes | No | Yes | Yes |
事务完整性 | No | Yes | Yes | Yes | Yes | Yes | Yes |
并发 | No | Yes | Yes | Yes | Yes | Yes | Yes |
大数据集 | No | Yes | Yes | Yes | Yes | Yes | Yes |
现有 Schema | No | Yes | Yes | No | Yes | Yes | Yes |
关系型和非关系型数据存储 | No | No | No | No | Yes | Yes | No |
查询 | No | Yes | Yes | Yes | Yes | Yes | Yes |
严格的标准 / 可移植 | Yes | No | No | No | Yes | Yes | Yes |
简单易用 | Yes | Yes | Yes | Yes | No | Yes | Yes |
OpenJPA 简介
OpenJPA 是 Apache 组织提供的开源项目,它实现了 EJB 3.0 中的 JPA 标准,为开发者提供功能强大、使用简单的持久化数据管理框架。OpenJPA 封装了和关系型数据库交互的操作,让开发者把注意力集中在编写业务逻辑上。OpenJPA 可以作为独立的持久层框架发挥作用,也可以轻松的与其它 Java EE 应用框架或者符合 EJB 3.0 标准的容器集成。
除了对 JPA 标准的支持之外,OpenJPA 还提供了非常多的特性和工具支持让企业应用开发变得更加简单,减少开发者的工作量,包括允许数据远程传输/离线处理、数据库/对象视图统一工具、使用缓存(Cache)提升企业应用效率等。
数据远程传输 / 离线处理
JPA 标准规定的运行环境是 "本地" 和 "在线" 的。本地是指 JPA 应用中的 EntityManager 必须直接连接到指定的数据库,而且必须和使用它的代码在同一个 JVM 中。在线是指所有针对实体的操作必须在一个 EntityManager 范围中运行。这两个特征,加上 EntityManager 是非序列化的,无法在网络上传输,导致 JPA 应用无法适用于企业应用中的 C/S 实现模式。OpenJPA 扩展了这部分接口,支持数据的远程传输和离线处理。
数据库 / 对象视图统一工具
使用 OpenJPA 开发企业应用时,保持数据库和对象视图的一致性是非常重要的工作,OpenJPA 支持三种模式处理数据库和对象视图的一致性:正向映射(Forward Mapping)、反向映射(Reverse Mapping)、中间匹配(Meet-in-the-Middle Mapping),并且为它们提供了相应的工具支持。
正向映射 是指使用 OpenJPA 框架中提供的 org.apache.openjpa.jdbc.meta.MappingTool 工具从开发者提供的实体以及在实体中提供的对象 / 关系映射注释生成相应的数据库表。
反向映射 是指 OpenJPA 框架中提供的 org.apache.openjpa.jdbc.meta.ReverseMappingTool 工具从数据库表生成符合 JPA 标准要求的实体以及相应的对象 / 关系映射注释内容。
中间匹配 是指开发者负责创建数据库表、符合 JPA 标准的实体和相应的对象 / 关系映射注释内容,使用 OpenJPA 框架中提供的 org.apache.openjpa.jdbc.meta.MappingTool 工具校验二者的一致性。
使用缓存提升效率
性能是企业应用重点关注的内容之一,缓存是提升企业系统性能的重要手段之一。OpenJPA 针对数据持久化提供多种层次、多方面的缓存支持,包括数据、查询、汇编查询的缓存等。这些缓存的应用可以大幅度的提高企业应用的运行效率。
总结
本文中,我们回顾了关系型数据库和面向对象之间的阻抗失谐问题和 Java 社区中为解决对象持久化而做出的努力,这些努力促进了 Java 中对象持久化的发展,但是没有任何一种技术象 EJB 3.0 标准中的 JPA 标准来的这么简单和高效。
JPA 标准中使用注释声明数据库表和对象之间的映射,开发者通过操作实体就可以完成对数据库的操作。OpenJPA 是 Apache 组织提供的开源项目,它实现了 EJB 3.0 中的 JPA 标准,为开发者提供功能强大、使用简单的持久化数据管理框架。除此之外 OpenJPA 还为开发者提供了更多额外的特性如数据远程传输/离线处理、数据库/对象视图统一工具、使用缓存提升企业应用效率等。
本系列 的后续文章将分别对 Apache OpenJPA 提供的标准特性和额外的增强特性进行详细介绍。在接下来的一篇文章中,请跟随我们开始 OpenJPA 编程之旅,学习如何下载、安装 OpenJPA 以及配置开发环境,并以此为起点开始第一个 OpenJPA 应用程序的开发。
赞助商链接