hibernate(四) 雙向多對多對映關係

一杯涼茶發表於2016-12-01

      序言

          莫名長了幾顆痘,真TM疼,可能是現在運動太少了,天天對著電腦,決定了,今天下午花兩小時去跑步了,

          現在繼上一章節的一對多的對映關係講解後,今天來講講多對多的對映關係把,明白了一對多,多對多個人感覺還是比較容易的,需要理清楚其資料庫關係圖,那麼你就拿下了它。對映檔案的配置還是那麼些死東西。

                                                --WH

 

一、小疑問的解答

       問題一:到這裡,有很多學習者會感到困惑,因為他不知道使用hibernate是不是需要自己去建立表,還是hibernate全自動,如果需要自己建立表,那麼主外來鍵這種設定也是自己設定嗎?這讓人感到很困惑,現在就來解決一下這個小疑問(如果知道了的可以直接跳過看下面的多對多對映關係講解)

       解答:從實際開發的角度說:肯定是先建立表,並且表中自己會匯入初始資料,然後在逆向生成實體類,並且各種對映關係看自己需要什麼就生成什麼。

          在我們測試和學習階段也可以如此,先建立好資料庫和表還有一些初始化資料,也可以不用把資料庫中各種表關係和表欄位建立好,只需要將資料庫手動建好,也就是說資料庫中有沒有表度沒關係,關鍵是必須得有這個資料庫。如果沒有表,那麼我們就得通過程式碼來建立表,比如new一個實體類,就相當於建立了一張表,如果沒有表的情況下,你就直接去查詢,那麼肯定會報不存在表的錯誤,然後每個表中的欄位和表之間的外來鍵關係,度可以通過hibernate來幫我們完成,我們編寫對映檔案和實體類,就是來建立表之間的關係和表中的內容的。這取決於一個配置屬性。

            <prop key="hibernate.hbm2ddl.auto">value</prop>

            value值可以為四種

                create:表示啟動的時候先drop,再create。 也就是說每次啟動,會先將資料庫中表給刪除,然後在建立一個。開發人員測試用的比較多

                create-drop: 也表示建立,只不過再系統關閉前執行一下drop。 每次關閉前就將表給刪除掉,等用的時候在建立

                update: 這個操作啟動的時候會去檢查schema是否一致,如果不一致會做scheme更新。就是檢查hibernate中和資料庫表中欄位關係是否一致,不一致就會更新資料庫

                validate: 啟動時驗證現有schema與你配置的hibernate是否一致,如果不一致就丟擲異常,並不做更新。

 

        總結:只要我們資料庫中存在表,我們就可以對他進行操作(改造表中欄位,通過外來鍵聯合其他表等度可以獨立完成),而不需要我們在去手動操作底層資料庫。所以在大多數書上就是直接上操作hibernate的程式碼,而不關心資料庫怎麼樣,他們的前提是資料庫中有他們所操作的表就夠了。

            

 

    問題二:在xxx.hbm.xml中的主鍵生成策略,是否需要讓資料庫底層主鍵自動生成,這個需要搞清楚,不能夠混淆。當你糾結主鍵生成策略與資料庫主鍵到底該不該用AUTO_INCREMENT時,那你就需要去總結一下這兩者的關係了。(我是個大好人TMD,幫你們總結一份)

            <id name="id" column="id">
                  <!-- 主鍵生成策略 -->
                  <generator class="increment"></generator>
                </id>

       主鍵生成策略常用就六種,

        1、increment:hibernate管理,自動讓主鍵自動增長,而資料庫中主鍵就可以不用在設定AUTO_INCREMENT了。

        2、identity:底層資料庫管理,也就是說資料庫需要自己設定主鍵自動增長(AUTO_INCREMENT),不設定的話,就需要自己手動設定,不太好。

              Mysql和sql server支援這個,但是Oracle不支援,也就是說Orable不支援底層自動增長,但是Oracle有另一種底層機制,那就是sequence

        3、sequence:底層資料庫管理,資料庫自己來提供這個主鍵是多少,具體如何算我們不瞭解

              Oracle就使用這個,Mysql就不支援這個,但mysql支援identity,也就是讓資料庫自動增長,這兩個的區別就在這裡,一個底層使用AUTO_INCREMENT,一個底層使用這個序列化增長的。

        4、native:hibernate不管理,讓資料庫底層自己選擇主鍵如何生成,也就是說,如果是mysql,那就預設使用identity,也就是我們自己需要設定AUTO_INCREMENT,如果是Oracle,那麼就預設使用sequence,讓資料庫底層自己設計哪個序列化增長

        5、uuid:這個大家很熟悉,也就是我們不需要在資料庫中主鍵上設定什麼,每次度會給主鍵生成一個隨機的32位字串

        6、assigned:這個很簡單,就是我們需要手動自己給主鍵設定值,hibernate和資料庫度不主動幫我們設定。

        就這六種,其實很好學,identity和sequence就是需要我們自己在資料庫中設定自動增長或者序列化增長,increment就是hibernate幫我們管理主鍵。資料庫底層不需要寫任何東西,前提是資料庫需要支援自動增長,比如Oracle就用不了這個,native也是需要我們自己在資料庫中設定,但是比起identity和sequence更加靈活,更改底層資料庫,這個就不需要改,uuid也很熟悉大家,assigned這個更簡單,就是用來自己寫主鍵值的嘛。  

 

      到這就結束了,正式開始我們的多對多對映關係把

 

 

二、多對多對映關係

      已經清楚了一對多的關係後,那麼就簡單很多了,多對多其實也分單向多對多,和雙向多對多,但是單向多對多比較簡單,並且用的最多的就是雙向多對多了,知道了雙向多對多,單向多對多就非常簡單,所以我們直接講雙向多對多

      生活中有很多例子就是雙向多對多的,最簡單和貼近我們生活的,

          1、學生和選課之間的關係了,學生可以選擇多門課程,課程可以被多個學生選擇,

          2、在淘寶中購物,一件商品能被多個人選擇,一個人能夠選擇多個商品

          3、....很多這種多對多關係,就拿學生和選課這個例子來講解把。

 

      要儲存多對多的關係,兩張表是不夠的,需要增加第三張表來表示這種關係,來看下面的資料庫關係圖。

          這個圖意思就是用student_course這個中間表來儲存student和course這兩張表的關係,並且student_course是聯合主鍵。同時也是外來鍵,指向student的sid和Course的cid。

          有人肯定會覺得為什麼還要用第三張表,不直接使用兩個外來鍵,你指向我,我指向你這樣呢,這樣會暴露出一個很大的問題,如果學過資料庫就應該會知道,這樣的兩張表相互關聯,那麼這兩張表的關係就固定在那裡了,刪哪個表就不能刪,這個都市小事,當你在查詢一個表中資料時,會造成死迴圈,你查了我,我又在查你,一直重複下去。那就GG了。

                      

      解析

        為什麼需要設定聯合主鍵和兩個外來鍵:

                student通過自己的主鍵在連線表中查詢,因為是複合主鍵,所以查詢到的記錄有很多,而不是唯一的,這些記錄中就記錄了一個學生的所有課程,拿到這些記錄後,由於連線表中的有course的外來鍵,所以能夠通過記錄中的c_id,找到course表中對應的記錄。反過來,course通過自己的主鍵在連線表中查詢得到很多記錄,由於連線表中也有student的外來鍵,所以通過記錄中的s_id也能找到student中對應的記錄。所以,表的設計就是這樣,需要聯合主鍵,並且也都市外來鍵,這些度是有用的。少一個就查不出對方了。

 

 

 

 

        2、實體類和對映配置

    Student持久化類和Student.hbm.xml

//Student實體類
public class Student {
    private Integer sid;
    private String sname;
    //用set集合來儲存選的多個課程
    private Set<Course> courseSet = new HashSet<Course>();

  set、get.....  
}

//Student.hbm.xml
    <class name="domain.Student" table="student">
        <id name="sid" column="sid">
            <!-- 主鍵生成策略 -->
            <generator class="increment"></generator>
        </id>
        <!-- 一些常規屬性 -->
        <property name="sname"></property>

<!-- 關鍵的地方就在這裡了。一定要搞清楚兩個column分別指的是什麼意思 腦袋中要有哪個資料庫關係圖-->

<!--要查詢到所有的course,就需要通過連線表,所以申明連線表的名稱-->
<set name="courseSet" table="student_course"> <!-- 本實體類在連線表中的外來鍵名稱,過程我們上面分析的很清楚了,為什麼需要這個呢?讓hibernate知道連線表中有一個外來鍵名為s_id的指向本實體類 --> <key column="s_id"></key> <!-- 多對多對映關係,對映類和其對映類在連線表中的外來鍵名稱 這個的意思跟上面的一樣,也是宣告讓hibernate知道,這樣一來,hibernate就知道如何查詢了--> <many-to-many class="domain.Course" column="c_id"></many-to-many> </set> </class>

      Course和Course.hbm.xml

//Course實體類
public class Course {
    private int cid;
    private String cname;
    private Set<Student> studentSet = new HashSet<Student>();
  ...  
}

//Course.hbm.xml 有了上面的分析,這個就簡單了,內容和意義跟上面的一模一樣。
    <class name="domain.Course" table="course">
        <id name="cid" column="cid">
            <!-- 主鍵生成策略 -->
            <generator class="increment"></generator>
        </id>
        <!-- 一些常規屬性 -->
        <property name="cname"></property>
        <set name="studentSet" table="student_course">
            <!-- 本類在連線表中外來鍵的名稱, -->
            <key column="c_id"></key>
      <!--多對多對映關係,對映類和其對映類在連線表中的外來鍵名稱--> <many-to-many class="domain.Student" column="s_id"></many-to-many> </set> </class>

 

    3、測試類

//其他的就省略了,只寫重要的程式碼。由於剛建立起來的關係,資料庫中還沒有任何資料,那麼就新增初始資料了。這裡會出現一個問題。如果把註釋的這一行給放開的話,
報一個org.hibernate.exception.ConstraintViolationException錯誤,為什麼會這樣呢?其實從我們上面對資料庫設計圖的分析我們可以知道(只是那個分析是用查詢來當例子,增加資料跟那個過程差不多),
在course的StudentSet中新增一個學生,這個過程是怎樣的呢?因為要對StudentSet進行操作,那麼就會找到連線表,新增一個學生,那麼就會在連線表中,新增一條course的cid對應student的sid的記錄,然後如果
你在用student.getCourseSet().add(course)的話,又往連線表中增加了一條一模一樣的記錄,這個時候肯定會報錯啊,因為是聯合主鍵,兩條記錄一樣,怎麼會插的進去呢。所以就會出現違反約束異常(違反主鍵約束)了。
Course course = new Course(); course.setCname("化學"); Student student = new Student(); student.setSname("qqq"); course.getStudentSet().add(student); // student.getCourseSet().add(course); session.save(course); session.save(student);

 

     4、效果圖

               

五、總結  

       雙向多對多理解了之後就會發現很簡單,但是在開始學的時候,會覺得裡面很繞,所以一定清楚每一步是什麼,重要的是理解為什麼連線表需要設定成那樣。理解了你就會很輕鬆的學會了這個雙向多對多對映關係,你一定動手自己去實現一下,其中隱藏了很多BUG,需要自己去解決。如果不動手寫,那麼你看懂了,過幾天還是要靠抓別人的程式碼,而不是自己動手寫。 

 

相關文章