hibernate(四) 雙向多對多映射關系


      序言

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

          現在繼上一章節的一對多的映射關系講解后,今天來講講多對多的映射關系把,明白了一對多,多對多個人感覺還是比較容易的,需要理清楚其數據庫關系圖,那么你就拿下了它。映射文件的配置還是那么些死東西。

                                                --WZY

一、小疑問的解答

       問題一:到這里,有很多學習者會感到困惑,因為他不知道使用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,需要自己去解決。如果不動手寫,那么你看懂了,過幾天還是要靠抓別人的代碼,而不是自己動手寫。 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM