hibernate 一对一(One-to-One)
一對(duì)一(one-to-one)實(shí)例(Person-IdCard)
一對(duì)一的關(guān)系在數(shù)據(jù)庫(kù)中表示為主外關(guān)系.例如.人和身份證的關(guān)系.每個(gè)人都對(duì)應(yīng)一個(gè)身份證號(hào).我們應(yīng)該兩個(gè)表.一個(gè)是關(guān)于人信息的表(Person).別外一個(gè)是身份證相關(guān)信息的表(id_card).id_card表的主鍵對(duì)應(yīng)該P(yáng)erson表的主鍵id,也是Person表的外鍵.有人才能有身份證.所以此例中Person是主表,id_card表為從表。
hibernate的一對(duì)一關(guān)系有兩種形式,一種是共享主鍵方式,另一種是唯一外鍵方式.
一、共享主鍵方式實(shí)現(xiàn)一對(duì)一
1. 實(shí)體類設(shè)計(jì)如下:
Person類:
Java代碼 ??IdCard類:
Java代碼 ??2.映射文件:
Person.hbm.xml文件如下:
Xml代碼 ??IdCard.hbm.xml文件如下:
Xml代碼 ?3. 在hibernate.cfg.xml文件中注冊(cè)映射文件:
Xml代碼 ??4.測(cè)試類如下:
Java代碼 ??控制臺(tái)打印信息如下所示:
Hibernate: insert into Person (name) values (?)
Hibernate: insert into id_card (authorize_date, id) values (?, ?)
person name : person1
?
數(shù)據(jù)庫(kù)表id_card的創(chuàng)建語(yǔ)句如下所示:(重點(diǎn)注意紅色字體部分)
DROP TABLE IF EXISTS `test`.`id_card`;
CREATE TABLE? `test`.`id_card` (
? `id` int(11) NOT NULL,
? `authorize_date` datetime DEFAULT NULL,
? PRIMARY KEY (`id`),
? KEY `FK627C1FB4284AAF67` (`id`),
? CONSTRAINT `FK627C1FB4284AAF67` FOREIGN KEY (`id`) REFERENCES `person` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
數(shù)據(jù)庫(kù)中記錄如下所示:
mysql> select * from person;
+----+---------+
| id | name??? |
+----+---------+
|? 1 | person1 |
+----+---------+
1 row in set (0.00 sec)
mysql> select * from id_card;
+----+---------------------+
| id | authorize_date????? |
+----+---------------------+
|? 1 | 2010-03-23 01:07:25 |
+----+---------------------+
1 row in set (0.00 sec)
?
在測(cè)試時(shí)一定要注意寫(xiě)上這句代碼:
Java代碼 ?這是讓從對(duì)象關(guān)聯(lián)上它所從屬的主對(duì)象。如果沒(méi)有這句話,則會(huì)拋出如下異常:
org.hibernate.id.IdentifierGenerationException: attempted to assign id from null one-to-one property: person
?
5. 查詢測(cè)試,測(cè)試類如下:
Java代碼 ??執(zhí)行此測(cè)試類時(shí),控制臺(tái)打印信息如下所示:
Hibernate: select person0_.id as id3_1_, person0_.name as name3_1_, idcard1_.id as id4_0_, idcard1_.authorize_date as authorize2_4_0_ from Person person0_ left outer join id_card idcard1_ on person0_.id=idcard1_.id where person0_.id=?
2010-03-23 21:46:07.0
從打印的SQL語(yǔ)句可以看出,一對(duì)一關(guān)系查詢主對(duì)象時(shí),然后得到主對(duì)象中的從屬對(duì)象,通過(guò)left join一次就把查詢結(jié)果查詢出來(lái)了,因?yàn)閺膶?duì)象從屬于主對(duì)象。而一對(duì)多,多對(duì)一關(guān)系時(shí),從打印的SQL語(yǔ)句可知,它經(jīng)過(guò)了兩次查詢才將查詢結(jié)果查詢出來(lái)。這是它們的區(qū)別。如果一對(duì)一關(guān)系中先查詢從屬對(duì)象,然后得到從屬中的主對(duì)象時(shí)(即把上面測(cè)試類中的注釋1, 2都去掉再運(yùn)行),控制臺(tái)打印信息如下:
Hibernate: select idcard0_.id as id4_0_, idcard0_.authorize_date as authorize2_4_0_ from id_card idcard0_ where idcard0_.id=?
Hibernate: select person0_.id as id3_1_, person0_.name as name3_1_, idcard1_.id as id4_0_, idcard1_.authorize_date as authorize2_4_0_ from Person person0_ left outer join id_card idcard1_ on person0_.id=idcard1_.id where person0_.id=?
person1
從打印的SQL可以看出,這種先查從對(duì)象,然后得到從屬對(duì)象中的主對(duì)象,要經(jīng)過(guò)兩次查詢才能得到查詢結(jié)果。
如果只把測(cè)試程序中注釋1去掉運(yùn)行,則只會(huì)執(zhí)行上面兩次查詢中的第一次查詢。
6.one-to-one(元素)懶加載分析:
必須同時(shí)滿足下面的三個(gè)條件時(shí)才能實(shí)現(xiàn)懶散加載:1).lazy!=false (lazy缺省方式就!=false)2).constrained=true 3).fetch=select(fetch缺省方式即為select)
(因?yàn)橹鞅聿荒苡衏onstrained=true,所以主表沒(méi)有懶加載功能)。能夠懶加載的對(duì)象都是被改寫(xiě)過(guò)的代理對(duì)象,當(dāng)相關(guān)聯(lián)的session沒(méi)有關(guān)閉時(shí),訪問(wèn)這些懶加載對(duì)象(代理對(duì)象)的屬性(getId和getClass除外)時(shí),hibernate會(huì)初始化這些代理,或用Hibernate.initialize(proxy)來(lái)初始化代理對(duì)象;當(dāng)相關(guān)聯(lián)的session關(guān)閉后,再訪問(wèn)懶加載的對(duì)象將會(huì)出現(xiàn)異常。
測(cè)試:(1).注釋掉查詢測(cè)試程序中標(biāo)記為3的語(yǔ)句,運(yùn)行程序,控制臺(tái)打印信息如下:
Hibernate: select person0_.id as id4_1_, person0_.name as name4_1_, idcard1_.id as id5_0_, idcard1_.authorize_date as authorize2_5_0_ from Person person0_ left outer join id_card idcard1_ on person0_.id=idcard1_.id where person0_.id=?
說(shuō)明查詢主對(duì)象Person時(shí)沒(méi)有懶加載特性,因此它通過(guò)left outer join id_card表,同時(shí)把它的從對(duì)象IdCard也查詢出來(lái)了。那為什么一對(duì)一中查詢主對(duì)象時(shí),不能實(shí)現(xiàn)懶加載呢??大家可以看看person表的結(jié)構(gòu),你從表結(jié)構(gòu)中根本不能決斷出Person對(duì)象有沒(méi)有相應(yīng)的IdCard對(duì)象,所以它無(wú)法給setIdCard賦值(hibernate不能想當(dāng)然的認(rèn)為你有值,給你new一個(gè)代理對(duì)象給它),所以它一定要去查詢相關(guān)聯(lián)的對(duì)象表,看是否有與此Person對(duì)應(yīng)的IdCard記錄。而如果查詢的是從對(duì)象IdCard時(shí),因?yàn)閕dcard中的id是一個(gè)person表的一個(gè)外鍵,所以它必定有一個(gè)相對(duì)應(yīng)的Person對(duì)象(因?yàn)橛衏onstrained=true),所以它可以先返回給你一個(gè)代理對(duì)象,當(dāng)你真正需要Person對(duì)象的數(shù)據(jù)時(shí),它再去查詢數(shù)據(jù)庫(kù)。
(2).注釋掉查詢測(cè)試程序中標(biāo)記為3,4的語(yǔ)句,同進(jìn)將標(biāo)記為1的語(yǔ)句前的注釋去掉再運(yùn)行程序,控制臺(tái)打印信息如下:
Hibernate: select idcard0_.id as id5_0_, idcard0_.authorize_date as authorize2_5_0_ from id_card idcard0_ where idcard0_.id=?
從打印信息可以看出,查詢從對(duì)象IdCard時(shí)實(shí)現(xiàn)了懶加載功能,因?yàn)樗徊樵兞薎dCard對(duì)象,而關(guān)聯(lián)的Person對(duì)象它沒(méi)有進(jìn)行查詢。
(3).如果在(2)基礎(chǔ)上將標(biāo)記為2的語(yǔ)句前的注釋也去掉再運(yùn)行程序,控制臺(tái)打印信息如下:
Hibernate: select idcard0_.id as id5_0_, idcard0_.authorize_date as authorize2_5_0_ from id_card idcard0_ where idcard0_.id=?
Hibernate: select person0_.id as id4_1_, person0_.name as name4_1_, idcard1_.id as id5_0_, idcard1_.authorize_date as authorize2_5_0_ from Person person0_ left outer join id_card idcard1_ on person0_.id=idcard1_.id where person0_.id=?
person1
它就進(jìn)行了兩次查詢,將IdCard關(guān)聯(lián)的Person對(duì)象也進(jìn)行了查詢。因?yàn)樵L問(wèn)這些懶加載對(duì)象(代理對(duì)象)的屬性(getId和getClass除外)時(shí),hibernate會(huì)初始化這些代理.
(4).如果修改IdCard.hbm.xml映射文件,增加fetch="join",如下所示:
Xml代碼 ?再按(2)進(jìn)行測(cè)試,此時(shí)控制臺(tái)打印信息如下:
Hibernate: select idcard0_.id as id5_1_, idcard0_.authorize_date as authorize2_5_1_, person1_.id as id4_0_, person1_.name as name4_0_ from id_card idcard0_ inner join Person person1_ on idcard0_.id=person1_.id where idcard0_.id=?
此時(shí)查詢從對(duì)象IdCard時(shí)也不再懶加載了,通過(guò)inner join一次性將主從對(duì)象都查詢出來(lái)。
(5).如果修改IdCard.hbm.xml映射文件,增加lazy="false",如下所示:
Xml代碼 ?再按(2)進(jìn)行測(cè)試,此時(shí)控制臺(tái)打印信息如下:
Hibernate: select idcard0_.id as id5_0_, idcard0_.authorize_date as authorize2_5_0_ from id_card idcard0_ where idcard0_.id=?
Hibernate: select person0_.id as id4_1_, person0_.name as name4_1_, idcard1_.id as id5_0_, idcard1_.authorize_date as authorize2_5_0_ from Person person0_ left outer join id_card idcard1_ on person0_.id=idcard1_.id where person0_.id=?
雖然也不實(shí)現(xiàn)懶加載功能,一次性將主從對(duì)象都查詢出來(lái),但此時(shí)是經(jīng)過(guò)兩次查詢才得到結(jié)果。
如果修改IdCard.hbm.xml映射文件,增加lazy="proxy",如下所示,與缺省時(shí)一樣的效果,因?yàn)槿笔r(shí),lazy是=proxy
Xml代碼 ?如果修改IdCard.hbm.xml映射文件,如下所示,則lazy(懶加載失效),此時(shí)效果如測(cè)試(4)。
Xml代碼 ??
二、唯一外鍵方式實(shí)現(xiàn)一對(duì)一
基于外鍵的one-to-one可以描述為多對(duì)一。
?hibernate一對(duì)一唯一外鍵關(guān)聯(lián)映射(雙向關(guān)聯(lián)Person<---->IdCard)
一對(duì)一唯一外鍵雙向關(guān)聯(lián),需要在另一端(person),添加<one-to-one>標(biāo)簽,指示hibernate如何加載
其關(guān)聯(lián)對(duì)象,默認(rèn)根據(jù)主鍵加載idcard,外鍵關(guān)聯(lián)映射中,因?yàn)閮蓚€(gè)實(shí)體采用的是idcard的外鍵維護(hù)的關(guān)系, 所以不能指定主鍵加載idcard,而要根據(jù)idcard的外鍵加載,所以采用如下映射方式:
<one-to-one name="idcard" property-ref="person"/>
IdCard.hbm.xml的映射文件如下:
?
Xml代碼 ??Person.hbm.xml的映射文件如下:
Xml代碼 ??實(shí)體類不用修改,還是用上面的測(cè)試類進(jìn)行測(cè)試即可。
保存測(cè)試類運(yùn)行后,相對(duì)共享主鍵方式的one-to-one,id_card表的結(jié)構(gòu)發(fā)生了變化,表結(jié)構(gòu)如下所示:
DROP TABLE IF EXISTS `test`.`id_card`;
CREATE TABLE? `test`.`id_card` (
? `id` int(11) NOT NULL AUTO_INCREMENT,
? `authorize_date` datetime DEFAULT NULL,
? `person_id` int(11) DEFAULT NULL,
? PRIMARY KEY (`id`),
? UNIQUE KEY `person_id` (`person_id`),
? KEY `FK627C1FB45B253C91` (`person_id`),
? CONSTRAINT `FK627C1FB45B253C91` FOREIGN KEY (`person_id`) REFERENCES `person` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
數(shù)據(jù)庫(kù)表中記錄如下:
mysql> select * from person;
+----+---------+
| id | name??? |
+----+---------+
|? 1 | person1 |
+----+---------+
1 row in set (0.00 sec)
mysql> select * from id_card;
+----+---------------------+-----------+
| id | authorize_date????? | person_id |
+----+---------------------+-----------+
|? 1 | 2010-03-23 22:40:38 |???????? 1 |
+----+---------------------+-----------+
1 row in set (0.00 sec)
?
如果Person.hbm.xml映射文件中沒(méi)有<one-to-one/>這一項(xiàng)的話,運(yùn)行測(cè)試:
Java代碼 ??會(huì)拋出如下異常:
?java.lang.NullPointerException
因?yàn)檫@種關(guān)系成了IdCard--->Person的單向關(guān)聯(lián)了。知道了Person,找不到對(duì)應(yīng)的IdCard.
當(dāng)運(yùn)行如下測(cè)試時(shí):
Java代碼 ??控制臺(tái)會(huì)打印出Person相對(duì)應(yīng)的IdCard為null.
?
但如果得到了IdCard,卻能找到相應(yīng)的Person.測(cè)試如下:
Java代碼 ??能得到正常的結(jié)果,person name為person1.
總結(jié): 在缺省情況下,hibernate只有在一對(duì)一關(guān)聯(lián)中,查詢主對(duì)象時(shí),是進(jìn)行關(guān)聯(lián)查詢一次得到查詢結(jié)果,其它(多對(duì)多、多對(duì)一、一對(duì)多、一對(duì)一查詢從對(duì)象)的查詢都是分兩次查詢得到查詢結(jié)果。
總結(jié)
以上是生活随笔為你收集整理的hibernate 一对一(One-to-One)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: hibernate 一对多(one-to
- 下一篇: Hibernate组件(Componen