概述
在这个EJB 3.0学习系列中的第二部分,你将学到如何使用POJO开发数据模型,还有如何透明的将那些数据对象模型与关系型数据库相互映射。使用EJB 3.0中注释式的实体bean,开发数据库驱动的应用就是小菜一碟。
请阅读有关EJB 3.0的整个学习系列:
第一部分:使用注释开发POJO服务
第二部分:带来简便的的持久化技术
在第一部分中,我讨论了在企业级JavaBean 3.0(EJB)中注释驱动的POJO编程模型。我阐述了如何开发POJO服务,如何让容器服务使用POJO, 如何使用依赖注入来组合应用。这些POJO服务主要是用来封装应用的商业逻辑。在商业逻辑的背后,现今的大多数应用都有由一个高性能的关系型数据库作为支撑的数据模型层。
在第二部分中,我将讨论EJB 3.0实体bean如何利用POJO和注释的优势来极大地简化你的数据模型以及它们与后台关系数据库的持久化。在我们进入EJB 3.0实体bean的细节之前,让我们先来看一下为什么对于企业级Java应用,数据模型和持久化是如此巨大的一个挑战。
对象-关系映射(ORM)
在Java虚拟机中,所有的数据都被模型化并且封装在了类和对象的树结构中。但是在后端的关系型数据库中,数据被模型化为关系型表,它们通过共享的键域(外键)相互关联起来。相同的数据却有两个视图,这对企业级Java的开发者来说是一个艰难的挑战:当你想从持久化的数据存储中存取数据时,你必须在对象与关系表达之间来回转换,这一过程叫做对象-关系映射(ORM)。在Java EE(Java企业版,以前叫做J2EE),你可以通过两个途径来实现对象-关系映射。
手动的:你可以使用Java数据库连接来直接操作持久化-对于简单应用的直截了当的解决方案。JDBC API的类是紧贴在关系型数据库表、行和列之后的数据模型。你必须手动地在应用的内部对象模型与JDBC对象模型之间进行转换,如果你的应用的内部模型本身就类似于2维的关系表的话,那采用JDBC是最佳手段。
自动的:你可以把ORM交给框架。框架通常向你提供一个可以和任意数据对象进行交互的API。通过那个API,你可以存储、获取和查询数据库。框架在后台完成了框架对象的转换。因为特定的关系型SQL查询不适合对象接口,ORM框架通常定义它自己的查询语言,并且自动为当前关系型数据库生成正确的SQL语句。对于拥有复杂的数据模型的应用来说,基于框架的手段能为你节省很多时间并降低了出错的可能。
对象数据库
一个对象型数据库直接在数据库中存储、获取和查找对象。因为不再需要ORM,所以它对于Java应用非常适合。不幸的是,现今的对象型数据库相对于关系型数据库来说还不成熟,速度也慢。你可以这样说,一个好的ORM框架从根本上来说,就是为关系型数据库提供一个对象型数据库的接口。两者它都要做到最好。
这篇文章,我将重点放在专为企业级Java ORM应用设计的自动框架上。下一节,我将提到几个流行的ORM框架和EJB 3.0中几个关键的革新。
ORM 框架
EJB 实体bean是Java EE中“官方”的ORM解决方案。但是,在EJB1.x和2.x中,实体bean的难以使用是出了名的,原因如下:
●EJB 1.x和2.x实体bean必须遵守一种严格的组件模型。每一个bean类必须实现一个home接口和一个商业接口。它们必须从某种抽象类中继承,而且必须实现其所有方法,即使它们多数为空。这样的一种严格组件模型使得想从EJB 1.x和2.x的实体bean中构建面向对象的数据模型几乎变得不可能了。
●EJB 1.x和2.x容器需要特别冗长的xml配置文件来建立实体bean与关系型数据库中的表映射。那些文件是非常单调乏味和容易出错的。
简而言之,EJB 1.x和2.x实体bean是一个设计拙劣的ORM框架。它既没有满足Java数据对象模型的需求,也没有满足关系表数据模型的需求。出于对EJB 1.x和2.x实体bean的不满,开发者开始寻找其它的ORM方案。实际使用中,开源的Hibernate(JBoss开发)和Oracle公司的TopLink是最成功的两个POJO ORM框架。Hibernate和TopLink都是基于POJO的。它们不依赖于任何预定义的组件模型。作为替代,它们使用POJO数据对象(简单的JavaBean式的),自动地解读出如何映射它们,以及它们之间的关系(关系型数据库)。通常,JavaBean类映射到一张数据库表,并根据数据库表中的外键映射出类之间的关系。你可以在一个简单直接的xml配置文件中指明ORM的配置信息,比如JavaBean类对应的表名和属性对应的列名。你可以通过框架中的工具(如:Hibernate中的Session类)来对那些POJO进行操作(如:存储、获取和查找)。
EJB 3.0是建立在Hibernate和TopLink的思想和成功之上。它为Java EE提供了一个标准的POJO ORM框架。另外,EJB 3.0有两个超越现今所有持久化解决方案的关键革新:
●没有使用XML文件来指明ORM配置信息, EJB 3.0允许开发者直接在POJO代码中注释出映射信息。举例来说,你可以用注释来指明每个JavaBean属性对应的关系型表列。在这篇文章的后面,你将看到更多的例子。注释使得映射更直接,更容易维护了。
●EJB 3.0为实体bean定义了一个新的归档格式。每个档案使用一组独立的,为后端数据库和ORM行为所专用的配置集来定义一个持久化上下文。在这篇文章的后面,我会讨论持久化上下文。
现在,让我们通过几个简单的例子来看一下EJB 3.0是如何完成POJO ORM的。
映射一个简单的对象
在EJB 3.0中,每个实体bean都是一个简单的JavaBean式的类。为了告诉EJB 3.0容器这个类应该为持久化进行映射,你应该用@Entity来注释这个类。
每一个实体bean类映射到一个关系型数据库表。默认地,表名对应类名。你可以用@Table来为类指定另一个表。每一个JavaBean属性映射到表的列上,同样的,默认列名就是属性名。你可以用@Column注释在属性的Setter方法上来改变这种默认关系。下面是一个EJB 3.0的简单例子:
@Entity// @Table (name="AlternativeTableName")public class Person implements Serializable {
protected int id;
protected String name;
protected Date dateOfBirth;
public void setId (int id) {
this.id = id;
}
@Id(generate = GeneratorType.AUTO)
public int getId () {
return id;
}
public void setName (String name) {
this.name = name;
}
// @Column (name="AlternativeColumnName")
public String getName () {
return name;
}
public void setDateOfBirth (Date dateOfBirth) {
this.dateOfBirth = dateOfBirth;
}
public Date getDateOfBirth () {
return dateOfBirth;
}}
当容器把Person类映射到Person SQL数据库表以后,每一个Person实例就是表中的一条数据记录。
映射一个简单的JavaBean类是容易的。但自动ORM框架真正闪光之处在于映射互相关联的对象。下一节中,我们看一下EJB 3.0如何操作对象间的关系。
关系
在一个数据模型里面,一般来说类相互之间都会有某种联系。比如,一个Person(个人)对象可以和一个Resume(确认)对象相关联,反过来也一样(一对一关系);一个Person对象可以和多个CreditCard(信用卡)对象相关,而一个CreditCard对象只能和一个Person对象相关(一对多关系)。多个Person对象可以和一个Address(地址)对象相关,而一个Person对象只能对应一个Address对象(多对一关系)。(译者注:此处原著笔误, Person与Address位置颠倒了;编者注:我看两者是多对多的关系。一家人住在同一个地方,这个地址对于这一家人来说是一对多的关系;房主在别的地方又买了一套房,房主与地址的关系是一对多的关系。)
在一个数据模型中,对象指针用来操作那些关系。举例来说,一个Person对象可以有一个属性(也就是域)指向一个Resume对象。而另一个属性是CreditCard对象的集合。为了告知EJB 3.0容器对象间的关系,你只需简单地在POJO中注释JavaBean属性。
@Entitypublic class Person implements Serializable {
// ... ...
protected Resume resume;
protected CreditCard [] cards;
protected Address addr;
// ... ...
@OneToOne
public Resume getResume () {
return resume;
}
// ... ...
@ManyToOne
// @JoinColumn (name="MyCustomId")
public Address getAddr () {
return addr;
}
// ... ...
@OneToMany
public Collection <CreditCard getCards () {
return cards;
}}
在关系型数据库中,那些关系自动地被EJB 3.0容器使用外键来重建了。举例来说,Person表有一个外键包含了Resume表中相应的主键。运行时,EJB 3.0容器加强了一对一的关系:它保证了Resume键值对于Person表中的每一行是唯一的。为了启用Resume表到Person表的双向查询,你可以Resume表中定义一个Person属性,并把它也加上@OneToOne注释。
Person表中也有一个外键包含了Address表中相应行的主键。这种情况下,相同的Address主键可以出现在多个Person行中,这是多对一关系。对于一对多的关系,映射稍有一点复杂,因为外键列是定义在多对一表中的。于是,在CreditCard类中,你必须用@ManyToOne来定义一个Person属性。
改变外部键字段名
ORM中使用的外部键字段的名字是由容器自动决定的或者由@JoinColumn注释来显式的指定。
上面讨论的关系只是实体bean之间关系的一种类型,实体类之间另外一种重要关系是继承。
继承
面向对象设计方法的一个关键概念是继承。使用