Java程式員從笨鳥到菜鳥之(五十五)細談Hibernate(六)Hibernate繼承關係映射

來源:互聯網
上載者:User



           在物件導向的程式領域中,類與類之間是有繼承關係的,例如Java世界中只需要extends關鍵字就可以確定這兩個類的父子關係,但是在關聯式資料庫的世界 中,表與表之間沒有任何關鍵字可以明確指明這兩張表的父子關係,表與表是沒有繼承關係這樣的說法的。為了將程式領域中的繼承關係反映到資料 中,Hibernate為我們提供了3中方案:


第一種方案:一個子類對應一張表。

第二種方案:使用一張表表示所有繼承體系下的類的屬性的並集。

第三種方案:每個子類使用一張表只儲存它特有的屬性,然後與父類所對應的表以一對一主鍵關聯的方式關聯起來。


這三種方案用官方的語言來說就是:

TPS:每個子類一個表(table per subclass) 。

TPH:每棵類繼承樹使用一個表(table per class hierarchy)

TPC:類表繼承。每個具體類一個表(table per concrete class)(有一些限制)

現在我們就根據一個執行個體來看一下這三種方案的各自優缺點,一起來熟悉一下這三種方案。現在假設有People、Student、Teacher三個類,父類為People,Student與Teacher為People的父類,代碼如下:

People類:

public class People{    /*父類所擁有的屬性*/    private Stringid;    private Stringname;    private Stringsex;    private Stringage;    private Timestampbirthday;    /*get和set方法*/}

Student類:

public class Student extends People{    /*學生專屬的屬性*/   private String cardId;//學號   public String getCardId()   { return cardId;}   public void setCardId(String cardId)   {        this.cardId = cardId;   }}

Teacher類:

public class Teacher extends People{   /*Teacher所專屬的屬性*/    privateint salary;//工資   public int getSalary()    {       return salary;    }   public void setSalary(int salary)    {       this.salary = salary;    }}

第一種方案:一個子類對應一張表 (TPS)

該方案是使繼承體系中每一個子類都對應資料庫中的一張表。如下:

                                   

  每一個子類對應的資料庫表都包含了父類的資訊,並且包含了自己專屬的屬性。每個子類對應一張表,而且這個表的資訊是完備的,即包含了所有從父類繼承下來的屬性對應的欄位。這種策略是使用<union-subclass>標籤來定義子類的。

配置People.hbm.xml檔案:

<?xml version="1.0"encoding="utf-8"?><!DOCTYPE hibernate-mapping PUBLIC"-//Hibernate/Hibernate Mapping DTD 3.0//EN""http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping>   <class name="com.bzu.hibernate.pojos.People"abstract="true">       <id name="id" type="string">           <column name="id"></column>           <generator class="uuid"></generator>       </id>        <property name="name" column="name"type="string"></property>       <property name="sex" column="sex"type="string"></property>       <property name="age" column="age"type="string"></property>       <property name="birthday" column="birthday"type="timestamp"></property>        <!--       <union-subclass name="com.bzu.hibernate.pojos.Student"table="student">           <property name="cardId" column="cardId"type="string"></property>       </union-subclass>       <union-subclass name="com.bzu.hibernate.pojos.Teacher"table="teacher">           <property name="salary" column="salary"type="integer"></property>       </union-subclass>       -->   </class>   <union-subclass name="com.bzu.hibernate.pojos.Student"       table="student"extends="com.bzu.hibernate.pojos.People">       <property name="cardId" column="cardId"type="string"></property>   </union-subclass>    <union-subclass name="com.bzu.hibernate.pojos.Teacher"       table="teacher"extends="com.bzu.hibernate.pojos.People">       <property name="salary" column="salary"type="integer"></property>   </union-subclass></hibernate-mapping>

以上配 置是一個子類一張表方案的配置,<union-subclass>標籤是用於指示出該hbm檔案所表示的類的子類,如People類有兩個子 類,就需要兩個<union-subclass>標籤以此類推。<union-subclass>標籤的"name"屬性用於指 定子類的全限定名稱,"table"屬性用於指定該子類對應的表的名稱,"extends"屬性用於指定該子類的父類,注意該屬性與<union-subclass>標籤的位置有關,若<union-subclass>標籤作為<class>標籤的子標籤,則"extends"屬性可以不設定,否則需要明確設定"extends"屬性。<class>標籤中的"abstract"屬性如果值為true則,不會產生表結構。如果值為false則會產生表結構,但是不會插入資料。

 

根據People.hbm.xml產生表結構,可以看到一個子類對應一張表:

drop table if exists student   drop table if exists teacher    create table student (        id varchar(255) not null,        name varchar(255),        sex varchar(255),        age varchar(255),        birthday datetime,        cardId varchar(255),        primary key (id)    )    create table teacher (        id varchar(255) not null,        name varchar(255),        sex varchar(255),        age varchar(255),        birthday datetime,        salary integer,        primary key (id)    )



 

 第二種方案:使用一張表表示所有繼承體系下的類的屬性的並集(TPH)

    這種策略是使用<subclass>標籤來實現的。因為類繼承體系下會有許多個子類,要把多個類的資訊存放在一張表中,必須有某種機制來區 分哪些記錄是屬於哪個類的。Hibernate中的這種機制就是,在表中添加一個欄位,用這個欄位的值來進行區分。在表中添加這個標示列使 用<discriminator>標籤來實現。

該策略的:

                             

將繼承體系中的所有類資訊表示在同一張表中後,只要是這個類沒有的屬性會被自動賦上null。

配置People.hbm.xml:

<?xml version="1.0"encoding="utf-8"?><!DOCTYPE hibernate-mapping PUBLIC"-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping>   <class name="com.bzu.hibernate.pojos.People"table="people">       <id name="id" type="string">           <column name="id"></column>           <generator class="uuid"></generator>       </id>        <discriminator column="peopleType"type="string"></discriminator>        <property name="name" column="name"type="string"></property>       <property name="sex" column="sex"type="string"></property>       <property name="age" column="age"type="string"></property>       <property name="birthday" column="birthday"type="timestamp"></property>        <subclass name="com.bzu.hibernate.pojos.Student"discriminator-value="student">           <property name="cardId" column="cardId"type="string"></property>       </subclass>              <subclass name="com.bzu.hibernate.pojos.Teacher"discriminator-value="teacher">           <property name="salary" column="salary"type="string"></property>       </subclass>   </class></hibernate-mapping>

<discriminator>標籤用於在表中建立一個識別欄位,其"column"屬性指定識別欄位的列名,"type"指定了識別欄位的類型。<subclass>標籤用於指 定該HBM檔案代表類的子類,有多少子類就有多少個該標籤,其"name"屬性指定子類的名稱,"discriminator-value"屬性指定該子類的資料的識別欄位的值是什麼,其"extends"屬性與<union-subclass>的"extends"屬性用法一致。

下面來看一下根據People.hbm.xml產生表結構,可以看到一張表將繼承體系下的所有資訊都包含了,其中"peopleType"為識別欄位:

drop table if exists people    createtable people (        idvarchar(255) not null,       peopleType varchar(255) not null,        namevarchar(255),        sexvarchar(255),        agevarchar(255),       birthday datetime,        cardIdvarchar(255),        salaryvarchar(255),       primary key (id)    )

第三種方案:每個子類使用一張表只儲存它特有的屬性,然後與父類所對應的表以一對一主鍵關聯的方式關聯起來。(TPC)

    這種策略是使用<joined-subclass>標籤來定義子類的。父類、子類都對應一張資料庫表。在父類對應的資料庫表中,它儲存了所 有記錄的公用資訊,實際上該父類對應的表會包含所有的記錄,包括父類和子類的記錄;在子類對應的資料庫表中,這個表只定義了子類中所特有的屬性對應的字 段。子類對應的資料表與父類對應的資料表,通過一對一主鍵關聯的方式關聯起來。

這種策略的:

                              

 

    people表中儲存了子類的所有記錄,但只記錄了他們共有的資訊,而他們專屬的資訊儲存在他們對應的表中,一條記錄要獲得其專屬的資訊,要通過people記錄的主鍵到其對應的子表中尋找主索引值一樣的記錄然後取出它專屬的資訊。

配置People.hbm.xml:

<?xml version="1.0"encoding="utf-8"?><!DOCTYPE hibernate-mapping PUBLIC"-//Hibernate/Hibernate Mapping DTD 3.0//EN""http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping>   <class name="com.suxiaolei.hibernate.pojos.People"table="people">       <id name="id" type="string">           <column name="id"></column>           <generator class="uuid"></generator>       </id>        <property name="name" column="name"type="string"></property>       <property name="sex" column="sex"type="string"></property>       <property name="age" column="age"type="string"></property>       <property name="birthday" column="birthday"type="timestamp"></property>              <joined-subclassname="com.suxiaolei.hibernate.pojos.Student"table="student">           <key column="id"></key>           <property name="cardId" column="cardId"type="string"></property>       </joined-subclass>              <joined-subclassname="com.suxiaolei.hibernate.pojos.Teacher"table="teacher">           <key column="id"></key>           <property name="salary" column="salary"type="integer"></property>       </joined-subclass>   </class></hibernate-mapping>

<joined-subclass>標籤需要包含一個key標籤,這個標籤指定了子類和父類之間是通過哪個欄位來關聯的。

 

根據People.hbm.xml產生表結構, 可以看到,父類對應的表儲存公有資訊,子類對應的表儲存專屬資訊,子類和父類對應的表使用一對一主鍵關聯的方式關聯起來

drop table if exists people   drop table if exists student    droptable if exists teacher   create table people (        id varchar(255) not null,        name varchar(255),        sex varchar(255),        age varchar(255),        birthday datetime,        primary key (id)   )   create table student (        id varchar(255) not null,        cardId varchar(255),        primary key (id)   )   create table teacher (        id varchar(255) not null,        salary integer,        primary key (id)   )    alter table student        add index FK8FFE823BF9D436B1 (id),        add constraint FK8FFE823BF9D436B1        foreign key (id)        references people (id)    alter table teacher        add index FKAA31CBE2F9D436B1 (id),        add constraint FKAA31CBE2F9D436B1        foreign key (id)        references people (id)


三種映射方式的比較和選擇---三種方式的優缺點

為了方便說明為三種方式按順序標號為[1][2][3]。

【1】:優點:資料結構清晰

  缺點:兩個子表的主鍵不能重複,不能使用資料庫的自增方式產生主鍵。

【2】:優點:查詢效率高,符合資料庫設計粗粒度(推薦)

缺點:存在冗餘欄位,有些欄位是子類不具有的屬性。

【3】:優點:資料結構清晰,沒有冗餘

缺點:類的繼承層次比較多的話,造成產生的表也比較多,增刪改查效率低下

總結:

1. 通過總結這三種方式的優缺點發現,使用繼承樹產生一張表的方式似乎更符合資料庫粗粒度設計的原則。當然資料量非常大的話也可以考慮每個類產生一張表的成方式

 2.程式的物件模型沒有發生變化,變化的是關聯式模式。

這就是hibernate的好處,想改變關聯式模式,只需要改變對應檔即可。


聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.