hibernate 一对一(One-to-One)
一對一(one-to-one)實例(Person-IdCard)
一對一的關系在數據庫中表示為主外關系.例如.人和身份證的關系.每個人都對應一個身份證號.我們應該兩個表.一個是關于人信息的表(Person).別外一個是身份證相關信息的表(id_card).id_card表的主鍵對應該Person表的主鍵id,也是Person表的外鍵.有人才能有身份證.所以此例中Person是主表,id_card表為從表。
hibernate的一對一關系有兩種形式,一種是共享主鍵方式,另一種是唯一外鍵方式.
一、共享主鍵方式實現一對一
1. 實體類設計如下:
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
?
數據庫表id_card的創建語句如下所示:(重點注意紅色字體部分)
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;
數據庫中記錄如下所示:
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代碼 ?這是讓從對象關聯上它所從屬的主對象。如果沒有這句話,則會拋出如下異常:
org.hibernate.id.IdentifierGenerationException: attempted to assign id from null one-to-one property: person
?
5. 查詢測試,測試類如下:
Java代碼 ??執行此測試類時,控制臺打印信息如下所示:
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語句可以看出,一對一關系查詢主對象時,然后得到主對象中的從屬對象,通過left join一次就把查詢結果查詢出來了,因為從對象從屬于主對象。而一對多,多對一關系時,從打印的SQL語句可知,它經過了兩次查詢才將查詢結果查詢出來。這是它們的區別。如果一對一關系中先查詢從屬對象,然后得到從屬中的主對象時(即把上面測試類中的注釋1, 2都去掉再運行),控制臺打印信息如下:
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可以看出,這種先查從對象,然后得到從屬對象中的主對象,要經過兩次查詢才能得到查詢結果。
如果只把測試程序中注釋1去掉運行,則只會執行上面兩次查詢中的第一次查詢。
6.one-to-one(元素)懶加載分析:
必須同時滿足下面的三個條件時才能實現懶散加載:1).lazy!=false (lazy缺省方式就!=false)2).constrained=true 3).fetch=select(fetch缺省方式即為select)
(因為主表不能有constrained=true,所以主表沒有懶加載功能)。能夠懶加載的對象都是被改寫過的代理對象,當相關聯的session沒有關閉時,訪問這些懶加載對象(代理對象)的屬性(getId和getClass除外)時,hibernate會初始化這些代理,或用Hibernate.initialize(proxy)來初始化代理對象;當相關聯的session關閉后,再訪問懶加載的對象將會出現異常。
測試:(1).注釋掉查詢測試程序中標記為3的語句,運行程序,控制臺打印信息如下:
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也查詢出來了。那為什么一對一中查詢主對象時,不能實現懶加載呢??大家可以看看person表的結構,你從表結構中根本不能決斷出Person對象有沒有相應的IdCard對象,所以它無法給setIdCard賦值(hibernate不能想當然的認為你有值,給你new一個代理對象給它),所以它一定要去查詢相關聯的對象表,看是否有與此Person對應的IdCard記錄。而如果查詢的是從對象IdCard時,因為idcard中的id是一個person表的一個外鍵,所以它必定有一個相對應的Person對象(因為有constrained=true),所以它可以先返回給你一個代理對象,當你真正需要Person對象的數據時,它再去查詢數據庫。
(2).注釋掉查詢測試程序中標記為3,4的語句,同進將標記為1的語句前的注釋去掉再運行程序,控制臺打印信息如下:
Hibernate: select idcard0_.id as id5_0_, idcard0_.authorize_date as authorize2_5_0_ from id_card idcard0_ where idcard0_.id=?
從打印信息可以看出,查詢從對象IdCard時實現了懶加載功能,因為它只查詢了IdCard對象,而關聯的Person對象它沒有進行查詢。
(3).如果在(2)基礎上將標記為2的語句前的注釋也去掉再運行程序,控制臺打印信息如下:
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
它就進行了兩次查詢,將IdCard關聯的Person對象也進行了查詢。因為訪問這些懶加載對象(代理對象)的屬性(getId和getClass除外)時,hibernate會初始化這些代理.
(4).如果修改IdCard.hbm.xml映射文件,增加fetch="join",如下所示:
Xml代碼 ?再按(2)進行測試,此時控制臺打印信息如下:
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)進行測試,此時控制臺打印信息如下:
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=?
雖然也不實現懶加載功能,一次性將主從對象都查詢出來,但此時是經過兩次查詢才得到結果。
如果修改IdCard.hbm.xml映射文件,增加lazy="proxy",如下所示,與缺省時一樣的效果,因為缺省時,lazy是=proxy
Xml代碼 ?如果修改IdCard.hbm.xml映射文件,如下所示,則lazy(懶加載失效),此時效果如測試(4)。
Xml代碼 ??
二、唯一外鍵方式實現一對一
基于外鍵的one-to-one可以描述為多對一。
?hibernate一對一唯一外鍵關聯映射(雙向關聯Person<---->IdCard)
一對一唯一外鍵雙向關聯,需要在另一端(person),添加<one-to-one>標簽,指示hibernate如何加載
其關聯對象,默認根據主鍵加載idcard,外鍵關聯映射中,因為兩個實體采用的是idcard的外鍵維護的關系, 所以不能指定主鍵加載idcard,而要根據idcard的外鍵加載,所以采用如下映射方式:
<one-to-one name="idcard" property-ref="person"/>
IdCard.hbm.xml的映射文件如下:
?
Xml代碼 ??Person.hbm.xml的映射文件如下:
Xml代碼 ??實體類不用修改,還是用上面的測試類進行測試即可。
保存測試類運行后,相對共享主鍵方式的one-to-one,id_card表的結構發生了變化,表結構如下所示:
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;
數據庫表中記錄如下:
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/>這一項的話,運行測試:
Java代碼 ??會拋出如下異常:
?java.lang.NullPointerException
因為這種關系成了IdCard--->Person的單向關聯了。知道了Person,找不到對應的IdCard.
當運行如下測試時:
Java代碼 ??控制臺會打印出Person相對應的IdCard為null.
?
但如果得到了IdCard,卻能找到相應的Person.測試如下:
Java代碼 ??能得到正常的結果,person name為person1.
總結: 在缺省情況下,hibernate只有在一對一關聯中,查詢主對象時,是進行關聯查詢一次得到查詢結果,其它(多對多、多對一、一對多、一對一查詢從對象)的查詢都是分兩次查詢得到查詢結果。
總結
以上是生活随笔為你收集整理的hibernate 一对一(One-to-One)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: hibernate 一对多(one-to
- 下一篇: Hibernate组件(Componen