Hibernate基础教程(5)

来源:互联网 发布:php如何解决高并发抢购 编辑:程序博客网 时间:2024/06/05 21:49

 第5章              映射概述

Hibernate的用途是,让数据库看起来像存储了Java对象一样。但是,数据库实际上不存储对象,它们仅是在表和列中存储数据。遗憾的是,没有将数据库中存储的数据与Java对象代表的数据联系起来的简便方法。

5.1        映射为什么无法自动化

为什么无法为在数据库中存储Java对象建立一个简单的规则?原因并不明显。例如,最明显的原因是,一个Java类必须与一个表相关联。对于用户,代码清单5-2中定义的User类肯定可以由一个简单的表来表示。

代码清单5-2     包含Password字段的简单User

public class User{

    String name;

    String password;

}

这个类可以和表很好对应,但仍然有一些问题:

v  如果保存一个用户两次,那么最终应该是多少行?

v  是否允许保存没有提供姓名的用户?

v  是否允许保存没有提供密码的用户?

v  用户密码是否应该有限制?(只使用数字?密码的长度?)

v  姓名是否应该有限制?(是否可以使用中文)

v  ……

如果一个类还引用了其他类,那么还有更多的问题需要考虑。请看代码清单5-3中定义的CustomerEmail

代码清单5-3     Customer类和Email

public class Customer{

      int customerId;

    int customerReference;

    String name;

    Email email;

}

 

public class Email{

    String address;

}

对于这两个类,会出现以下问题:

v  对顾客进行唯一识别是根据customerId还是根据customerRefernce

v  多位顾客可以使用同一个电子邮件?

v  应该在Customer表中表示关系吗?

v  应该在Email表中表示关系吗?

v  应该使用一张中间表来表示关系吗?

根据对这些问题的回答,数据库表可能会有非常大的差异。而我们这里还仅仅举了两个很简单的例子。

默然说话:归纳一下,我们可以很容易将Java类与数据库表建立数据映射,但却没有办法很容易与数据库的约束建立映射,所以,我们需要映射文件。

也许有人会说,我们可以使用这些实体Beansettergetter方法来完成约束限制。不错,这是一种方式,但是从面向对象的角度来看,约束的维护不应该由实体类自身来完成,因为这样做约束代码的维护性和扩展性都将很低。而且,数据库表自身也不负责约束的维护,约束的维护是由数据库来完成的,所以,要提高我们代码的可维护性和可扩展性,必然应该由其他的类来完成对实体Bean的约束维护任务。Hibernate也是这样做的)。

5.2      主键

大多数提供SQL访问的“关系”数据库都允许表没有预定义的主键。但是,Hibernate没这么宽容----如果表已经创建了,但是没有主键,Hibernate就要求指定一个主键。对于熟悉SQL和数据库,却不熟悉ORM工具的朋友来说,这似乎很奇怪。

如果没有主键,就不可能唯一地识别表中的行。那为什么SQL可以不需要唯一地识别条目,而Hibernate却需要?Hibernate要表示Java对象,而Java对象决是可以唯一识别的。正是这个原因,所以许多Java新手,总是使用==去比较两个String是否相同,而不是调用equalsSQL无法区分这样的差异,但在某些情况下的确需要这种区分能力。

因此,Hibernate开发者决定在创建映射时强迫使用主键,从而避免许多问题。

尽管Hibernate不允许忽略主键,但是允许用多个列组合成主键。甚至在绝对必要的情况下回避这个限制(可以通过创建视图或存储过程建立“伪”主键,也可以使用传统的JDBC访问表数据)

5.3      延迟加载

Hibernate允许将某些关系标为lazy,在实际需要它们之前,不会从磁盘加载它们。

Hibernate 3中,默认情况下,类(包括SetMap等集合)应该延迟加载(见第9章)。

可以使用映射文件指定哪些类和字段应该采用延迟加载。

5.4      关联

在讨论映射过程为什么无法自动化时,我们讨论两个类(见代码清单5-3),我们还指出了它们导致的5个问题:

v  对顾客进行唯一识别是根据customerId还是根据customerRefernce

v  多位顾客可以使用同一个电子邮件?

v  应该在Customer表中表示关系吗?

v  应该在Email表中表示关系吗?

v  应该使用一张中间表来表示关系吗?

第一个问题很容易回答----它取决于指定哪个列作为主键。其余4个问题是相关的,它们都取决于对象关系。另外,如果Customer类使用Collection类或数组来表示与Email的关系,那么一个用户可能有多个电子邮件地址。

public class Customer{

       int customerId;

    int customerReference;

    String name;

    Email email;

}

所以,应该增加另两个关键问题:

v  问题1:一个电子邮件地址可以属于多位用户吗?

v  问题2:一个顾客可以有多个电子邮件地址吗?

对这两个问题的回答组合可以表示成表5-4这样的形式。

5-4   决定实体关系的基数

对问题1的回答

对问题2的回答

CustomerEmail之间的关系

一对一

一对多

多对一

多对多

这就是表达对象之间关系的基数的4种方式。然后,可以在映射表中以各种方式表示这些关系。

5.4.1 一对一关联

可以用多种方式表示类之间的一对一关联。按照最简单的形式,两个类的属性放在同一个表中。例如,UserEmail类之间的一对一关联可以用一个表来表示,见表5-5

5-5   组合的User/Email

ID

Username

Email

1

dcminter

dcminter@example.com

2

jlinwood

jlinwood@example.com

3

tjkitchen

tjkitchen@example.com

可以用一个数据库实体表这个UserEmail类组合。见图5-4

username

email

User

id

PK

5-4表示一对一关系的单一实体

另一种方法是在不同的表中维护实体,这些表具有相同的主键(如表5-6和表5-7所示)

5-6   User

ID

Username

1

dcminter

2

jlinwood

3

tjkitchen

5-7   Email

ID

Email

1

dcminter@example.com

2

jlinwood@example.com

3

tjkitchen@example.com

可以在实体之间创建强制的外键关系,但是不应该在两个方向上都应用外键关系,因为这会导致循环依赖。还可以完全省略外键关系,由Hibernate管理键的选择和分配。

username

email

User

id

PK

           

Email

id

PK

如果让表使用相同的主键是不可行的,那么可以在两个表之间建立外键关系,并且对外键列应用“唯一”约束。

这种方式的优点在于,只要删除唯一约束,这个关联就可以轻松地从一对一关联变成多对一。

5.4.2        一对多和多对一关联

一对多关联(从另一个类的方向来看,就是多对一关联)可以用不带其他约束的外键来表示。

这种关系还可以用链接表(中间表)来表示。这个表维护指向每个相关表的外键,这些外键本身构成链接表的主键。还可以在链接表中添加其他列,从而维护关联中实体的次序信息。必须在关系“一”端上应用唯一约束,否则链接表就可以表示UserEmail实体之间的所有关系类型----多对多关联。

5.4.3        多对多关联

如果不用外键构成组合主键,而是让链接表建立自己的主键(往往是个代理键),那么两个实体之间的关联就成了完整的多对多关系。

5.4.4        应用映射来建立关联

需要应用映射来表达底层表中的各种关联方式----不存在表示关联的自动化方法。

5.5      映射的类型

目前,Hibernate支持两种表达映射的标准方法。

使用得最久的技术是XML映射文件。这是最成熟的方法,也是当前控制Hibernate的最佳方法,它对Hibernate特性集提供最全面的控制能力。在第1章和第3章中已经讲过简单的映射文件示例。我们将在第8章讨论XML映射文件的细节。

Hibernate现在还支持Java 5中引入的注解特性。这个特性允许使用一种特殊的语法在应用程序的源代码中直接嵌入元数据。尽管可以以这种方式控制Hibernate的核心特性,但是不能在注解中指定许多附加特性。在源代码中直接维护映射信息很方便,但通过XML映射文件可以使用更多的Hibernate特性,所以在选择映射方法时需要权衡考虑。我们将在第6章中详细讨论基于注解的映射。

5.6      在映射中可以表示的其他信息

尽管Hibernate可以为映射决定许多合理的默认值,但是可以通过基于文件和XML的方式覆盖大多数默认值。本节将讨论Hibernate允许在映射中提供的其他信息。

5.6.1 指定数据库列类型和大小

即使尽可能使用数据库专有类型,仍然会导致问题,因为这些类型和Java基本类型之间并不完全对应。通过覆盖默认的类型映射,开发人员可以在存储空间、性能和原始Java数据的保真度之间做出适当的权衡。

5.6.2        将继承关系映射到数据库

对于表示表中数据的继承关系,还没有SQL标准。一些数据库为此提供了专有的语法,但并非所有数据库都提供这个功能。Hibernate提供了几种可配置的继承关系表示方法,而且映射文件允许用户选择适合自己模型的方法。

5.6.3        主键

Hibernate要求用主键来标识实体。可以通过映射文件选择使用代理键、来自业务数据的键和/或组合主键。

在使用代理键时,Hibernate还允许选择键生成技术。有各种各样的键生成技术,它们在可移植性和效率方面差异很大。

5.6.4        使用基于SQL公式的属性

有时候,实体的属性不应该定义为直接存储在数据库中的数据,而是应该定义为在数据上执行的函数。例如,Java逻辑应该不必直接管理小计字段,而是将这个字段定义为其他属性的统计函数。

5.6.5        非空约束和唯一约束

生成的数据库表会包含对应的NOT NULLUNIQUE约束,从而避免无效数据破坏表。因此,不需要通过应用程序逻辑避免出现无效数据。

注意,主键关系隐含着非空约束和唯一约束。

5.6.6        操作的级联

在代码中管理适当的级联规则是非常麻烦的,在映射中指定级联规则要方便得多。

5.7      小结

本章概述了需要映射文件的原因以及映射文件支持的特性。还讨论了各种关联类型,以及它们适用的场合。

下面两章分别讨论如何使用注解和XML文件指定映射。

原创粉丝点击