1. 前言
在使用Java进行应用开发时,访问数据库往往是一项必备的功能,对此,早期的做法要么是使用原始的JDBC,要么使用标准但笨重的EJB2,后来通过开发者社区和软件厂商的不断努力,开发出了大量的轻量级持久化工具,例如商业软件TopLink,开源软件Hibernate,以及标准化但分歧严重的JDO。在这场混战中,毫无疑问,Hibernate获得了胜利。但是,时间走到了今天,情况又发生了一点变化,那就是Java社区为统一持久层技术而做出的努力终于有了成果——Java EE 5规范(JSR 244)最终发布了。
Java EE 5的持久层技术包含在EJB 3.0(JSR-220)中,该技术从企业bean组件体系(新的组件体系仅包含session bean和message-driven bean)中独立出来,重新命名为Java Persistence API(即JPA)
[
注]。从技术上看,这个全新的规范已经具备了一统天下的能力:轻量级;即能宿主于容器,也能独立运行;元数据映射;标准化。
从目前的情况看来,JPA似乎无可阻挡,大多数现存的持久层工具,包括Hibernate、某些JDO实现、TopLink等在内,都已经提供了JPA兼容接口。面对新的形势,作为一个开发者,熟悉这个新的编程模式,并对各类持久层技术进行选择,就成了一个无法回避的现实。
幸运的是,对Hibernate开发者来说,这个学习或转变过程是非常容易的;此外,新的编程模型是如此的简单,即使是一个Hibernate新手,也可以快速的掌握。本文试图通过一个完整的示例,对Hibernate的各类编程模型进行说明,以便于开发者以更适合自己的方式使用Hibernate。
[
注]
EJB 3.0规范被组织成以下三个文档:
l EJB 3.0 Simplified API
l EJB Core Contracts and Requirements
l Java Persistence API
其中
JPA在第三个文档中进行定义。
2. 选择合适的方案
2.1 Hibernate构件
Hibernate是一个采用LGPL协议的开放源代码软件,为应用程序提供功能强大、性能出众的对象/关系持久化和查询服务。
从版本3开始,Hibernate被分解为三个主要的组成部分:
l Hibernate Core:通过XML映射元数据和原生编程接口,提供了持久化。当前版本为3.2.0 CR2。
l Hibernate Annotiations:实现了JPA规范中的元数据映射;此外也包含了Hibernate特有的O/R映射扩展和验证框架。当前版本为3.2.0 CR1。
l Hibernate EntityManager:实现了JPA规范中的编程接口和生命周期规则部分。当前版本为3.2.0 CR1。
可以看出,对于持久化的一些关键技术,Hibernate既提供了自己特有的实现方式,也提供了标准化JPA的实现,从而为开发者提供了更多的选择。例如,对于O/R映射方式,我们可以选择:
l 使用Hbm映射文件(通过Hibernate Core提供)
l 使用JPA元数据及Hibernate扩展(通过Hibernate Annotations提供)
对于编程接口,我们可以选择:
l Hibernate原生编程接口(通过Hibernate Core提供)
l JPA编程接口(通过Hibernate EntityManager提供)
2.2 构件组合
实际上,上述两类技术方案是可以任意组合的,选择何种技术路线只取决于你的实际需求:
1)传统方式
在原生Hibernate编程接口下使用Hbm映射。这是一个代价最小的选择,你不用引入任何新的技术元素,就可以继续享用Hibernate带来的任何好处。当然,这也意味着你必须绑定到Hibernate这部战车上(其实我们丝毫不用为Hibernate的未来担心,连续发生的两次收购,只会使Hibernate更加强大;即使需要转换到JPA体系中,那也是一件非常容易完成的任务)。
2)只引入Annotation映射的方式
在原生Hibernate编程接口下使用JPA映射。我们可以不用改变代码和学习新的API,仅仅是利用Java SE 5.0 Annotation方式的JPA元数据映射,即可简化开发工作。而且,基于Annotation的映射方式是非常直观的,只需要很小的代价即可掌握。
3)只引入JPA编程接口的方式
继续使用hbm映射方式,但引入新的JPA编程接口。这种方式实际上不具备现实意义:因为这使你继依赖于Hibernate,但又不能充分利用Hibernate的全部能力。
4)完整的JPA方式
同时使用JPA编程接口和JPA元数据映射。这样,你的代码就彻底摆脱了对Hibernate的依赖,只要你愿意,就可以移植到任何JPA兼容的环境中。
Hibernate提供了足够的灵活性,开发者可以根据自己的需求,使用上述任何一种编程模式。
2.3 Spring集成
没错,就是你经常听到,或者经常使用的那个Spring。尽管Spring的设计目标之一是为了让我们减少对第三方类库的依赖性,但我们还是心甘情愿的依赖于Spring。
Spring通过几个包对持久化提供了支持,与我们话题相关的,则主要是org.springframework.orm.hibernate3和org.springframework.orm.jpa。它们分别提供了对hibernate3和JPA的支持,目的在于提高代码效率和依赖注入。要利用hibernate3和JPA的完整特性,唯一可用的spring版本是2.0,目前处于m5阶段(尽管本文所使用的软件包,包括spring在内,都不是其最终发行版,但它们都足够可用了;此外,对于掌握一个有前途的新技术来说,付出一些调试的代价是值得的)。
使用Spring持久化支持的常用方法是使用DAO模式。在80%的情况下,对于Hibernate来说,也许只需要与org.springframework.orm.hibernate3.support.HibernateDaoSupport和org.springframework.orm.hibernate3.HibernateTemplate打交道就够了;相对应于JPA,则是org.springframework.orm.jpa.support.JpaDaoSupport和org.springframework.orm.jpa.JpaTemplate。
下面是一个典型的应用场景:
SomeDao是一个接口,定义了一组领域相关的持久化API;在不同的情况下,它可能有多种实现,比如Hibernate实现和JPA实现。不同的实现方式中,分别继承了相应的抽象基类DaoSupport,通过该基类,可以获得相应的Template类,以获得编程的便利性。在种Template类(这里是HibernateTemplate和JpaTemplate)中可以进行针对实体的各种操作,如保存、更新、删除、查询等。
当上述API不足以完成某些功能的时候,你可以非常容易的得到hibernate/JPA的原生API,甚至是返回到原始的JDBC,Spring在效率和功能之间取得了很好的平衡。
3. 从一个示例程序开始
为了验证和解释本文的观点,开发出一个示例程序,该示例程序演示了ORM应用中的一些实际需求:包括实体间的持久化映射和实体间的多种关系。
在此可以下载示例程序。目前的示例程序已经包含了“使用Annotation方式的持久化”和“使用JPA方式的持久化”两种应用场景完整演示。
示例程序在Eclipse 3.1.2环境下编写,并需要Java SE 5.0环境支持,数据库为hsqldb 1.8。
3.1 模型
示例程序模拟了一个与出版物相关的实体模型。这个模型可以用两种方式进行描述,一种的面向对象的方式,一种是面向数据库的方式。
采用面向对象的方式进行建模,得到如下类图:
在上图示例的模型中,有6类实体:
l Publisher:出版商。每个出版商都有一个描述信息(PubInfo),出版商可以发行多种出版物(Title)。
l PubInfo:出版商信息。
l Title:出版物,可能包括图书(Book),以及其他可能的种类(如杂志)。
l Book:出版物的一种。
l Author:作者。每个作者都有联系方式(ContactInfo),作者自己可以创作多种出版物,当然也可以和其他作者合著。
l ContactInfo:作者的联系方式。
实体间的关系包括:
l 一对一:一个出版商有一个描述信息。
l 多对一:一个出版商有多个出版物。
l 多对对:一个出版物可以有多个作者,一个作者可以有多部出版物
l 继承:出版物有多种类型,但它们都有一些共同的特征。
l 嵌入:作者有一个联系方式的属性。
面向数据库的模型可以用关系-实体图进行描述,在这里我们只用自然语言进行简单说明:
l Publisher表:出版商,与Publisher类对应。
l PubInfo表:出版商信息,与PubInfo类对应。
l Title表:出版物,与Title类对应。
l Book表:图书,与Book类对应
l Author表:作者,与Author类和ContactInfo两个类对应。
l TitleAuthor表:关系表,对应于Author表和Title表之间的多对多关系,没有直接对应的Java类。
3.2 程序结构
3.2.1 源代码
示例程序的代码组织如下:
包
|
类
|
说明
|
app.hbdemo
|
ApplicationContextFactory
|
加载spring的工具类
|
|
Test
|
程序入口
|
app.hbdemo.model
|
Author
|
作者
|
|
Book
|
图书
|
|
ContactInfo
|
联系方式
|
|
PubInfo
|
出版商信息
|
|
Publisher
|
出版商
|
|
Title
|
出版物
|
app.hbdemo.dao
|
PubsDao
|
DAO
接口
|
|
PubsDaoHibernate
|
DAO
的Hibernate实现
|
|
PubsDaoJpa
|
DAO
的JPA实现
|
3.2.2 配置文件
示例程序采用配置文件进行声明性编成,这些配置文件是:
类别
|
文件名
|
用途
|
组件配置
|
applicationContext.xml
|
主要的配置文件。定义了dataSource组件和导入文件。
|
|
hibernate-annotations.xml
|
定义了sessionFactory组件,为hibernate原生编程接口提供O/R映射信息。
|
|
hibernate-jpa.xml
|
定义了entityManagerFactory组件,与下面的
persistence.xml
一起
为JPA编程接口提供O/R映射信息。
|
|
dao-hibernate.xml
|
定义了Hibernate实现方式下的DAO组件。
|
|
dao-jpa.xml
|
定义了JPA实现方式下的DAO组件。
|
其他
|
META-INF/persistence.xml
|
JPA
风格的配置信息
|
3.2.3 数据库
示例数据库采用了In-Process模式的hsqldb,其数据库文件位于database目录下。
3.2.4 类库
依赖的主要类库包括:
l hibernate3.jar(Hibernate Core 3.2.0 cr2)
l hibernate-annotations.jar(Hibernate Annotations 3.2.0 cr1)
l hibernate-entitymanager.jar(Hibernate EntityManager 3.2.0 cr1)
l ejb3-persistence.jar(ejb3 persistence proposed final draft)
l spring.jar(Spring framework 2.0 m5)
l Hsqldb.jar(Hsqldb 1.8.0.4)
3.3 运行
示例程序采用eclipse开发,因此运行它的最简单方式就是将工程文件直接导入到eclipse中。
如果出现无法编译或运行错误,请检查一下内容:
l JRE System Library设置是否正确;
l hbdemolib设置是否正确,如果发生找不到某些类的错误,请设置好这个用户自定义库。
示例程序目前演示了hibenrate的两种编程模式:
使用Annotations方式的持久化和
使用JPA方式的持久化,要采用第一种方式,请取保配置文件applicationContext.xml中配置了如下内容:
<!--
使用
annotation
元数据映射和
hibernate
原生编程接口
-->
<!--<import resource="hibernate-annotations.xml"/>
<import resource="dao-hibernate.xml"/>-->
<!--
使用
annotation
元数据映射和
JPA
编程接口
-->
<import
resource
="hibernate-jpa.xml"/>
<import
resource
="dao-jpa.xml"/>
|
要采用第二种方式,请确保在配置文件applicationContext.xml中配置了如下内容:
<!--
使用
annotation
元数据映射和
hibernate
原生编程接口
-->
<import
resource
="hibernate-annotations.xml"/>
<import
resource
="dao-hibernate.xml"/>
<!--
使用
annotation
元数据映射和
JPA
编程接口
-->
<!--<import resource="hibernate-jpa.xml"/>
<import resource="dao-jpa.xml"/>-->
|