hibernate 一对一(One-to-One)
一對一(one-to-one)實(shí)例(Person-IdCard)
一對一的關(guān)系在數(shù)據(jù)庫中表示為主外關(guān)系.例如.人和身份證的關(guān)系.每個人都對應(yīng)一個身份證號.我們應(yīng)該兩個表.一個是關(guān)于人信息的表(Person).別外一個是身份證相關(guān)信息的表(id_card).id_card表的主鍵對應(yīng)該P(yáng)erson表的主鍵id,也是Person表的外鍵.有人才能有身份證.所以此例中Person是主表,id_card表為從表。
hibernate的一對一關(guān)系有兩種形式,一種是共享主鍵方式,另一種是唯一外鍵方式.
一、共享主鍵方式實(shí)現(xiàn)一對一
1. 實(shí)體類設(shè)計(jì)如下:
Person類:
Java代碼 ??IdCard類:
Java代碼 ??2.映射文件:
Person.hbm.xml文件如下:
Xml代碼 ??IdCard.hbm.xml文件如下:
Xml代碼 ?3. 在hibernate.cfg.xml文件中注冊映射文件:
Xml代碼 ??4.測試類如下:
Java代碼 ??控制臺打印信息如下所示:
Hibernate: insert into Person (name) values (?)
Hibernate: insert into id_card (authorize_date, id) values (?, ?)
person name : person1
?
數(shù)據(jù)庫表id_card的創(chuàng)建語句如下所示:(重點(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ù)庫中記錄如下所示:
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)
?
在測試時一定要注意寫上這句代碼:
Java代碼 ?這是讓從對象關(guān)聯(lián)上它所從屬的主對象。如果沒有這句話,則會拋出如下異常:
org.hibernate.id.IdentifierGenerationException: attempted to assign id from null one-to-one property: person
?
5. 查詢測試,測試類如下:
Java代碼 ??執(zhí)行此測試類時,控制臺打印信息如下所示:
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語句可以看出,一對一關(guān)系查詢主對象時,然后得到主對象中的從屬對象,通過left join一次就把查詢結(jié)果查詢出來了,因?yàn)閺膶ο髲膶儆谥鲗ο蟆6粚Χ?#xff0c;多對一關(guān)系時,從打印的SQL語句可知,它經(jīng)過了兩次查詢才將查詢結(jié)果查詢出來。這是它們的區(qū)別。如果一對一關(guān)系中先查詢從屬對象,然后得到從屬中的主對象時(即把上面測試類中的注釋1, 2都去掉再運(yùn)行),控制臺打印信息如下:
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可以看出,這種先查從對象,然后得到從屬對象中的主對象,要經(jīng)過兩次查詢才能得到查詢結(jié)果。
如果只把測試程序中注釋1去掉運(yùn)行,則只會執(zhí)行上面兩次查詢中的第一次查詢。
6.one-to-one(元素)懶加載分析:
必須同時滿足下面的三個條件時才能實(shí)現(xiàn)懶散加載:1).lazy!=false (lazy缺省方式就!=false)2).constrained=true 3).fetch=select(fetch缺省方式即為select)
(因?yàn)橹鞅聿荒苡衏onstrained=true,所以主表沒有懶加載功能)。能夠懶加載的對象都是被改寫過的代理對象,當(dāng)相關(guān)聯(lián)的session沒有關(guān)閉時,訪問這些懶加載對象(代理對象)的屬性(getId和getClass除外)時,hibernate會初始化這些代理,或用Hibernate.initialize(proxy)來初始化代理對象;當(dāng)相關(guān)聯(lián)的session關(guān)閉后,再訪問懶加載的對象將會出現(xiàn)異常。
測試:(1).注釋掉查詢測試程序中標(biāo)記為3的語句,運(yùn)行程序,控制臺打印信息如下:
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=?
說明查詢主對象Person時沒有懶加載特性,因此它通過left outer join id_card表,同時把它的從對象IdCard也查詢出來了。那為什么一對一中查詢主對象時,不能實(shí)現(xiàn)懶加載呢??大家可以看看person表的結(jié)構(gòu),你從表結(jié)構(gòu)中根本不能決斷出Person對象有沒有相應(yīng)的IdCard對象,所以它無法給setIdCard賦值(hibernate不能想當(dāng)然的認(rèn)為你有值,給你new一個代理對象給它),所以它一定要去查詢相關(guān)聯(lián)的對象表,看是否有與此Person對應(yīng)的IdCard記錄。而如果查詢的是從對象IdCard時,因?yàn)閕dcard中的id是一個person表的一個外鍵,所以它必定有一個相對應(yīng)的Person對象(因?yàn)橛衏onstrained=true),所以它可以先返回給你一個代理對象,當(dāng)你真正需要Person對象的數(shù)據(jù)時,它再去查詢數(shù)據(jù)庫。
(2).注釋掉查詢測試程序中標(biāo)記為3,4的語句,同進(jìn)將標(biāo)記為1的語句前的注釋去掉再運(yùn)行程序,控制臺打印信息如下:
Hibernate: select idcard0_.id as id5_0_, idcard0_.authorize_date as authorize2_5_0_ from id_card idcard0_ where idcard0_.id=?
從打印信息可以看出,查詢從對象IdCard時實(shí)現(xiàn)了懶加載功能,因?yàn)樗徊樵兞薎dCard對象,而關(guān)聯(lián)的Person對象它沒有進(jìn)行查詢。
(3).如果在(2)基礎(chǔ)上將標(biāo)記為2的語句前的注釋也去掉再運(yùn)行程序,控制臺打印信息如下:
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對象也進(jìn)行了查詢。因?yàn)樵L問這些懶加載對象(代理對象)的屬性(getId和getClass除外)時,hibernate會初始化這些代理.
(4).如果修改IdCard.hbm.xml映射文件,增加fetch="join",如下所示:
Xml代碼 ?再按(2)進(jìn)行測試,此時控制臺打印信息如下:
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=?
此時查詢從對象IdCard時也不再懶加載了,通過inner join一次性將主從對象都查詢出來。
(5).如果修改IdCard.hbm.xml映射文件,增加lazy="false",如下所示:
Xml代碼 ?再按(2)進(jìn)行測試,此時控制臺打印信息如下:
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)懶加載功能,一次性將主從對象都查詢出來,但此時是經(jīng)過兩次查詢才得到結(jié)果。
如果修改IdCard.hbm.xml映射文件,增加lazy="proxy",如下所示,與缺省時一樣的效果,因?yàn)槿笔r,lazy是=proxy
Xml代碼 ?如果修改IdCard.hbm.xml映射文件,如下所示,則lazy(懶加載失效),此時效果如測試(4)。
Xml代碼 ??
二、唯一外鍵方式實(shí)現(xiàn)一對一
基于外鍵的one-to-one可以描述為多對一。
?hibernate一對一唯一外鍵關(guān)聯(lián)映射(雙向關(guān)聯(lián)Person<---->IdCard)
一對一唯一外鍵雙向關(guān)聯(lián),需要在另一端(person),添加<one-to-one>標(biāo)簽,指示hibernate如何加載
其關(guān)聯(lián)對象,默認(rèn)根據(jù)主鍵加載idcard,外鍵關(guān)聯(lián)映射中,因?yàn)閮蓚€實(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í)體類不用修改,還是用上面的測試類進(jìn)行測試即可。
保存測試類運(yùn)行后,相對共享主鍵方式的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ù)庫表中記錄如下:
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映射文件中沒有<one-to-one/>這一項(xiàng)的話,運(yùn)行測試:
Java代碼 ??會拋出如下異常:
?java.lang.NullPointerException
因?yàn)檫@種關(guān)系成了IdCard--->Person的單向關(guān)聯(lián)了。知道了Person,找不到對應(yīng)的IdCard.
當(dāng)運(yùn)行如下測試時:
Java代碼 ??控制臺會打印出Person相對應(yīng)的IdCard為null.
?
但如果得到了IdCard,卻能找到相應(yīng)的Person.測試如下:
Java代碼 ??能得到正常的結(jié)果,person name為person1.
總結(jié): 在缺省情況下,hibernate只有在一對一關(guān)聯(lián)中,查詢主對象時,是進(jìn)行關(guān)聯(lián)查詢一次得到查詢結(jié)果,其它(多對多、多對一、一對多、一對一查詢從對象)的查詢都是分兩次查詢得到查詢結(jié)果。
總結(jié)
以上是生活随笔為你收集整理的hibernate 一对一(One-to-One)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: hibernate 一对多(one-to
- 下一篇: Hibernate组件(Componen