Hibernate的一对一关联实例
Hibernate中的表的关联有一对一,一对多和多对多三种关联方式,在这篇笔记和接下来的笔记中,我将用我自己的实际例子来说明如何具体实施。
我使用的Hibernate版本是2.1.8,在Hibernate的网站2.1.6版本的中文文档中有关一对一的关联有下面一段表述:
5.1.11. 一对一
持久化对象之间一对一的关联关系是通过one-to-one元素定义的。
< one-to-one
name ="propertyName" (1)
class ="ClassName" (2)
cascade ="all|none|save-update|delete" (3)
constrained ="true|false" (4)
outer-join ="true|false|auto" (5)
property-ref ="propertyNameFromAssociatedClass" (6)
access ="field|property|ClassName" (7)
/>
(1) name: 属性的名字。
(2) class (可选 - 默认是通过反射得到的属性类型):被关联的类的名字。
(3) cascade(级联) (可选) 表明操作是否从父对象级联到被关联的对象。
(4) constrained(约束) (可选) 表明该类对应的表对应的数据库表,和被关联的对象所对应的数据库表之间,通过一个外键引用对主键进行约束。这个选项影响save()和delete()在级联执行时的先后顺序(也在schema export tool中被使用)。
(5) outer-join(外连接) (可选 - 默认为 自动): 当设置hibernate.use_outer_join的时候,对这个关联允许外连接抓取。
(6) property-ref: (可选) 指定关联类的一个属性,这个属性将会和本外键相对应。如果没有指定,会使用对方关联类的主键。
(7) access (可选 - 默认是 property): Hibernate用来访问属性的策略。
有两种不同的一对一关联:
主键关联
惟一外键关联
主键关联不需要额外的表字段;两行是通过这种一对一关系相关联的,那么这两行就共享同样的主关键字值。所以如果你希望两个对象通过主键一对一关联,你必须确认它们被赋予同样的标识值!
比如说,对下面的Employee和Person进行主键一对一关联:
< one-to-one name ="person" class ="Person" />
< one-to-one name ="employee" class ="Employee" constrained ="true" />
现在我们必须确保PERSON和EMPLOYEE中相关的字段是相等的。我们使用一个特别的称为foreign的Hibernate标识符生成器策略:
< class name ="person" table ="PERSON" >
< id name ="id" column ="PERSON_ID" >
< generator class ="foreign" >
< param name ="property" > employee </ param >
</ generator >
</ id >
< one-to-one name ="employee"
class ="Employee"
constrained ="true" />
</ class >
一个刚刚保存的Person实例被赋予和该Person的employee属性所指向的Employee实例同样的关键字值。
另一种方式是一个外键和一个惟一关键字对应,上面的Employee和Person的例子,如果使这种关联方式,应该表达成:
< many-to-one name ="person" class ="Person" column ="PERSON_ID" unique ="true" />
如果在Person的映射加入下面几句,这种关联就是双向的:
< one-to-one name"employee" class ="Employee" property-ref ="person" />
持久化对象之间一对一的关联关系是通过one-to-one元素定义的。
< one-to-one
name ="propertyName" (1)
class ="ClassName" (2)
cascade ="all|none|save-update|delete" (3)
constrained ="true|false" (4)
outer-join ="true|false|auto" (5)
property-ref ="propertyNameFromAssociatedClass" (6)
access ="field|property|ClassName" (7)
/>
(1) name: 属性的名字。
(2) class (可选 - 默认是通过反射得到的属性类型):被关联的类的名字。
(3) cascade(级联) (可选) 表明操作是否从父对象级联到被关联的对象。
(4) constrained(约束) (可选) 表明该类对应的表对应的数据库表,和被关联的对象所对应的数据库表之间,通过一个外键引用对主键进行约束。这个选项影响save()和delete()在级联执行时的先后顺序(也在schema export tool中被使用)。
(5) outer-join(外连接) (可选 - 默认为 自动): 当设置hibernate.use_outer_join的时候,对这个关联允许外连接抓取。
(6) property-ref: (可选) 指定关联类的一个属性,这个属性将会和本外键相对应。如果没有指定,会使用对方关联类的主键。
(7) access (可选 - 默认是 property): Hibernate用来访问属性的策略。
有两种不同的一对一关联:
主键关联
惟一外键关联
主键关联不需要额外的表字段;两行是通过这种一对一关系相关联的,那么这两行就共享同样的主关键字值。所以如果你希望两个对象通过主键一对一关联,你必须确认它们被赋予同样的标识值!
比如说,对下面的Employee和Person进行主键一对一关联:
< one-to-one name ="person" class ="Person" />
< one-to-one name ="employee" class ="Employee" constrained ="true" />
现在我们必须确保PERSON和EMPLOYEE中相关的字段是相等的。我们使用一个特别的称为foreign的Hibernate标识符生成器策略:
< class name ="person" table ="PERSON" >
< id name ="id" column ="PERSON_ID" >
< generator class ="foreign" >
< param name ="property" > employee </ param >
</ generator >
</ id >
< one-to-one name ="employee"
class ="Employee"
constrained ="true" />
</ class >
一个刚刚保存的Person实例被赋予和该Person的employee属性所指向的Employee实例同样的关键字值。
另一种方式是一个外键和一个惟一关键字对应,上面的Employee和Person的例子,如果使这种关联方式,应该表达成:
< many-to-one name ="person" class ="Person" column ="PERSON_ID" unique ="true" />
如果在Person的映射加入下面几句,这种关联就是双向的:
< one-to-one name"employee" class ="Employee" property-ref ="person" />
下面是我的一个一对一主键关联的例子,使用的数据库是MySQL 4.1.11:
我有两个表:UserBasic和UserInfo,UserBasic记录的是用户的基本注册信息,UserInfo表记录的是用户的详细信息。表的结构如下:
1
CREATE
TABLE
IF
NOT
EXISTS
UserBasic
2 (
3 Guid INT NOT NULL AUTO_INCREMENT,
4 Account VARCHAR ( 64 ) NOT NULL ,
5 Password VARCHAR ( 16 ) NOT NULL ,
6 Email VARCHAR ( 128 ) NOT NULL ,
7 PRIMARY KEY (Guid)
8 ) TYPE = InnoDB;
9
10 CREATE TABLE IF NOT EXISTS UserInfo
11 (
12 Guid INT NOT NULL ,
13 Username VARCHAR ( 128 ),
14 Gender CHAR ( 1 ),
15 Birthday DATETIME ,
16 PRIMARY KEY (Guid)
17 ) TYPE = InnoDB;
18
19 ALTER TABLE UserInfo ADD CONSTRAINT UserInfoRFUserBasic FOREIGN KEY (Guid)
20 REFERENCES UserBasic (Guid) ON DELETE CASCADE ON UPDATE RESTRICT ;
2 (
3 Guid INT NOT NULL AUTO_INCREMENT,
4 Account VARCHAR ( 64 ) NOT NULL ,
5 Password VARCHAR ( 16 ) NOT NULL ,
6 Email VARCHAR ( 128 ) NOT NULL ,
7 PRIMARY KEY (Guid)
8 ) TYPE = InnoDB;
9
10 CREATE TABLE IF NOT EXISTS UserInfo
11 (
12 Guid INT NOT NULL ,
13 Username VARCHAR ( 128 ),
14 Gender CHAR ( 1 ),
15 Birthday DATETIME ,
16 PRIMARY KEY (Guid)
17 ) TYPE = InnoDB;
18
19 ALTER TABLE UserInfo ADD CONSTRAINT UserInfoRFUserBasic FOREIGN KEY (Guid)
20 REFERENCES UserBasic (Guid) ON DELETE CASCADE ON UPDATE RESTRICT ;
UserInfo的主键值和UserBasic的主键值是一样的,两个表是单向的一对一关系。UserBasic为主控方,UserInfo是被动方。
用Middlegen生成的UserBasic.hbm.xml文件,修改后的内容如下:
1
<?
xml version="1.0"
?>
2 <! DOCTYPE hibernate-mapping PUBLIC
3 "-//Hibernate/Hibernate Mapping DTD 2.0//EN"
4 "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >
5
6 < hibernate-mapping >
7 <!--
8 Created by the Middlegen Hibernate plugin 2.1
9
10 http://boss.bekk.no/boss/middlegen/
11 http://www.hibernate.org/
12 -->
13
14 < class
15 name ="com.xxx.hibernate.UserBasic"
16 table ="UserBasic"
17 dynamic-update ="true"
18 dynamic-insert ="true"
19 >
20 < meta attribute ="class-description" inherit ="false" >
21 @hibernate.class
22 table="UserBasic"
23 dynamic-update="true"
24 dynamic-insert="true"
25 </ meta >
26
27 < id
28 name ="guid"
29 type ="int"
30 column ="Guid"
31 >
32 < meta attribute ="field-description" >
33 @hibernate.id
34 generator-class="native"
35 type="int"
36 column="Guid"
37
38
39 </ meta >
40 < generator class ="native" />
41 </ id >
42
43 < property
44 name ="account"
45 type ="java.lang.String"
46 column ="Account"
47 not-null ="true"
48 length ="64"
49 >
50 < meta attribute ="field-description" >
51 @hibernate.property
52 column="Account"
53 length="64"
54 not-null="true"
55 </ meta >
56 </ property >
57 < property
58 name ="password"
59 type ="java.lang.String"
60 column ="Password"
61 not-null ="true"
62 length ="16"
63 >
64 < meta attribute ="field-description" >
65 @hibernate.property
66 column="Password"
67 length="16"
68 not-null="true"
69 </ meta >
70 </ property >
71 < property
72 name ="email"
73 type ="java.lang.String"
74 column ="Email"
75 not-null ="true"
76 length ="128"
77 >
78 < meta attribute ="field-description" >
79 @hibernate.property
80 column="Email"
81 length="128"
82 not-null="true"
83 </ meta >
84 </ property >
85
86 <!-- Associations -->
87
88 <!-- bi-directional one-to-one association to UserInfo -->
89 < one-to-one
90 name ="userInfo"
91 class ="com.xxx.hibernate.UserInfo"
92 cascade ="save-update"
93 >
94 < meta attribute ="field-description" >
95 @hibernate.one-to-one
96 class="com.xxx.hibernate.UserInfo"
97 cascade="save-update"
98 </ meta >
99 </ one-to-one >
100
101 </ class >
102 </ hibernate-mapping >
2 <! DOCTYPE hibernate-mapping PUBLIC
3 "-//Hibernate/Hibernate Mapping DTD 2.0//EN"
4 "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >
5
6 < hibernate-mapping >
7 <!--
8 Created by the Middlegen Hibernate plugin 2.1
9
10 http://boss.bekk.no/boss/middlegen/
11 http://www.hibernate.org/
12 -->
13
14 < class
15 name ="com.xxx.hibernate.UserBasic"
16 table ="UserBasic"
17 dynamic-update ="true"
18 dynamic-insert ="true"
19 >
20 < meta attribute ="class-description" inherit ="false" >
21 @hibernate.class
22 table="UserBasic"
23 dynamic-update="true"
24 dynamic-insert="true"
25 </ meta >
26
27 < id
28 name ="guid"
29 type ="int"
30 column ="Guid"
31 >
32 < meta attribute ="field-description" >
33 @hibernate.id
34 generator-class="native"
35 type="int"
36 column="Guid"
37
38
39 </ meta >
40 < generator class ="native" />
41 </ id >
42
43 < property
44 name ="account"
45 type ="java.lang.String"
46 column ="Account"
47 not-null ="true"
48 length ="64"
49 >
50 < meta attribute ="field-description" >
51 @hibernate.property
52 column="Account"
53 length="64"
54 not-null="true"
55 </ meta >
56 </ property >
57 < property
58 name ="password"
59 type ="java.lang.String"
60 column ="Password"
61 not-null ="true"
62 length ="16"
63 >
64 < meta attribute ="field-description" >
65 @hibernate.property
66 column="Password"
67 length="16"
68 not-null="true"
69 </ meta >
70 </ property >
71 < property
72 name ="email"
73 type ="java.lang.String"
74 column ="Email"
75 not-null ="true"
76 length ="128"
77 >
78 < meta attribute ="field-description" >
79 @hibernate.property
80 column="Email"
81 length="128"
82 not-null="true"
83 </ meta >
84 </ property >
85
86 <!-- Associations -->
87
88 <!-- bi-directional one-to-one association to UserInfo -->
89 < one-to-one
90 name ="userInfo"
91 class ="com.xxx.hibernate.UserInfo"
92 cascade ="save-update"
93 >
94 < meta attribute ="field-description" >
95 @hibernate.one-to-one
96 class="com.xxx.hibernate.UserInfo"
97 cascade="save-update"
98 </ meta >
99 </ one-to-one >
100
101 </ class >
102 </ hibernate-mapping >
由于在建立外键的时候就声明了ON DELETE CASCADE,所以在xml的配置文件中第97行声明为save-update。如果声明为all,那么在删除UserBasic表的数据时,会无谓的多出一条删除UserInfo的delete语句出来。
UserInfo.hbm.xml文件的内容如下:
1
<?
xml version
=
"
1.0
"
?>
2 <! DOCTYPE hibernate - mapping PUBLIC
3 " -//Hibernate/Hibernate Mapping DTD 2.0//EN "
4 " http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd " >
5
6 < hibernate - mapping >
7 <!--
8 Created by the Middlegen Hibernate plugin 2.1
9
10 http: // boss.bekk.no/boss/middlegen/
11 http: // www.hibernate.org/
12 -->
13
14 < class
15 name = " com.xxx.hibernate.UserInfo "
16 table = " UserInfo "
17 dynamic - update = " true "
18 dynamic - insert = " true "
19 >
20 < meta attribute = " class-description " inherit = " false " >
21 @hibernate. class
22 table = " UserInfo "
23 dynamic - update = " true "
24 dynamic - insert = " true "
25 </ meta >
26
27 < id
28 name = " guid "
29 type = " int "
30 column = " Guid "
31 >
32 < meta attribute = " field-description " >
33
2 <! DOCTYPE hibernate - mapping PUBLIC
3 " -//Hibernate/Hibernate Mapping DTD 2.0//EN "
4 " http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd " >
5
6 < hibernate - mapping >
7 <!--
8 Created by the Middlegen Hibernate plugin 2.1
9
10 http: // boss.bekk.no/boss/middlegen/
11 http: // www.hibernate.org/
12 -->
13
14 < class
15 name = " com.xxx.hibernate.UserInfo "
16 table = " UserInfo "
17 dynamic - update = " true "
18 dynamic - insert = " true "
19 >
20 < meta attribute = " class-description " inherit = " false " >
21 @hibernate. class
22 table = " UserInfo "
23 dynamic - update = " true "
24 dynamic - insert = " true "
25 </ meta >
26
27 < id
28 name = " guid "
29 type = " int "
30 column = " Guid "
31 >
32 < meta attribute = " field-description " >
33