1. 程式人生 > >hibernate(六) cascade(級聯)和inverse關系詳解

hibernate(六) cascade(級聯)和inverse關系詳解

是把 我們 概念 src 練習 需要 開始 解釋 關聯關系

    序言

        寫這篇文章之前,自己也查了很多的資料來搞清楚這兩者的關系和各自所做的事情,但是百度一搜,大多數博文感覺說的雲裏霧裏,可能博主自己清楚是怎麽一回事,但是給一個不懂的人或者一知半解的人看的話,別人也看不懂其中的關系,所以我自己寫博文的時候,會盡量用通俗通俗在通俗的語言去描述一個概念,希望能盡自己的力量去幫助你們理解。光看我的是不行的,最關鍵的是要自己動手去實踐一遍,能得出一樣的結論,那就說明懂了,在我不懂的時候,我就去自己實現它,一次次嘗試,慢慢的就總結出規律了。

                                            --WH

一、外鍵

     我為什麽要把這個單獨拿出來說呢?因為昨天我才發現我自己對這個外鍵的概念原來理解偏差了,並且很多人估計和我一樣,對這個東西理解錯了,現在就來說說一個什麽誤區。

     1、這張表的外鍵是deptId把?  2、這張表有外鍵嗎?

      大多數人這裏說的外鍵,度是指的一張表中被外鍵約束的字段名稱。這是很多人從一開始就默認的,其實並不然,

      解釋:對於每張有外鍵約束這個約束關系的表,都會給這個外鍵約束關系取一個名字,從給表設置外鍵約束的語句中就可以得知。

          CONSTRAINT 外鍵名 FOREIGN KEY 被外鍵約束修飾的字段名 REFERENCES 父表名(主鍵)

        所以說,平常大多數人口中的外鍵,指的是被外鍵約束修飾的字段名,外鍵關系是有自己的名稱的。這點大家需要搞清楚,雖然平常影響不大,但是到真正關鍵的地方,自己可能就會被這種小知識點給弄蒙圈。

二、cascade(級聯)關系

      為什麽要把這個單獨拿出來講一篇文章呢?因為我在看別人博文時,就把cascade和inverse和那幾種關聯關系連在一起講了,並且是那種一筆帶過的描述,寫的比較簡單,其實理解了確實很簡單,但對於剛開始學的人來說,這將會是一個大的災難,一知半解是最難受的了。

      解釋:級聯,就是對一個對象進行操作的時候,會把他相關聯的對象也一並進行相應的操作,相關聯的對象意思是指 比如前兩節學的一對多關系中,班級跟學生,Student的實體類中,存在著Classes對象的引用變量,如果保存Classes對象的引用變量有值的話,則該值就是相關聯的對象,並且在對student進行save時,如果保存Classes對象的引用變量有值,那麽就會將Classes對象也進行save操作, 這個就是級聯的作用。

      說大白話這個意思很難到位,舉個員工和部門 雙向一對多的例子把。

  創建實驗環境(這個可以自己去實現一下,練習一下關聯關系的配置)

  首先得對這兩個表的關系圖弄清楚,在接下來的所有分析中,度要帶著這個關系去分析,你才不會蒙圈

              技術分享

Staff.java 和 Staff.hbm.xml

技術分享
public class Staff {
    private int id;
    private String name;
    private Dept dept;
。。。
}

//Staff.hbm.xml
<hibernate-mapping>
    <class name="oneToMany.Staff" table="staff">
        <id name="id" column="id">
        //設置的increment,這個應該看得懂, <generator class="increment"></generator> </id> <property name="name"></property> //name:staff實體類中的dept屬性,column:子表中被外鍵約束修飾的字段名 class:Staff相關聯的Dept類 <many-to-one name="dept" column="deptId" class="oneToMany.Dept"></many-to-one> </class> </hibernate-mapping>
技術分享

Dept.java 和 Dept.hbm.xml

技術分享
public class Dept {
    private int id;
    private String name;
    private Set<Staff> staffSet = new HashSet<Staff>();
。。。
}
//Dept.hbm.xml
<hibernate-mapping>
    <class name="oneToMany.Dept" table="dept">
        <id name="id" column="id">
            <generator class="increment"></generator>
        </id>
        <property name="name"></property>
    //key:子表被外鍵約束修飾的字段名  
        <set name="staffSet">
            <key column="deptId"></key>
            <one-to-many class="oneToMany.Staff"/>
        </set>    
    </class>    
</hibernate-mapping>
技術分享

  配置了一個雙向一對多的關聯關系

  測試類

技術分享
    //創建新部門
Dept dept = new Dept(); dept.setName("er部門");
    //創建新的職員 Staff staff = new Staff(); staff.setName("www");

    //給職員中添加部門 staff.setDept(dept);
    //給部門中添加職員   dept.getStaffSet().add(staff);
     //保存部門 session.save(dept);
     //保存員工 session.save(staff);
技術分享

    結果 肯定將兩個實例保存到對應表中了。

        技術分享  

    在我們什麽都不清楚的時候,就會先保存部門,然後又要在保存一下員工,這樣才能讓兩條記錄進入響應的表中,如果使用了級聯,那麽就不需要這樣寫的如此麻煩那了。

    比如我們想在保存staff時,就把dept也順帶給保存了。

其他不變,就在staff.hbm.xml中增加級聯屬性  

技術分享
    <class name="oneToMany.Staff" table="staff">
<id name="id" column="id">
<generator class="increment"></generator>
</id>
<property name="name"></property>
<many-to-one name="dept" column="deptId" class="oneToMany.Dept" cascade="save-update"></many-to-one>

</class>
技術分享

    cascade="save-update" 在相關聯的屬性這裏設置級聯,表示該實體類對象如果在save或update或者saveOrUpdate操作時,會將這個相關聯的對象(前提是有這個對象,也就是引用對象變量有值)進行相應的操作,所以在測試類中就只需要寫上session.save(staff); 而不在需要寫session.save(dept)啦。因為有級聯的存在,   

技術分享
        Dept dept = new Dept();
        dept.setName("er部門");
        
        Staff staff = new Staff();
        staff.setName("www");
    //這個就是設置相關聯的對象 staff.setDept(dept); //這句話可以有可以沒有,具體作用在講解inverse的時候在說 dept.getStaffSet().add(staff); //session.save(dept);
    //只需要保存staff,就會將dept也一並保存了。 session.save(staff);
技術分享

  結果 如我們想的那樣,級聯保存了dept這個對象。

        技術分享

  當然,這只是在staff這一方設置級聯,你也可以在dept這一方設置級聯,使的只保存dept,就能將staff也保存了。這裏只是把保存對象做一個例子來講解,級聯並不一定就只是級聯保存還有很多別的屬性,看下面總結

總結:  

    知道了級聯的作用,下面來看看級聯的屬性

      cascade關系有以下幾種

          all: 所有情況下均進行關聯操作,即save-update和delete。
          none: 所有情況下均不進行關聯操作。這是默認值。
          save-update: 在執行save/update/saveOrUpdate時進行關聯操作。
          delete: 在執行delete 時進行關聯操作。
          all-delete-orphan: 當一個節點在對象圖中成為孤兒節點時,刪除該節點

      我們使用得是save-update,也就是說如果相關聯的對象在表中沒有記錄,則會一起save,如果有,看是否發生改變,會進行updat    

      其他應該度知道,說一下這個all-delete-orphan:什麽是孤兒節點,舉個例子,班級和學生,一張classes表,一張student表,student表中有5個學生的數據,其5個學生都屬於這個班級,也就是這5個學生中的外鍵字段都指向那個班級,現在刪除其中一個學生(remove),進行的數據操作僅僅是將student表中的該學生的外鍵字段置為null,也就是說,則個學生是沒有班級的,所以稱該學生為孤兒節點,我們本應該要將他完全刪除的,但是結果並不如我們所想的那樣,所以設置這個級聯屬性,就是為了刪除這個孤兒節點。也就是解決這類情況。

      cascade關系比較簡單,就是這麽幾種,不難理解。關鍵的地方是理解對關聯對象進行相應的操作,這個關聯對象指的是誰,知道了這個,就知道了為什麽在映射文件中那個位置設置級聯屬性了。

三、inverse

      這個是我比較難理解的一個點,一開始,因為很多人度沒把他說清楚。

      inverse的值是boolean值,也就是能設置為true或false。 如果一方的映射文件中設置為true,說明在映射關系(一對多,多對多等)中讓對方來維護關系。如果為false,就自己來維護關系。默認值是true。 並且這屬性只能在一端設置。比如一對多,這個一端。也就是在有set集合的這方設置。

      維護關系:維護什麽關系呢?包括兩個方面

        1、也就是維護外鍵的關系了,通俗點講,就是哪一方去設置這個被外鍵約束的字段的值。就拿上面這個例子來說,staff和dept兩張表不管進行什麽操作,只要關系到了另一張表,就不可避免的要通過操作外鍵字段,比如,staff查詢自己所屬的部門,就得通過被外鍵約束的字段值到dept中的主鍵中查找,如果dept想查詢自己部門中有哪些員工,就拿著自己的主鍵值跟staff中的外鍵字段做比較,找到相同的值則是屬於自己部門的員工。 這個是查詢操作, 現在如果是添加操作呢,staff表中添加一條記錄,並且部門屬於dept表中的其中一個,staff中有被外鍵約束修飾的字段,那是通過staff的insert語句就對這個外鍵字段賦值,還是讓dept對象使用update語句對其賦值呢,兩個都能對這個外鍵字段的值進行操作,誰去操作呢?如果不做設置,兩個都會操作,雖然不會出現問題,但是會影響性能,因為staff操作的話,在使用insert語句就能設置外鍵字段的值了,但是dept也會進行對其進行操作,又使用update語句,這樣一來,這個update就顯的很多余。

        2、維護級聯的關系,也就是說如果如果讓對方維護關系,則自己方的級聯將會失效,對方設置的級聯有用,如果自己維護關系,則自己方的級聯會有用,但是對方設置的級聯就會失效。

      就上面的運行結果,會發送5條sql語句,前兩條沒關系,看後面三條。看到最後一條了嗎,就是我們所說的發了一跳update語句。這就證實了我們上面所說的觀點,兩個表度對其維護外鍵關系。

技術分享
Hibernate: 
    select
        max(id) 
    from
        dept
Hibernate: 
    select
        max(id) 
    from
        staff
 上面這兩條不用管,這個是設置了主鍵生成策略為increment就會發送這兩句。得到數據庫表中最大的一個id值,才知道下一次要賦的id值給多少。
-------------------------------------------------
Hibernate: insert into dept (name, id) values (?, ?) Hibernate: insert into staff (name, deptId, id) values (?, ?, ?) Hibernate: update staff set deptId=? where id=?
技術分享

     為了解決這種問題,使用inverse這個屬性,來只讓一方維護關系(維護外鍵值)。

在一的一方設置該屬性,inverse=true 是默認值,也就是說讓staff來維護這種關系。

技術分享
//Dept.hbm.xml
<hibernate-mapping>
    <class name="oneToMany.Dept" table="dept">
        <id name="id" column="id">
            <generator class="increment"></generator>
        </id>
        <property name="name"></property>
    //inverse="true",讓對方維護關系,此時這裏的cascade設置沒什麽用,因為自身不維護關系,它也就失效了。
        <set name="staffSet" inverse="true" cascade="save-update">
            <key column="deptId"></key>
            <one-to-many class="oneToMany.Staff"/>
        </set>    
    </class>    
</hibernate-mapping>

//Staff.hbm.xml
<class name="oneToMany.Staff" table="staff">
<id name="id" column="id">
<generator class="increment"></generator>
</id>
<property name="name"></property>
    //這個級聯就有用,因為是讓自己這方維護關系
<many-to-one name="dept" column="deptId" class="oneToMany.Dept" cascade="save-update"></many-to-one>

</class>
技術分享

    註意:dept.getStaffSet().add(staff); 或者 staff.setDept(dept); 作用有兩個,一個是讓其雙方度有相關聯的對象,在設置級聯時,能只需保存一方,另一方就級聯保存了。另一個作用是這樣設置了關系,會讓staff或者dept這方會知道兩者的關系是怎麽樣的,也就是能夠有給外鍵字段賦值的能力。 因為我們設置了讓staff管理,所以dept.getStaffSet().add(staff);這句話就可以註釋掉,是多余了,告訴他了該怎麽設置外鍵字段的值,他也不會去設置,只需要讓staff去設置就好。

技術分享
        Dept dept = new Dept();
        dept.setName("er部門");
        
        Staff staff = new Staff();
        staff.setName("www");
        staff.setDept(dept);
        
        //dept.getStaffSet().add(staff);    
        
        //session.save(dept);//在dept方設置了級聯,但是只保存dept,staff也不會級聯保存,因為這種關系dept已經不管了,dept方的級聯會失效。所以需要將其註釋,在staff方設置級聯,保存staff就行
        session.save(staff);//級聯保存dept,並且自己會設置外鍵字段的值,也就是維護外鍵關系。
技術分享

    看發送的SQL語句,如果猜想沒錯的話,這次就不會在發送update語句了。

技術分享
Hibernate: 
    select
        max(id) 
    from
        dept
Hibernate: 
    select
        max(id) 
    from
        staff
------------------------------------------------------
Hibernate: 
    insert 
    into
        dept
        (name, id) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        staff
        (name, deptId, id) 
    values
        (?, ?, ?)
技術分享

    如果將inverse設置為false。就表明讓dept來設置外鍵值,staff可以不用管了,

技術分享
//Dept.hbm.xml
<hibernate-mapping>
    <class name="oneToMany.Dept" table="dept">
        <id name="id" column="id">
            <generator class="increment"></generator>
        </id>
        <property name="name"></property>
    //inverse="false",讓自己維護關系,此時這裏的cascade設置就生效了,對方的eascade失效。
        <set name="staffSet" inverse="false" cascade="save-update">
            <key column="deptId"></key>
            <one-to-many class="oneToMany.Staff"/>
        </set>    
    </class>    
</hibernate-mapping>

//Staff.hbm.xml
   <class name="oneToMany.Staff" table="staff">
        <id name="id" column="id">
            <generator class="increment"></generator>
        </id>
        <property name="name"></property>
    //這個級聯失效,也就是說,如果單單只保存staff,是不會級聯保存dept的。
        <many-to-one name="dept" column="deptId" class="oneToMany.Dept" cascade="save-update"></many-to-one>
        
    </class>
技術分享

      因為有了上面的配置,看看測試的代碼如何寫

技術分享
       Dept dept = new Dept();
        dept.setName("er部門");
        
        Staff staff = new Staff();
        staff.setName("www");
       //這句就可以去掉了,staff不會在管理了。 
     //staff.setDept(dept); //因為dept來維護關系,所以必須得讓他知道如何去關系這種外鍵關系並且知道相關聯對象,所以說這句話的作用正好又能讓級聯的作用體現出來,又能體現外鍵關系, dept.getStaffSet().add(staff); session.save(dept);//因為在dept方設置了save-update級聯,所以只保存dept就可以了。 //session.save(staff);
技術分享

    這個的結果就會有update語句,因為是dept來管理,他要管理,就必須發送update

技術分享
Hibernate: 
    select
        max(id) 
    from
        dept
Hibernate: 
    select
        max(id) 
    from
        staff
 上面這兩條不用管,這個是設置了主鍵生成策略為increment就會發送這兩句。得到數據庫表中最大的一個id值,才知道下一次要賦的id值給多少。
-------------------------------------------------

Hibernate: 
    insert 
    into
        dept
        (name, id) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        staff
        (name, deptId, id) 
    values
        (?, ?, ?)
Hibernate: 
    update
        staff 
    set
        deptId=? 
    where
        id=?
技術分享

四、總結

    到這裏,inverse和cascade這兩個的作用就已經講解完了

        1、inverse的權限在cascade之上,意思就是cascade是否有用,還得看inverse這個屬性

        2、inverse的作用:在映射關系中,讓其中一方去維護關系,好處就是能提高性能,不用重復維護。維護兩種關系,看下

            2.1 控制級聯關系是否有效

                cascade是否有效,就得看inserve的值,如果是自己方來維護關系,那麽cascade就有效,反之無效

            2.2 控制外鍵關系

                這個就得通過讓自己擁有對方的實例引用(可能是set,也可能就是單個存儲對象的變量),這樣才具備控制外鍵關系的能力,然後看inserve的值,

        3、inverse只能在一的一方設置,並且默認值是true,也就是說,不設置inverse時,默認是讓多的一方去維護關系,這種一般是在雙向、外鍵關系中才設置inverse的值,如果是單向的,就只有一方有維護關系的權利。

        4、在以後的代碼中,先要搞清楚關系,才能寫出性能最好的代碼。通過學習這兩個屬性,在測試代碼中,就不必那麽麻煩了,只需要考慮維護關系的一方,另一方就會自動保存了。

        5、如果你對測試代碼發送了多少條sql語句不清楚的話,可以往前面看看那篇講解一級緩存和三種狀態的文章,通過快照區和session作用域來分析,到底會發送多少條sql語句。

  

hibernate(六) cascade(級聯)和inverse關系詳解