7.2 Hibernate对象关系映射
7.2.1 对象关系映射的基本概念
ORM是Hibernate实现的核心思想。ORM的实现思想就是将对象映射为关系数据库中的表,或者反过来。Hibernate正是采用了这种思想,方便了开发人员以面向对象的思想来实现对数据库的操作。
ORM是数据库表和对象之间的映射关系。在建立数据库的表时,一般首先对数据库进行建模,画出E-R图,然后再根据实体联系模型也就是E-R图来建立关系模型,再建立相应的表。实体间一般存在3种联系:一对一、一对多和多对多。在Hibernate的ORM中,类与表之间的映射,主要是通过映射文件来确定类与表之间的对应关系,同时类与类之间的关系也会影响与表的映射关系。在学习面向对象设计时已知,类与类之间存在5种关系:继承、实现、关联、依赖和聚合/组合,Hibernate中的POJO类之间的关系也是如此。
Hibernate中的ORM映射文件通常以.hbm.xml作为扩展名,使用这个文件不仅易读,而且可以手工修改,也可以通过一些工具来生成映射文档。
6.2.2 基本类映射过程
Hibernate在实现ORM功能时主要用到的文件有:
1)映射类(*.java):映射类一般是POJO类,对应描述关系数据库的表的结构,类中的属性往往被描述成表中的字段,可以实现把表中的记录与该类的对象进行相互映射。
2)映射文件(*.hbm.xml):指定数据库表和映射类之间的关系,包括映射类和数据库表的对应关系、表字段和类属性类型的对应关系,以及表字段和类属性名称的对应关系等。
3)数据库配置文件(*.properties/*.cfg.xml):指定与数据库连接时需要的连接信息,如连接哪种数据库、登录数据库的用户名、登录密码及连接字符串等。当然还可以把地址映射信息放在这里。
6.2.3 关系映射类型
(1)“单向一对一”关联映射(one-to-one)
两个对象之间是一对一的关系,如人-身份证,单向一对一主键关联,靠的是他们的主键相等。在映射文件中,通过one-to-one元素定义:
(图片来自网络,不妥侵删)
<class name="com.test.Person" table="t_table">
<id name="id">
<!--采用foreign生成策略,foreign会取得关联对象的标识-->
<generator class="foreign">
<!--property是关联对象-->
<param name="property">idCard</param>
</generator>
</id>
<property name="name"/>
<!--一对一关联映射,主键关联-->
<!--one to one标签指示hibernate如何加载其关联对象,默认根据主键加载。也就是拿到关键字段值,根据对端的主键来加载关联对象。constrained="true"表示当前主键还是一个外键。参照了对端(IdCard)的主键,也就是会生成外键约束语句-->
<one-to-one name="idCard" constrained="true"/>
</class>
(2)“单向多对一”关联映射(many-to-one)
原理为:在“多”的一端加入一个外键,指向“一”的一端,比如下面左图,可以在多的一端加入以下标签映射:
<many-to-one name="group" column="groupid"/>
(3)“单向一对多”关系映射(one-to-many)
与多对一原理一致,如下面右图。注意他与多对一的区别是维护的关系不同。多对一维护的关系是:多指向一的关系,加载多的时候 可以把一加载上来;一对多则相反。可以在映射文件的“一”端加入以下标签映射:
<set name="students">
<key column="classesid"/>
<one-to-many class="con.Student"/>
</set>
(4)“单向多对多”关联映射(many-to-many)
此时一般要新增一张表才能完成基本映射比如实体“用户”和“角色”之间就是多对多关联,要描述这种关系,要新增一张表(t-user-role)来完成映射:
在进行映射处理时,可以在映射文件的user一端加入以下标签:
<set name="roles" table="t-user-role">
<key column="user-id"/>
<many-to-many class="com.Role" column="role-id"/>
</set>
(5)“双向一对一”
对比单向一对一,人和身份证的双向一对一需要在IdCard中加入<one-to-one>标签,它只影响加载
双向一对一主键映射,可以在映射文件的IdCard端新加入以下标签:
<one-to-one name="person"/>
双向一对一唯一外键映射,可以在映射文件的IdCard端新加入以下标签:
<one-to-one name="person" property-ref="idCard"/>
注意:双向一对一唯一外键映射采用one-to-one标签映射,必须指定其中的property-ref属性为关系字段的名称。
(6)“双向一对多”
采用双向一对多主要是解决单向一对多关联的缺陷,而不是由需求所驱动的。
双向一对多关联的映射关系如下:
在“一”的一端的集合上采用<key>标签,在多的一端加入一个外键
在多的一端采用<many-to-many>标签
注意:这两个标签加入的字段应保持一致,否则会产生数据混乱。映射文件中的关键映射代码,可以在classes的“一”端加入以下标签:
<set name="students" inverse="true">
<key column="classesid"/>
<one-to-many class="con.Student"/>
</set>
在student的“一”端加入:
<many-to-one name="classes" column="classesid"/>
其中,inverse属性可以用在双向一对多和双向多对多关系上,默认值为false,表示本端可以维护关系;如果值为true,会交给另一端维护关系,本端失效。所以双向一对多通常在多的一端维护关系,该属性是控制方向上的反转,只影响存储。
(7)role端关键映射代码为:
<set name="roles" table="t-user-role">
<key column="user-id"/>
<many-to-many class="com.User" column="role-id"/>
</set>
user端关键映射代码为:
<set name="roles" table="t-user-role">
<key column="user-id"/>
<many-to-many class="com.Role" column="role-id"/>
</set>
综上所述,可以看出,同一类映射,无论是单向还是双向,它们的存储结构都是相同的,之所以映射文件不同,是因为加载不同。无论是一对一、一对多还是多对一、多对多,A对B,A就是主动方,A主动想了解B的情况,这样把B设置到A端。而双向也就是双方同样都想了解对方的情况,那就要同时也把A端设置到B端了。