Hibernate表關係之一對多(inverse詳解)

 在映射一對多表關係的時候存在一個inverse的問題,一直以來對inverse不怎麼了解,今天學到它想要弄明白,在網上看了無數個帖子仍是不懂,最後終於看到一位高人的一篇帖子,解釋得很透徹,終於完全瞭解inverse的含義了,歷時5個小時!如今若不是在喜悅的掩蓋下我早已吐血了。。。下面說說一對多表關係的應用和inverse詳解:java

以父與子關係爲例:父親能夠有多個孩子,而孩子只能有一個父親。那麼反映到數據表是這樣實現的:
create table parent(
pid int auto_increment primary key,
pname varchar(20) not null
)
create table child(
cid int auto_increment primary key,
pid int not null,       //在孩子表中添加父親表的外鍵,實現一對多
cname varchar(20) not null
)
 
用Hibernate自動生成的兩個映射類,以Set集合方式表示一對多,以一個父親對象方式表示多對一:
public class Child {
private Integer cid;
private Parent parent;     //屬於哪一個父親
private String cname;
...
}
public class Parent {
private Integer pid;
private String pname;
private Set childs = new HashSet(0);     //包含孩子的集合
...
}
 
關鍵仍然是兩個映射文件:
Child.hbm.xml:
<hibernate-mapping>
     <class name="com.pojo.Child" table="child" catalog="test">
         <id name="cid" type="java.lang.Integer">
             <column name="cid" />
             <generator class="native" />
         </id>
        //多對一的屬性爲parent,對應數據庫中的字段爲pid,級聯更新爲all
        <many-to-one name="parent" class="com.pojo.Parent" fetch="select" cascade="all">
             <column name="pid" />
         </many-to-one>
         <property name="cname" type="java.lang.String">
             <column name="cname" length="20" not-null="true" />
         </property>
     </class>
</hibernate-mapping>
 
Parent.hbm.xml:
<hibernate-mapping>
     <class name="com.pojo.Parent" table="parent" catalog="test">
         <id name="pid" type="java.lang.Integer">
             <column name="pid" />
             <generator class="native" />
         </id>
         <property name="pname" type="java.lang.String">
             <column name="pname" length="20" not-null="true" />
         </property>
       <set name="childs" inverse="true" cascade="all">   //描述孩子集合,把控制權交給孩子
             <key>
                 <column name="pid" not-null="true" />   //以Pid來分辨哪一個孩子是屬於這個父親的
             </key>
             <one-to-many class="com.pojo.Child" />
         </set>
     </class>
</hibernate-mapping>
 
測試類:
Parent p=new Parent();
Child c1=new Child();
Child c2=new Child();
p.setPname("xpc5");
c1.setCname("cloud");
c1.setParent(p);     //設置孩子1的父親爲p
c2.setCname("cloud1");
c2.setParent(p);     //設置孩子2的父親爲p
p.getChilds().add(c1);     //爲父親添加孩子1
p.getChilds().add(c2);     //爲父親添加孩子2
 
session.save(p);
 
運行後在插入一個父親記錄的同時,兩個孩子記錄被自動插入
 
下面詳細說明一下inverse的做用:
inverse是反轉的意思,在Hibernate中inverse決定誰掌有控制權,即表之間的關係誰來維護,那麼當inverse="true"表示交出控制權,inverse="false"表示不交出控制權,這樣理解有些難度,網上大多對此塊講解不清楚,下面以我本身的理解說明,首先來看實例,還以上面爲例,在Parent.hbm.xml中有這樣的描述:
<set name="childs" inverse="true" cascade="all"> 
...
</set>
首先說明這個映射文件是描述Parent類的,那麼set描述的是Child集合,inverse="true"意爲交出控制權,總體意思就是Parent把控制權交給了Child,也就是說由Child來維護表關係。若是inverse="false"表示不交出控制權,那麼固然由Parent本身來維護表關係了,值得提到的是在Child映射文件中沒有提到由誰來控權,默認的是inverse="false",就是由本身控權,即Child,進一步說當Parent那邊inverse="false"不交控權的時候,其實是兩個類都有責任來維護表關係。說到這裏可能會問到底控權維護表關係是什麼意思呢?下面來看測試類代碼:
Parent p=new Parent();
Child c1=new Child();
p.setPname("xpc5");
c1.setCname("cloud");
c1.setParent(p);   
p.getChilds().add(c1);   
session.save(p);
這樣寫不管誰負責維護數據插入都沒問題,那麼咱們改動一下試試,如今由Child負責維護,c1.setParent(p)是Child類與Parent類關聯的關鍵,那麼把句註釋掉再試試,運行後執行語句是同樣的,可是查看錶記錄你會發現Parent表能正確插入記錄,而Child表中pid字段爲空,這正是由於c1.setParent(p)這句是Child與Parent關聯的關鍵而你又把它註釋了,作爲控權的Child沒有設置與Parent關聯,那麼這兩表之間的聯繫就消失了,即使有p.getChilds().add(c1)這句也沒法實現關聯,由於p不是控權。而當你修改Parent映射文件中的inverse="false"的時候,父親和孩子都負責維護,那麼什麼都不動再試上面的程序,執行後的結果雙方均可正確被插入,由於此時有p.getChilds().add(c1)這句存在就能夠了,由於Parent也有控權。
 
那麼爲何要使用inverse呢,讓雙方都有控權很差嗎?下面來看,當Parent負責維護關係的時候,因爲它包含一個孩子集合,它沒法知道哪些孩子已經指向本身了,因此爲了確保正確性,它要更新全部孩子的pid,讓它指向本身,這樣就引出了效率問題,若存在100個孩子,那麼就無形中多作了100個update,因此顯然,這個父子關係由孩子來維護比較省力.減輕了數據庫的負擔。
相關文章
相關標籤/搜索