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 @hibernate.id
34 generator - class = " foreign "
35 type = " int "
36 column = " Guid "
37
38
39 </ meta >
40 < generator class = " foreign " >
41 < param name = " property " > userBasic </ param >
42 </ generator >
43 </ id >
44
45 < property
46 name = " username "
47 type = " java.lang.String "
48 column = " Username "
49 length = " 128 "
50 >
51 < meta attribute = " field-description " >
52 @hibernate.property
53 column = " Username "
54 length = " 128 "
55 </ meta >
56 </ property >
57 < property
58 name = " gender "
59 type = " java.lang.String "
60 column = " Gender "
61 length = " 1 "
62 >
63 < meta attribute = " field-description " >
64 @hibernate.property
65 column = " Gender "
66 length = " 1 "
67 </ meta >
68 </ property >
69 < property
70 name = " birthday "
71 type = " java.sql.Date "
72 column = " Birthday "
73 length = " 19 "
74 >
75 < meta attribute = " field-description " >
76 @hibernate.property
77 column = " Birthday "
78 length = " 19 "
79 </ meta >
80 </ property >
81
82 <!-- Associations -->
83
84 <!-- bi - directional one - to - one association to UserBasic -->
85 < one - to - one
86 name = " userBasic "
87 class = " com.xxx.hibernate.UserBasic "
88 constrained = " true "
89 >
90 < meta attribute = " field-description " >
91 @hibernate.one - to - one
92 class = " com.xxx.hibernate.UserBasic "
93 constrained = " true "
94 </ meta >
95 </ one - to - one >
96
97 </ class >
98 </ 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.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 @hibernate.id
34 generator - class = " foreign "
35 type = " int "
36 column = " Guid "
37
38
39 </ meta >
40 < generator class = " foreign " >
41 < param name = " property " > userBasic </ param >
42 </ generator >
43 </ id >
44
45 < property
46 name = " username "
47 type = " java.lang.String "
48 column = " Username "
49 length = " 128 "
50 >
51 < meta attribute = " field-description " >
52 @hibernate.property
53 column = " Username "
54 length = " 128 "
55 </ meta >
56 </ property >
57 < property
58 name = " gender "
59 type = " java.lang.String "
60 column = " Gender "
61 length = " 1 "
62 >
63 < meta attribute = " field-description " >
64 @hibernate.property
65 column = " Gender "
66 length = " 1 "
67 </ meta >
68 </ property >
69 < property
70 name = " birthday "
71 type = " java.sql.Date "
72 column = " Birthday "
73 length = " 19 "
74 >
75 < meta attribute = " field-description " >
76 @hibernate.property
77 column = " Birthday "
78 length = " 19 "
79 </ meta >
80 </ property >
81
82 <!-- Associations -->
83
84 <!-- bi - directional one - to - one association to UserBasic -->
85 < one - to - one
86 name = " userBasic "
87 class = " com.xxx.hibernate.UserBasic "
88 constrained = " true "
89 >
90 < meta attribute = " field-description " >
91 @hibernate.one - to - one
92 class = " com.xxx.hibernate.UserBasic "
93 constrained = " true "
94 </ meta >
95 </ one - to - one >
96
97 </ class >
98 </ hibernate - mapping >
用hbm2java生成对应的对应的Java类:hbm2java *.xml --output=xxx。
Hibernate的配置文件hibernate.cfg.xml内容如下:
1
<?
xml version="1.0" encoding="utf-8"
?>
2 <! DOCTYPE hibernate-configuration
3 PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN"
4 "http://hibernate.sourceforge.net/hibernate-configuration-2.0.dtd" >
5
6 < hibernate-configuration >
7 < session-factory >
8
9 <!-- local connection properties -->
10 < property name ="hibernate.connection.url" > jdbc:mysql://127.0.0.1/xxx?useUnicode=true & characterEncoding=UTF-8 & autoReconnect=true </ property >
11 < property name ="hibernate.connection.driver_class" > com.mysql.jdbc.Driver </ property >
12 < property name ="hibernate.connection.username" > root </ property >
13 < property name ="hibernate.connection.password" > 123456 </ property >
14 <!-- property name="hibernate.connection.pool_size"></property -->
15
16 <!-- dialect for MySQL -->
17 < property name ="dialect" > net.sf.hibernate.dialect.MySQLDialect </ property >
18
19 < property name ="hibernate.show_sql" > true </ property >
20 < property name ="hibernate.use_outer_join" > true </ property >
21 < property name ="hibernate.transaction.factory_class" > net.sf.hibernate.transaction.JDBCTransactionFactory </ property >
22
23 < mapping resource ="com/xxx/hibernate/UserBasic.hbm.xml" />
24 < mapping resource ="com/xxx/hibernate/UserInfo.hbm.xml" />
25
26 </ session-factory >
27 </ hibernate-configuration >
2 <! DOCTYPE hibernate-configuration
3 PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN"
4 "http://hibernate.sourceforge.net/hibernate-configuration-2.0.dtd" >
5
6 < hibernate-configuration >
7 < session-factory >
8
9 <!-- local connection properties -->
10 < property name ="hibernate.connection.url" > jdbc:mysql://127.0.0.1/xxx?useUnicode=true & characterEncoding=UTF-8 & autoReconnect=true </ property >
11 < property name ="hibernate.connection.driver_class" > com.mysql.jdbc.Driver </ property >
12 < property name ="hibernate.connection.username" > root </ property >
13 < property name ="hibernate.connection.password" > 123456 </ property >
14 <!-- property name="hibernate.connection.pool_size"></property -->
15
16 <!-- dialect for MySQL -->
17 < property name ="dialect" > net.sf.hibernate.dialect.MySQLDialect </ property >
18
19 < property name ="hibernate.show_sql" > true </ property >
20 < property name ="hibernate.use_outer_join" > true </ property >
21 < property name ="hibernate.transaction.factory_class" > net.sf.hibernate.transaction.JDBCTransactionFactory </ property >
22
23 < mapping resource ="com/xxx/hibernate/UserBasic.hbm.xml" />
24 < mapping resource ="com/xxx/hibernate/UserInfo.hbm.xml" />
25
26 </ session-factory >
27 </ hibernate-configuration >
JUnit的测试用例程序片断如下:
1
public
void
testInsertUser() throws Exception
{
2 UserBasic user = new UserBasic();
3 user.setAccount("test");
4 user.setPassword("123456");
5 user.setEmail("[email protected]");
6
7 UserInfo info = new UserInfo();
8 info.setUsername("George Hill");
9 info.setGender("M");
10 info.setBirthday(new Date());
11
12 user.setUserInfo(info);
13 info.setUserBasic(user);
14
15 Transaction tx = session.beginTransaction();
16 session.save(user);
17 tx.commit();
18 }
2 UserBasic user = new UserBasic();
3 user.setAccount("test");
4 user.setPassword("123456");
5 user.setEmail("[email protected]");
6
7 UserInfo info = new UserInfo();
8 info.setUsername("George Hill");
9 info.setGender("M");
10 info.setBirthday(new Date());
11
12 user.setUserInfo(info);
13 info.setUserBasic(user);
14
15 Transaction tx = session.beginTransaction();
16 session.save(user);
17 tx.commit();
18 }
运行测试程序,可以看到输出了两条insert语句。