1. 程式人生 > >Hibernate旅程(五)Hibernate對映--基本類對映和物件關係對映

Hibernate旅程(五)Hibernate對映--基本類對映和物件關係對映

回想一些我們在沒有學習ssh的時候,我們建立資料庫的表時,首先是資料庫建模E-R圖,然後再通過實體模型來建立關係模型,再建立相應的表。實體間存在三種關係,一對一,一對多(或者說多對一),多對多。而如今我們要根據類來對映相應的表,那隻能是通過類與類之間的關係加上對映檔案來對映資料庫的表。我們學習UML建模,類與類之間存在五種關係,繼承,實現,關聯,依賴,聚合/組合,在hibernate中實體類之間的關係也是如此,對於不同的關係對應的程式碼實現我們已經很熟悉了,所以對於實體類是複習的知識。

Hibernate的本質就是物件關係對映(ObjectRelational Mapping),ORM實現了將物件資料儲存到資料庫中,以前我們對關係表進行操作,執行增刪改查等任務,現在我們不再對關係表進行操作,而是直接對物件操作。

hibernate中的ORM對映檔案通常以.hbm.xml作為字尾。使用這個對映檔案不僅易讀,而且可以手工修改,也可以通過一些工具來生成對映文件。下面將對hibernate中的對映進行介紹。

Hibernate對映分類,如下圖所示。

 

1 基本類對映

根據實體類建立相應的表,這種簡單的關係為hibernate基本對映。

User1實體類程式碼如下:

//user實體。
public classUser1 {
   //使用者編號。
   private String id;
  
   //名字。
   private String name;
  
   //密碼。
   private String password;
  
   //建立日期。
   private Date createTime;
  
   //失效時間。
    private Date expireTime;
 
   public String getId() {
      return id;
   }
 
// publicvoid setId(String id) {
//    this.id= id;
// }
 
   public String getName() {
      return name;
   }
 
   public void setName(String name) {
      this.name = name;
   }
 
   public String getPassword() {
      return password;
   }
 
   public void setPassword(Stringpassword) {
      this.password = password;
   }
 
   public Date getCreateTime() {
      return createTime;
   }
 
   public void setCreateTime(DatecreateTime) {
      this.createTime = createTime;
   }
 
   public Date getExpireTime() {
      return expireTime;
   }
 
   public void setExpireTime(DateexpireTime) {
      this.expireTime = expireTime;
   }
 }

User1.hbm.xml對映檔案如下所示:

<hibernate-mapping package="com.bjpowernode.hibernate">
 
   <class name="User1"  table="t_user1">
      <id name="id"column="user_id" length="32"access="field">
         <generator class="uuid" />
      </id>
      <!-- 設定主鍵不能重複和不能為空的屬性. -->
      <property name="name" length="30"unique="true" not-null="true"/>
      <property name="password"/>
      <property name="createTime" type="date" column="create_time"/>
      <property name="expireTime"/>
   </class>
</hibernate-mapping>

通過User1.hbm.xml對映檔案將User1物件轉換為關係資料庫中的表t_user1

轉換出的結果如下所示:

 

2 物件關係對映

    2.1 多對一關聯對映(單向)

例如使用者和組的關係就是多對一的關係,多個使用者對應一個組。

 

將實體對映成表,將對應的實體對映成表。對應的屬性對映成表字段。

多對一關聯對映是在多的一端來維護關聯字段,在我們這個例子中也就是在使用者一端來維護關係欄位。

User.hbm.xml檔案。

<hibernate-mapping package="org.hibernate.auction">
 
   <class name="com.bjpowernode.hibernate.User" table="t_user" >
      <id name="id">
         <generator class="native" />
      </id>
      <property name="name"/>
      <many-to-one name="group"  column="groupid"cascade="save-update"></many-to-one>
   </class>
</hibernate-mapping>

Group.hbm.xml檔案。

<hibernate-mapping package="org.hibernate.auction">
 
   <class name="com.bjpowernode.hibernate.Group"  table="t_group">
      <id name="id">
         <generator class="native" />
      </id>
      <property name="name"/>
   </class>
</hibernate-mapping>

在這裡我們看的程式碼就看*.hbm.mlx程式碼,因為對於類之間的關聯,在實現時,一個類作為另一個類的私有成員,這一點在學UML建模的時候我們都懂了,在這裡主要看的是ORMM,也就是*.hbm.xml檔案。

    2.2 一對一關聯對映

一對一關聯對映在實際生活中是比較常見的,如人與家庭住址的關係,通過人這個物件可以找到他家庭住址相關的內容。

    2.2.1 一對一對映(單向主鍵關聯

 

單向一對一主鍵關聯,靠的是它們的主鍵相等,從Person中能看到IdCard,也就是把t_idCard中的主鍵拿過來當做t_Pseron的主鍵。

Xml檔案中:

<class name="com.bjpowernode.hibernate.Person"table="t_person" >
      <id name="id">
      <!-- 採用foreign生成策略,foreign會取得關聯物件的標識 -->
         <generator class="foreign" >
         <!--property指的是關聯物件。  -->
            <param name="property">idCard</param>
         </generator>
      </id>
      <property name="name"/>
      <!-- 一對一關聯對映,主鍵關聯.  -->
      <!--
      one-to-one標籤指示hibernate如何載入其關聯物件,預設根據主鍵載入.
      也就是拿到關係欄位值,根據對端的主鍵來載入關聯物件.
      constrained="true",表示當前主鍵(Person的主鍵)還是一個外來鍵 .
      參照了對端的主鍵(IdCard的主鍵),也就是會生成外來鍵約束語句.
      -->
      <one-to-one name="idCard" constrained="true"/>
   </class>

<hibernate-mapping package="org.hibernate.auction">
 
   <class name="com.bjpowernode.hibernate.IdCard" table="t_idCard" >
      <id name="id">
         <generator class="native" />
      </id>
      <property name="cardNo"/>
   </class>
</hibernate-mapping>

一對一的關係是通過one-to-one元素定義的。

   2.2.2 一對一對映(雙向主鍵關聯

一對一雙向主鍵關聯與一對一單向主鍵關聯的區別就是,一對一單向主鍵關聯,在person端能看到idCard,而idCard不能看到Person端。而雙向關聯就是在idCard端也能看到person也就是不但在Person.hbm.xml中加上<one-to-one>標籤,同時在IdCard.hbm.xml檔案中加上<one-to-one>標籤。程式碼如下所示。

<hibernate-mapping package="org.hibernate.auction">
 
   <class name="com.bjpowernode.hibernate.IdCard" table="t_idCard" >
      <id name="id">
         <generator class="native" />
      </id>
      <property name="cardNo"/>
      <one-to-one name="person"/>
   </class>
</hibernate-mapping>


    2.2.3 一對一對映(單向唯一外來鍵關聯

一對一單向唯一外來鍵關聯,也就是多對一關聯的特例,把多的一端限制為一,就是一對一唯一外來鍵關聯。同多對一一樣,在一端加入另一端的並採用<many-to-one>標籤,通過unique="true",這樣來限制了多的一端為一

先上程式碼。

IdCard.hbm.xml

<hibernate-mapping package="org.hibernate.auction">
 
   <class name="com.bjpowernode.hibernate.IdCard" table="t_idCard" >
      <id name="id">
         <generator class="native" />
      </id>
      <property name="cardNo"/>
   </class>
</hibernate-mapping>

Person.hbm.xml

<hibernate-mapping package="org.hibernate.auction">
 
   <class name="com.bjpowernode.hibernate.Person" table="t_person" >
      <id name="id">
      <!-- 採用foreign生成策略,foreign會取得關聯物件的標識 -->
         <generator class="native" />
     
      </id>
      <property name="name"/>
      <many-to-one name="idCard" unique="true"></many-to-one>
  
   </class>
</hibernate-mapping>

圖如下所示:

 

t_pserson端加上一個外來鍵欄位idCard,限制idCard的唯一性就是一對一唯一外來鍵關聯。

    2.2.4 一對一對映(雙向唯一外來鍵關聯

一對一唯一外來鍵單向關聯我們已經瞭解了,雙向反過來就是在沒有的一端加上就可以了。

我們的IdCard.hbm.xml中採用<one-to-one>標籤。

<hibernate-mapping package="org.hibernate.auction">
 
   <class name="com.bjpowernode.hibernate.IdCard" table="t_idCard" >
      <id name="id">
         <generator class="native" />
      </id>
      <property name="cardNo"/>
      <one-to-one name="person" property-ref="idCard"></one-to-one>
   </class>
</hibernate-mapping>

person.hbm.xml同一對一唯一外來鍵單向關聯一樣。

  <class name="com.bjpowernode.hibernate.Person" table="t_person" >
      <id name="id">
      <!-- 採用foreign生成策略,foreign會取得關聯物件的標識 -->
         <generator class="native" />
     
      </id>
      <property name="name"/>
      <many-to-one name="idCard" unique="true"></many-to-one>
  
   </class>


從上述中可以總結出,對於一對一關聯對映,主鍵關聯和唯一外來鍵關聯單向和雙向產生出的表結構是一樣的,不同的是在載入的時候不同。也就是一對一雙向關聯和一對一單向關聯的相比,只是改變了一對一關聯對映的載入,而沒有改變儲存。

    2.3 一對多關聯對映

    2.3.1 一對多關聯對映(單向)

上面我們介紹了多對一,我們反過來看一對多不就是多對一嗎?那還用再進行不同的對映嗎?有什麼差別嗎?一對多和多對一對映原理是一致的,儲存是相同的,也就是生成的資料庫的表是一樣的,他們之間不同的是維護的關係不同。

他們之間不同點是維護的關係不同

*多對一維護的關係是:多指向一的關係,有了此關係,載入多的時候可以將一載入上來。

*一對多維護的關係是:一指向多的關係,有了此關係,在載入一的時候可以將多載入上來。

 

程式碼如下所示。

Class.hbm.xml

<class name="com.bjpowernode.hibernate.Classes" table="t_Classes" >
      <id name="id">
         <generator class="native" />
      </id>
      <property name="name"/>
      <set name="students">
      <!-- 
         <keycolumn="classesid" not-null="true"/>
      -->
         <key column="classesid" />
         <one-to-many class="com.bjpowernode.hibernate.Student"/>
      </set>
   </class>

Students.hbm.xml

 <class name="com.bjpowernode.hibernate.Student" table="t_student" >
      <id name="id">
         <generator class="native" />
      </id>
      <property name="name"/>
   </class>

從班級能看到學生,是班級來維護關係,不是學生來維護關係,學生不知道自己是哪個班,所以在儲存學生的時候,班級的程式碼不知道。為了更新學生是哪個班級的要發出很多update語句來告訴學生是哪個班級的。當我們設定classesid not-null=“true”時,則將無法儲存資料,解決辦法我們改為雙向關聯對映。

    2.3.2 一對多關聯對映(雙向)

為了解決一對多單向可能存在的問題,我們採用雙向一對多,每一方都能維護對方。


一對多雙向關聯對映方式:

   *在一的一端的集合上採用<key>標籤,在多的一端加入一個外來鍵。

*在多的一端採用<many-to-one>的標籤

~注意<key>標籤和<many-to-one>標籤加入欄位保持一致,否則會產生資料混亂。

程式碼如下所示。

<class name="com.bjpowernode.hibernate.Classes" table="t_Classes" >
      <id name="id">
         <generator class="native" />
      </id>
      <property name="name"/>
      <set name="students" inverse="true">
      <!-- 
         <keycolumn="classesid" not-null="true"/>
      -->
         <key column="classesid" />
         <one-to-many class="com.bjpowernode.hibernate.Student"/>
      </set>
   </class>
<class name="com.bjpowernode.hibernate.Student" table="t_student" >
      <id name="id">
         <generator class="native" />
      </id>
      <property name="name"/>
       <many-to-one name="classes"column="classesid"/>
   </class>


注意:Inverse屬性

1、Inverse中文意思為相反的,反轉。在hibernateinverse可以用在一對多和多對多雙向關聯上,inverse預設是false,為false的時候表示本端可以維護關係,如果inversetrue,則本端不能維護關係,會交給另一端維護關係,本端失效,所以在一對多關聯對映我們通常在多的一端維護關係,讓一的一端失效。

2、Inverse是控制方向上的反轉,隻影響儲存。

比較一對多單向和雙向對映,從儲存結構上看沒有什麼區別,但是從配置檔案上看,一對多雙向比一對多單向,一對多雙向關聯的配置檔案中在多的一端的配置檔案上存在<many-to-one>相關配置,即保證多對一的對映。

    2.4 多對多關聯對映

    2.4.1 多對多關聯對映(單向)

多對多物件關係對映,需要加入一張新表完成基本對映。如下圖所示。


程式碼。

Role.hbm.xml

<class name="com.bjpowernode.hibernate.Role"  table="t_role">
      <id name="id">
         <generator class="native" />
      </id>
      <property name="name"/>
     
   </class>

User.hbm.xml

<class name="com.bjpowernode.hibernate.User" table="t_user" >
      <id name="id">
         <generator class="native" />
      </id>
      <property name="name"/>
        
      <set name="roles" table="t_user_role">
         <key column="user_id"/>
         <many-to-many class="com.bjpowernode.hibernate.Role"  column="role_id"/>
      </set>
   </class>

    2.4.2 多對多關聯對映(雙向)

雙向多對多物件關係對映,是兩端都能將對方載入上來,雙向都需要加上標籤對映。

要注意:

*生成中間表名必須一樣

*生成中間表字段必須一樣


程式碼如下所示。

Role.hbm.xml

 <class name="com.bjpowernode.hibernate.Role"  table="t_role">
      <id name="id">
         <generator class="native" />
      </id>
      <property name="name"/>
     
      <set name="users"  table="t_user_role">
         <key column="role_id"/>
         <many-to-many class="com.bjpowernode.hibernate.User" column="user_id"/>
      </set>
   </class>

.User.hbm.xml

<class name="com.bjpowernode.hibernate.User"table="t_user" >
      <id name="id">
         <generator class="native" />
      </id>
      <property name="name"/>
        
      <set name="roles" table="t_user_role">
         <key column="user_id"/>
         <many-to-many class="com.bjpowernode.hibernate.Role"  column="role_id"/>
      </set>
   </class>


區別單向多對多和雙向多對多儲存結構沒有任何的區別,但他們的對映檔案是有區別的,載入過程是不同的。

        3  關係對映總結

綜上所述,可以看出,同一類對映,無論是單向還是雙向,他們的儲存結構相同的,之所以對映檔案不同,是因為載入時不同(在增刪改時)。

無論是多對一、一對多、一對一還是多對一,ABA就是主動方,A主動想要了解B的情況,這樣把B設定到A。而雙向,也就是ABA想了解B的資訊,而B也想了解A的資訊,那就要同時把A設定到B端了。