序言
之前講解了一對多(單向、雙向)、多對多(雙向),今天就講解一下最後一個關係,一對一。 心情不錯。狀態也挺好的,趕緊寫一篇博文造福一下大家把。
--WH
一、一對一關係的概述
一對一關係看起來簡單,其實也挺複雜的。其中關係就包含了四種,單向雙向和主鍵關聯外來鍵關聯。 什麼意思呢,也就是包含了單向一對一主鍵關聯、雙向一對一主鍵關聯,單向一對一外來鍵關聯,雙向一對一外來鍵關聯, 這四種中,單雙向就不用在說了把,就是看你業務需求來去設定是否是單雙向,而外來鍵關聯也很簡單,前面的一對多和多對多度是依靠外來鍵關聯關係來寫的。那主鍵關聯關係是怎麼樣的呢?其實跟外來鍵關聯差不多,唯一的區別就是,讓一個類的主鍵當作外來鍵使用來指向另一個關聯類的主鍵,從而兩個類的主鍵就達到了同步,也就是一致。你的主鍵是什麼我的主鍵就是什麼。如果這看不懂,那麼就看下面的詳細講解的。
二、單向一對一主鍵關聯
通過人和身份證這個一對一的例子來解釋。
實體類的屬性
資料庫關係圖
person中的id既是主鍵又是指向IdCard主鍵的外來鍵。通過外來鍵得特性,可以知道person的id和IdCard的主鍵id要相同,所以這才叫做主鍵關係。
IdCard.java和IdCard.hbm.xml
public class IdCard { private int id; private String cardNo; //。。。 } //IdCard.hbm.xml //這個沒什麼好說的,因為是單向一對一,這個就正常寫,他不用幹什麼。 <class name="domain1.IdCard" table="idcard"> <id name="id" column="id"> <!-- 主鍵生成策略 使用native,需要底層資料庫自己設定主鍵的值哦,比如AUTO_INCREMENT --> <generator class="native"> </generator> </id> <!-- 一些常規屬性 --> <property name="cardNo"></property>
Person.java和Person.hbm.xml
public class Person { private int id; private String name; private IdCard idCard;//體現一對一的關係。儲存對映類的例項物件。 //。。。 }
//Person.hbm.xml
<hibernate-mapping>
<class name="domain1.Person" table="person">
<id name="id" column="id">
<!-- 重點在這裡。主鍵生成策略 因為主鍵跟外來鍵是同一個,所以直接在這裡申明該主鍵就是外來鍵,並且指向了idCard這個類 -->
<generator class="foreign">
<param name="property">idCard</param>
</generator>
</id>
<!-- 一些常規屬性 -->
<property name="name"></property>
<!--由於在申明主鍵的時候已經將關係寫清楚了,所以在這裡沒有column這個屬性。按平常的習慣,我們會在這裡寫上column="資料庫中外來鍵欄位屬性名稱。"-->
<!--constrained屬性:就是表明我們的主鍵當外來鍵使用了。 這個屬性兩個作用,一是通知這種對應關係在上面已經寫過了,所以這裡才不需要寫column,二是表明這種關係是什麼,也就是主鍵當外來鍵。
其實還有一個級聯關係的作用,這裡不做多說明,具體會在這章之後一起講解,不然會讓人感覺很混亂。-->
<one-to-one name="idCard" constrained="true"></one-to-one>
</class>
新增測試資料
//省略前面獲得session的步驟和後面關閉session事物提交的步驟,只看關鍵程式碼,這裡先新增一條測試資料 IdCard idCard = new IdCard(); idCard.setCardNo("11111111"); Person person = new Person(); person.setName("qqq"); person.setIdCard(idCard); session.save(idCard);//這個其實可以不用的,講了級聯就可以省略的,現在先保留下來,大神的話看看不說話就好哦 session.save(person);
現在來真正測試一下這個單向一對一主鍵關係
//這樣會報異常,因為我們設定的是單向一對一,從person到Idcard,所以從idcard是查不到person。java.lang.NullPointerException IdCard idCard = (IdCard)session.get(IdCard.class, 1); System.out.println(idCard.getPerson().getName());; //但是這樣就查得到一個人的idCard Person person = (Person)session.get(Person.class,1); person.getIdCard().getCardNo();
到這裡,單向一對一主鍵關聯就講解完了,知道了單向,雙向就so easy了,
二、雙向一對一主鍵關聯
實體類屬性
這個圖中雙向箭頭的意思是不管從那邊查詢,度能夠找到對方,比如,person到Idcard:直接拿自己的主鍵值到對方表中查詢主鍵值一樣的,查詢到了就將該記錄放到自己的idCard屬性中,就行了 idCard到person也是一樣,拿主鍵值到對方表中查詢主鍵值相同的。查詢到了就將記錄放到person屬性變數中。
資料庫關係
跟單向一對一主鍵關係基本上一樣,只需要在IdCard這個實體類上加上一個Person person來儲存對應的person例項物件,並且在IdCard.hbm.xml中加上一個<one-to-one>的對映關係,來看一下
其他度不變,我寫出來的就是要變化的
IdCard.java和IdCard.hbm.xml
public class IdCard { private int id; private String cardNo; private Person person;//多了這個 //... } IdCard.hbm.xml <class name="domain1.IdCard" table="idcard"> <id name="id" column="id"> <!-- 主鍵生成策略 --> <generator class="native"> </generator> </id> <!-- 一些常規屬性 --> <property name="cardNo"></property> <!-- 這裡只需要寫這些就足夠了,因為one-to-one預設使用的就是用主鍵跟關聯類的主鍵進行比較,本來就是主鍵關係,通過主鍵跟主鍵比較,就能達到目的,所以這個中沒有column這個屬性,
但是可以配置一些別的屬性,不需要寫column, --> <one-to-one name="person"></one-to-one>
測試
現在在通過Idcard查詢person就不會報異常了,可以找到。
IdCard idCard = (IdCard)session.get(IdCard.class, 1); System.out.println(idCard.getPerson().getName());; //執行傳送的sql語句和結果 Hibernate: select idcard0_.id as id3_1_, idcard0_.cardNo as cardNo3_1_, person1_.id as id4_0_, person1_.name as name4_0_ from idcard idcard0_ left outer join person person1_ on idcard0_.id=person1_.id where idcard0_.id=? qqq
注意:主鍵關係的一對一的缺點:不知道你們發現了沒有,在增加實驗資料的時候,必須得先有Idcard,才能有person,
三、單向一對一外來鍵關聯
理解了主鍵關聯,這個外來鍵關係非常簡單,因為他就是多對一的一個特例,如果多端控制為1個的話,那不就是一對一了嗎,這裡要注意站的角度問題,多對一重點在多端,如果是一對多的話,重點在一端,一端本來就是1了,就沒有所謂的特例了,所以還是要到多端去設定讓他唯一,這樣就打到了一對一關係,因此上面說的是多對一的一個特例,這樣解釋應該清楚了。如何設定多端唯一呢,通過一個屬性 unique=ture。
來看看資料庫關係圖(跟一對多的資料庫關係模型一樣)
實體類中屬性
因為是單向一對一,從Person到IdCard,所以Person中多一個能存放IdCard例項物件的屬性
IdCard.java和IdCard.hbm.xml
public class IdCard { private int id; private String cardNo; //。。。 } IdCard.hbm.xml //很普通的一個對映檔案 <class name="domain1.IdCard" table="idcard"> <id name="id" column="id"> <!-- 主鍵生成策略 --> <generator class="native"> </generator> </id> <!-- 一些常規屬性 --> <property name="cardNo"></property> </class>
Person.java和Person.hbm.xml
public class Person { private int id; private String name; private IdCard idCard; //... }
//Person.hbm.xml
<class name="domain1.Person" table="person">
<id name="id" column="id">
<!-- 主鍵生成策略 -->
<generator class="native">
</generator>
</id>
<!-- 一些常規屬性 -->
<property name="name"></property>
<!--跟多對一一樣,只是增加了一個unique屬性。這樣就指定了這端為一了。-->
<many-to-one name="idCard" column="cardId" unique=true></many-to-one>
</class>
增加測試資料
IdCard idCard = new IdCard(); idCard.setCardNo("11111111"); Person person = new Person(); person.setName("qqq"); person.setIdCard(idCard); session.save(idCard); session.save(person);
測試資料為,看到這個圖就應該知道我們這裡是用外來鍵關係了,在person表中有一個外來鍵欄位值。
真正的測試一下單向一對一,其實也就是從person能查到idcard,但是從idcard查不到person
//這樣會報異常,因為我們設定的是單向一對一,從person到Idcard,所以從idcard是查不到person。java.lang.NullPointerException IdCard idCard = (IdCard)session.get(IdCard.class, 1); System.out.println(idCard.getPerson().getName());; //但是這樣就查得到一個人的idCard Person person = (Person)session.get(Person.class,1); person.getIdCard().getCardNo();
四、雙向一對一外來鍵關係
雙向也很簡單,只要改變兩個地方,就在IdCard.java和IdCard.hbm.xml中加入這種對映關係就足夠了。
實體類圖
資料庫關係圖還是跟單向一對一外來鍵關係一樣
IdCard.java和IdCard.hbm.xml
public class IdCard { private int id; private String cardNo; private Person person;//用來存放person物件,一對一關係 //... }
//IdCard.hbm.xml
<class name="domain1.IdCard" table="idcard">
<id name="id" column="id">
<!-- 主鍵生成策略 -->
<generator class="increment">
</generator>
</id>
<!-- 一些常規屬性 -->
<property name="cardNo"></property>
<!-- 要注意property-ref這個屬性,很重要,關鍵的地方就在這裡。
property-ref:指定關聯類的屬性名,這個屬性將會和本類的主鍵相對應。如果沒有指定,
會使用對方關聯類的主鍵來跟本類的主鍵比較,這裡要注意不是關聯表中的外來鍵欄位名。如果不指定這個屬性,那麼
一對一預設會使用主鍵去做對比。相當於原本我們
是可以通過本類的主鍵去和關聯類的外來鍵比較,然後來找到對應記錄的,但是這裡一對一中沒有
column屬性,所以該方法行不通,因此就想出了這種辦法,不跟外來鍵比,也不能跟主鍵比(因為不是主鍵關係),那麼
就跟關聯表中的一個屬性比,也就是我們這個person中的idCard屬性,為什麼找得到呢,因為從person能找到idcard,那麼
person中的idCard中就會有對應的值,我們跟該值比,也就能找到對應的person了。
class:person所在的類,這個也可以不寫,hibernate會自動幫我們找到
-->
<one-to-one name="person" property-ref="idCard" class="domain1.Person"></one-to-one>
</class>
測試
這樣從IdCard就能找到person了。而不是報空指標異常
IdCard idCard = (IdCard)session.get(IdCard.class, 1); System.out.println(idCard.getPerson().getName());; //執行傳送的sql語句和結果 Hibernate: select idcard0_.id as id3_1_, idcard0_.cardNo as cardNo3_1_, person1_.id as id4_0_, person1_.name as name4_0_ from idcard idcard0_ left outer join person person1_ on idcard0_.id=person1_.id where idcard0_.id=? qqq
五、總結
學完之後,我們應該知道
1、單向一對一主鍵關聯、雙向一對一主鍵關聯、單向一對一外來鍵關聯、雙向一對一外來鍵關聯的配置
2、主鍵關聯的特點:一個表中的主鍵就是外來鍵,指向另一個表中的主鍵,所以兩張表的主鍵是相同的,但是有一個缺點,就是必須依賴另一張表的主鍵,這在有些業務邏輯上是行不通的
3、知道了單向一對一主鍵關聯,那麼雙向一對一主鍵關聯就非常的簡單,其重點在主鍵id中的主鍵生成策略那塊還有constrained屬性的使用
4、單向一對一外來鍵關聯其實就是多對一的一個特例,其中關鍵的地方在unique這個屬性上面
5、單向一對一外來鍵關聯知道後,雙向一對一外來鍵關聯也非常簡單,關鍵的地方就在<one-to-one>中property-ref的配置,注意這個的意思是配置關聯類中的屬性,而不是關聯類中的外來鍵欄位名。
6、one-to-one預設是使用主鍵和主鍵進行比較來查詢資料,所以其中並沒有column這個屬性。因為沒有這個column屬性,所以就外來鍵關聯中就需要用到第5點的property-ref的屬性了。
到此,一對一關係就結束了,如果有什麼不懂的問題,就在下面留言,然後在儘自己的力量幫你們解答把。寫這一篇文章不容易,希望看了的同學覺得有幫助,點個推薦,將會給我很大的信心。