Hibernate关系映射体系

来源:互联网 发布:js history pushstate 编辑:程序博客网 时间:2024/06/06 05:55
Hibernate中的关联关系大致分为如下两类:
单向关系:只需单向访问关联端。
1-1
需要在持久类里为关联实体的引用属性增加setter和getter方法。与单向N-1相比,只
需要在<many-to-one.../>元素增加unique="true"属性,用以表示N的一端必须唯一
即可。既然N的一端增加了唯一约束,那么就成为1-1了。同样分以下几种情况:
1、基于外键
将无连接表的N-1关联映射中<many-to-one.../>元素增加unique="true"
属性,用以表示N的一端必须唯一即可。
实例:
<many-to-one name="address" cascade="all" unique="true"
class="Address" column="address_id"/>
2、有连接表
这种情况很少见,同样只需要将有连接表的N-1中的<many-to-one.../>元素增加
unique="true"属性即可。
3、基于主键
基于主键关联的持久化类不能拥有自己的主键生成器策略,它的主键由关联实体来负责生成。
该种情况下:使用<one-to-one.../>元素来映射关联实体,配置该元素时需指定一个name
属性,其值为关联实体属性的属性名。此外,该元素还可接受如下几个可选属性:
class:指定关联实体的全限定类名,默认是通过反射得到该属性所属类的类名。
cascade:指定哪些操作会从主表记录级联到子表记录。
constrained:表明该类对应 的表,和被关联的对象所对应的数据库表之间,通过一个
外键引用对主键进行约束。这个选项影响save()和delete()在级联执行时的先
后顺序,以及决定该关联能否被委托(也在schema export tool中被使用).
fetch:该属性设置抓取策略,该属性接受join(外连接抓取)和select(选择抓取)
两个值的其中之一。
property-ref:指定关联类的一个属性,这个属性将会和本类的主键相对应。如果没有指定,
默认使用对方关联类的主键。
access:指定Hibernate访问该属性的访问策略,默认是property。
lazy:指定引用关联实体的延迟加载特性,该属性只能接受false/proxy/no-proxy
三个值。默认值是proxy。实际上,Hibernate默认会启动单实例关联(即多对一、
一对一)的代理,指定no-proxy即要求该属性应该在实例变量第一次被访问时采用延迟
抓取,lazy="false"指定此关联实体总是被预先抓取.
注意:
当采用这种关联映射策略时,person表将作为从表,此时person表的主键将没有自己
的主键生成策略,因为person表了记录的主键将会与主表(address)里记录的主键保持一致。
1-N(推荐使用双向的1-N)
单向的1-N关联的持久类发生了改变,持久化类里需要使用集合属性,应为1的一端需要访问N的一端,而
N的一端将以集合(Set)形式表现。从这个意义上来说,1-N(实际上还包括N-N)和集合映射非常相似,只是
将用于映射集合元素的<element.../>元素改为使用<one-to-many.../>元素。该元素有以下几个
可选属性:
class:指定了关联实体的类型,默认通过反射来获取关联实体的类型,如果集合属性没有使用泛型,则
必须指定该属性。
not-found:该属性值只能是exception或ignore,指定当从表记录参照的主表记录不存在时,
Hibernate如何处理这种情况。默认是exception,即抛出一个异常;如果程序希望
Hibernate忽略这个异常,可以指定not-found="ignore".
对于单向1-N关联关系,只需在1的一端增加Set类型的属性,该属性记录当前实体的关联实体,当然还要
为这个Set类型的属性增加setter和getter方法即可。同样分为如下两种情况:
无连接表:
需要在1的一端增加对应的集合映射元素,如<set.../>、<bag.../>(通常不要使用<list.../>
元素)等。与映射集合属性类似,必须为相应集合元素增加<key.../>子元素,用以映射外键列。区别:集合中的
的元素使用<one-to-many.../>来映射关联实体,而不是使用<element.../>子元素。
实例:
<!--该处没有指定级联的其他可选属性-->
<set name="">
<!--指定关联的外键列-->
<key column=""/>
<!--用以映射到关联类属性-->
<one-to-many class=""/>
</set>
注意:上例映射<set.../>元素时没有指定cascade属性,在默认情况下,
对主表实体的持久化操作不会级联到从表实体
有连接表:
该种情况下,映射文件不再使用<one-to-many.../>元素映射关联实体,而是使用<many-to-many.../>
元素,但为了保证当前实体是1的一端,因此要为元素指定unique="true".<one-to-many.../>元素下
可选属性:
class:指定关联实体的类名,默认由Hibernate通过反射来获取该类名。
not-found:指定当外键参照的主表记录不存在时如何处理。该属性只接受ignore和exception两个值,
默认是exception,即不存在时抛出异常;如果程序希望Hibernate忽略这个异常,则可指定该属性
值为ignore。
formula:指定一个SQL表达式,该外键值将根据该SQL表达式来计算。
outer-join:指定Hibernate是否启动外连接来抓取关联实体,该属性只能是true、false和auto之一,
其中true表示启用,false表示不启用,auto表示由程序决定。
fetch:指定Hibernate的抓取策略,该属性值只能是join(使用外连接抓取)和select(使用选择抓取)两
个值之一。
lazy:指定Hibernate是否需要启动延迟加载来加载关联实体。
unique:指定本持久化实体是否增加唯一约束,默认是false。
where:指定一个SQL表达式,指定当查询、获取关联实体时的过滤条件,只有满足该where条件的关联实体才会被
加载。
order-by:该属性用于设置数据库对集合元素排序,该属性仅对1.4或更高版本的JDK有效。该属性的值为指定表的
指定字段(一个或几个)加上asc或者desc关键字,这种排序是数据库执行SQL查询时进行的排序,而不是直接
在内存中排序。

property-ref:指定关联类的一个属性,这个属性将会和本类的外键相对应(当外键参照唯一键时需指定该属性)。
如果没有指定,直接使用对方关联类的主键。
实例:
<set class="" table="">
<key column/>
<many-to-many class="" column="" unique="true"/>
</set>

N-1
分一下两种情况:
1、无连接表:
程序应该在N的一端的持久化类中增加一个属性,该属性引用1的一端的关联实体,使用
<many-to-one.../>元素映射N-1的关联实体,直接采用该元素来映射关联实体将会
在N的一端的数据表中增加一个外键列,用于参照主表记录。
2、有连接表:
对于绝大部分单向N-1关联而言,使用基于外键的关联映射已经足够了,但由于底层数
据库建模时也可以使用连接表来建立这种关系,因此Hibernate也为这种关联关系提供了支
持。
如果需要使用连接表来映射单向N-1关联,则需要显式使用<join.../>元素,该元
素用于强制将一个类的属性映射到多张表中,通常也用于强制使用连接表。同时,还需要外键关联,
应在配置文件中增加<key.../>子元素来映射外键,并为join元素增加<many-to-one.../>
子元素,用于映射N-1的关联实体。使用<join.../>元素时通常需要指定一个table属性,用于
指定连接表的表名。以下为几个可选属性:
schema:指定此连接表所在的Schema,用于覆盖根元素的default-schema属性。
catalog:同上功能。
inverse:默认值为false,设为true时,Hibernate不会插入或者更新此连接
定义的属性。
optional:默认值为false。true时,Hibernate只会在此连接定义的属性非空
时插入一行数据,并且总是使用外连接来得到该属性。
实例:
<join table="person_address">
<!--映射连接表中参照本表主键的外键列 -->
<key column="person_id"/>
<!--映射连接表中参照本表主键的外键列-->
<many-to-one name="address" cascade="all"
class="Address" column="address_id"/>
</join>
N-N
单向的N-N关联和1-N关联的持久化类代码完全相同,控制关系的一端需要增加一个Set类型的
属性,被关联的持久化实例以集合形式存在。N-N关联必须使用连接表,N-N关联与有连接表的1-N关
联非常相似,只要去掉<many-to-many.../>元素的unique="true"属性即可。
实例:
<!--映射集合属性,集合元素是其他持久化实体指定连接表的表名-->
<set name="" table="">
<!--指定连接表中参照本表记录的外键名称-->
<key column=""/>
<!--使用many-to-many来映射N-N关联-->
<many-to-many class="" column=""/>
</set>
双向关系:关联的两端可以互相访问。
1-1
让两个持久类都增加引用关联实体的属性,并为该属性提供相应的setter和getter方法。
与单向的1-1关联类似的是,双向1-1关联也有三种映射策略:基于主键、基于外键、使用连接表。
基于外键:
此时,外键可以存放在任意一端,需要存放外键的一端,需要增加<many-to-one.../>
元素,为其元素增加unique="true"属性来表示该实体实际上是1的一端。
对于1-1的关联关系,两个实体原本处于平等状态;但当我们选择任意一个表来增加外键后
(增加<many-to-one.../>元素的实体端),该表即变成从表,而另一个表则成为主表。另一
端需要使用<one-to-one.../>元素,该元素使用name属性指定引用关联类的属性。
实例:
1端:
<!--one-to-one元素映射关联属性,外键列在对方的表内
property-ref指定引用关联类的属性。即:在address
属性所属的Address类内,必须有person属性的setter
和getter方法-->
<one-to-one name="address" property-ref="person"/>
N端(1):
<!--使用many-to-one映射1-1关联实体,unique="true"
确定为1-1-->
<many-to-one name="person" unique="true"
column="person_id" not-null="true"/>
基于主键:
使用这种方式时:一端的主键生成器需要使用foreign策略,表明将根据对方的主键来生成自己
的主键,本实体不能拥有自己的主键生成策略。当然,任意一端都可以采用foreign主键生成器策略,
表明将根据对方主键来生成自己的主键。
实例:
指定主键生成策略的一端:
<!--指定foreign主键生成器策略-->
<generator>
<!--指定该主键值将根据person属性引用的
关联实体的主键来生成-->
<param name="property">person</param>
</generator>
...
<one-to-one name="person"/>
另一端:
<one-to-one name="address"/>
实例中指定Address持久化类使用foreign的主键生成策略,这意味着该Address
实体不能主动指定主键值,只能根据关联实体的主键来生成Address实体的主键值。
基于连接表:
该种情况想当罕见,映射想当复杂,数据模型繁琐。通常不推荐使用。
双向1-1关联两端都需要使用<join.../>元素显式指定连接表,<join.../>元素的table
属性用于指定连接表的表名,因此两端的<join.../>元素的table属性值相同。两端都增加key元素
映射连接表中的外键列,还需增加<many-to-one.../>元素映射关联属性,两个<many-to-one.../>
元素都应该增加unique="true"属性表明为1-1关联。此外,为了让Hibernate在连接表的两个数据列上
增加唯一约束,映射文件应该为两个<key.../>子元素指定unique="true".
当使用连接表来建立1-1关联关系时,两个实体的地位绝对平等,不存在任何主从约束关系,Hibernate
映射它们的连接表时,将会选择某一外键列作为连接表的主键——因此两个持久化类的映射文件依然并不是完全相同
的。映射文件必须在一端的<join.../>元素中指定inverse="true",而另一端则指定
optional="true"。
实例:
一端:(Person.hbm.xml)
<!--使用join元素强制使用连接表-->
<join table="person_address" inverse="true">
<!--映射连接表中参照本实体主键的外键列-->
<key column="person_id" unique="true">
<!--映射1-1关联实体-->
<many-to-one name="address" class="Address"
column="address_id" unique="true"/>
</join>
另一端:(Address.hbm.xml)
<!--使用join元素强制使用连接表-->
<join table="person_address" optional="true">
<!--映射连接表中参照本实体主键的外键列-->
<key column="address_id" unique="true">
<!--映射1-1关联实体-->
<many-to-one name="person" class="Person"
column="person_id" unique="true"/>
</join>

1-N
对于1-N关联,Hibernate推荐使用双向关联,而且不要让1的一端控制关联关系,而使用N
的一端控制关联关系。双向的1-N关联与N-1关联是完全相同的两种情形,两端都需要增加对关联属性
的访问,N的一端增加引用到关联实体的属性,1的一端增加集合属性,集合元素为关联实体。同样分
一下两种情况:
无连接表:
大多数情况下,我们使用这种情况即可。无连接表的双向1-N关联,需要同时修改两个持久化类的
配置文件。N的一端需要增加<many-to-one.../>元素来映射关联属性,而1的一端则需要使用
<set.../>或<bag.../>元素来映射关联属性。<set.../>或<bag.../>元素里需要增加
<key.../>子元素映射外键列,并使用<one-to-many.../>子元素映射关联关系。
注意:
对于双向的1-N关联映射,通常不应该允许1的一端控制关联关系,而应该由N的一端来控制
关联关系,此时我们可以在<set.../>元素中指定inverse="true",用于指定1的一端不
控制关联关系。
在双向的1-N关联中,两个持久化类的配置文件都需要指定外键列的列名,此时不可以省略。
因为不是用连接表的1-N关联必须基于外键关联,而外键只保存在N的一端的表中。如果两边指定的
外键列名不同,将导致关联映射出错。如果不指定外键列的列名,该列名由系统自动生成,而系统
很难保证自动生成的两个列名相同。
实例:
1端:
<!---->
<set name="adresses" inverse="true">
<!--指定关联的外键列-->
<key column="person_id"/>
<!--用以映射到关联类的属性-->
<one-to-many class="Adress"/>
<set>
N端:
如果为了保证每条address_inf记录(从表记录)都有关联的person_inf(
主表记录),则可为该元素指定not-null="true".
<!--必须指定列名为person_id,与关联实体中key元素的
column属性值一致-->
<many-to-one name="person" class="Person"
column="person_id" not-null="true"/>
有连接表:
有连接表的1-N双向关联类似于N-N关联,1的一端使用集合元素映射,然后在集合元素里增加
<many-to-many.../>子元素,该子元素映射到关联类。为保证该实体是1的一端,应该为
<many-to-many.../>元素增加unique="true"属性。N的一端则使用<join.../>元素
来强制使用连接表。(两边指定连接表的table属性不能省略,且属性值必须相同,否则关联映射出错。)
此外,N的一端使用<join.../>元素强制使用连接表,因此将使用<key.../>子元素来映射
连接表中外键列,且使用<many-to-one.../>来映射连接表中关联实体的外键列;反过来,1的一端
也将在<set.../>元素里使用<key.../>和<many-to-many.../>两个子元素,它们也映射连
接表中的两列。为了保证得到正常的映射关系,应保证<join.../>里<many-to-one.../>子
元素的column属性和<set.../>里<key.../>元素的column属性相同。
实例:
1端:
<set name="" inverse="true" table="tab">
<key column="c1"/>
<many-to-many class="" column="c2" unique="true"/>
</set>
N端:
<join table="tab">
<key column="c2"/>
<many-to-one name="pro" column="c1" not-null="true"/>
</join>
N-N
双向N-N关联需要两端都使用Set集合属性,两端都增加对集合属性的访问。双向N-N关联没有太多的选择,
只能使用连接表来建立两个实体之间的关联关系。
实例:
类中:(两端一样)
private Set<?> ?s = new HashSet<?>();
文件中:
一端:
<set name="addresses" table="person_address">
<key column="person_id"/>
<many-to-many class="Address" column="address_id"/>
</set>
另一端:
<set name="persons" table="person_address">
<key column="address_id"/>
<many-to-many class="Address" column="person_id"/>
</set>
注意:双向关系里没有N-1,因为双向关系中1-N和N-1是完全相同的。


<many-to-one.../>与<property.../>对比:
以上两个元素的作用类似,也用于映射持久化类的某个属性,区别在于元素<many-to-one.../>
映射的是关联实体。另外,它还拥有如下可选属性:
column:指定进行关联的外键列的列名,该列名默认与该属性同名。
class:指定关联实体的全限定类名,默认是通过反射得到该属性所属类的类名
cascade:指定哪些持久化操作会从主表记录级联到子表记录。
a、all:系统先自动级联插入主表记录,也就是先持久化Address对象,在持久化Person
对象;
fetch:指定Hibernate的抓取策略,该属性值只能是join(使用外连接抓取)和select
(使用选择抓取)两个值的其中之一。
update,insert:指定对应的字段是否包含在用于Hibernate生成的update或insert
语句中,两个属性的默认值都是true。如果将二者指定为false,则表明这是一个纯粹的
外源关联,它的值是通过映射同一个(或多个)字段的其他属性得到的,或由触发器或其他程序
来负责生成。
property-ref:指定关联类的一个属性,这个属性将会和本类的外键相对应(当外键参照唯一键
时需指定该属性)。如果没有指定,直接使用对方关联类的主键。
access:指定Hibernate访问此关联属性的访问策略,默认是property。
unique:指定Hibernate通过DDL为外键列添加唯一约束。此外,也可以用做property-ref
的目标属性。这使关联同时具有一对一的效果。
not-null:指定使用DDL为外键字段添加非空约束。
optimistic-lock:该属性指定其更新时是否需要获得乐观锁定,也就是其值决定引用关联实体
的属性发生改变时版本值是否增长。
lazy:指定引用关联实体的延迟加载特性,该属性只能接受false/proxy/no-proxy三个值。
该属性默认值是proxy。实际上,Hibernate默认会启动单实例关联(即多对一、一对一)
的代理,指定no-proxy即要求该属性应该在实例变量第一次被访问时采用延迟抓取,lazy
="false"指定此关联属性总是被预先抓取。
not-found:指定当外键参照的主表记录不存在时如何处理。该属性只接受ignore和exception
两个值,默认是exception,即不存在时抛出异常;如果程序希望Hibernate忽略这个异常,
则可指定该属性值为ignore。
formula:指定一个SQL表达式,该外键值将根据该SQL表达式来计算。
原创粉丝点击