1. 程式人生 > >Hibernate學習(五)———— hibernate一對一關係對映詳解

Hibernate學習(五)———— hibernate一對一關係對映詳解

 一、一對一關係的概述

      一對一關係看起來簡單,其實也挺複雜的。其中關係就包含了四種,單向雙向和主鍵關聯外來鍵關聯。 什麼意思呢,也就是包含了單向一對一主鍵關聯、雙向一對一主鍵關聯,單向一對一外來鍵關聯,雙向一對一外來鍵關聯, 這四種中,單雙向就不用在說了把,就是看你業務需求來去設定是否是單雙向,而外來鍵關聯也很簡單,前面的一對多和多對多度是依靠外來鍵關聯關係來寫的。那主鍵關聯關係是怎麼樣的呢?其實跟外來鍵關聯差不多,唯一的區別就是,讓一個類的主鍵當作外來鍵使用來指向另一個關聯類的主鍵,從而兩個類的主鍵就達到了同步,也就是一致。你的主鍵是什麼我的主鍵就是什麼。如果這看不懂,那麼就看下面的詳細講解的。

 

 

二、單向一對一主鍵關聯

      通過人和身份證這個一對一的例子來解釋。

          實體類的屬性

                 

          資料庫關係圖

              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的屬性了。

 

        到此,一對一關係就結束了,如果有什麼不懂的問題,就在下面留言,然後在儘自己的力量幫你們解答把。寫這一篇文章不容易,希望看了的同學覺得有幫助,點個推薦,將會給我很大的信心。