關聯關係:採用“一對多或一對一”的映射即可;
聚集關係:採用“集和映射”,即映射Set,Bag,List,Map
繼承關係:
因爲關係數據庫的表之間不存在繼承關係,所以Hibernate提供了以下三種對繼承關係映射的方法,即在繼承關係樹中:
(1)、每個具體類(非抽象類)對應一個表:此方式中關係數據模型完全不支持對象的繼承關係。
(2)、基類(根類)對應一個表:此方式中對關係數據模型進行非常規設計,在數據庫表中加入額外的區分子類的字段,從而使關係數據模型可以支持繼承關係。
(3)、每個類對應一個表:此方式中在關係數據模型中用外鍵關係來表示繼承關係。
這三種映射方式都各自有利有弊,需要根據具體情況來選擇使用。分別說明一下:
爲了說明方便,引用一個比較經典的簡單例子
Employee類,它爲抽象類,有兩個直接子類
HourlyEmployee類,Employee的子類,繼承父類的name屬性
SalarilyEmployee類,Employee的子類,繼承父類的name屬性
Company類,它有一個employees集和屬性
在此,先這樣說明一下啊,日後有時間放個類圖上來就一目瞭然了。
繼承關係中每個具體類對應一個表
這是最簡單的映射方式。基類Employee沒用映射,只需要爲兩個子類提供映射關係即可。看一下映射文件:
<hibernate-mapping package="com.wjb.mypack">
<!--Company的映射-->
<class name="Company" table="COMPANIES">
<id name="id" type="long" column="ID">
<generator class="native"/>
</id>
<property name="name" type="string" column="NAME"/>
</class>
<!--HourlyEmployee的映射-->
<class name="HouredEmployee" table="HOURLY_EMPLOYEES">
<id name="id" type="long" column="ID">
<generator class="native"/>
</id>
<!--映射從父類繼承的name屬性-->
<property name="name" type="string" column="NAME"/>
<property name="rate" type="double" column="RATE"/>
<many-to-one name="company"
column="COMPANY_ID" class="Company"/>
</class>
<!--SalarilyEmployee的映射-->
<class name="SalarilyEmployee" table="SALARILY_EMPLOYEES">
<id name="id" type="long" column="ID">
<generator class="native"/>
</id>
<!--映射從父類繼承的name屬性-->
<property name="name" type="string" column="NAME"/>
<property name="salary" type="double" column="SALARY"/>
<many-to-one name="company"
column="COMPANY_ID" class="Company"/>
</class>
<!--注:Employee類沒有映射,在DB中不存在對應的表-->
</hibernate-mapping>
可以看出,在這種映射方式中,每個子類除了映射自己的屬性外,還需要映射從父類繼承來下的屬性,這是該映射方式的一個特點。
基類(根類)對應一個表
這種映射方式只需爲基類Employee創建一個表即可。在表中不僅提供與Employee所有屬性的字段,還要提供與所有子類屬性對應的字段,此外還需要一個字段用於區分子類的具體類型。此時的映射文件爲:
<hibernate-mapping package="com.wjb.mypack">
<!--Company的映射-->
<class name="Company" table="COMPANIES">
<id name="id" type="long" column="ID">
<generator class="native"/>
</id>
<property name="name" type="string" column="NAME"/>
</class>
<!--Employee以及子類的映射-->
<class name="Employee" table="EMPLOYEES">
<id name="id" type="long" column="ID">
<generator class="native"/>
</id>
<!--用於區分子類類型的字段-->
<discriminator type="string" column="EMPLOYEE_TYPE">
<property name="name" type="string" column="NAME"/>
<many-to-one name="company"
column="COMPANY_ID" class="Company"/>
<!--子類HourlyEmployee的映射-->
<subclass name="HourlyEmployee"
discriminator-value="HE">
<property name="rate" column="RATE" type="double"/>
</subclass>
<!--子類SalarilyEmployee的映射-->
<subclass name="SalarilyEmployee"
discriminator-value="SE">
<property name="salary" column="SALARY" type="double"/>
</subclass>
</class>
<!--注:HourlyEmployee類沒有單獨的映射,在DB中不存在對應的表-->
<!--注:SalarilyEmployee類沒有單獨的映射,在DB中不存在對應的表-->
</hibernate-mapping>
可以看出,來個子類沒有單獨的映射,在DB中沒有對應的表存在。而只有一個記錄所有自身屬性和子類所有屬性的表,在子類爲HourlyEmployee的時候,SALARY字段將爲NULL,同樣子類爲SalarilyEmployee的時候,RATE字段將爲NULL。那麼,如果業務邏輯要求SalariedEmployee對象的rate屬性不允許爲null,顯然無法在EMPLOYEES表中爲SALARY字段定義not null約束,可見這種映射方式無法保證關係數據模型的數據完整性。
每個類對應一個表
這種方式爲基類和子類分別創建表,即EMPLOYEES、HE和SE三個表。EMPLOYEES只包含Employee自己屬性的字段,每個子類的表也同樣只包含自己類屬性的字段。此外,HE表和SE表都以EMPLOYEE_ID字段作爲主鍵,該字段還同時作爲外鍵參照EMPLOYEES表。
HourlyEmployee和SalarilyEmployee沒有獨立的映射配置,但是在DB中有相應的表存在,這是其一個特點。
<hibernate-mapping package="com.wjb.mypack">
<!--Company的映射-->
<class name="Company" table="COMPANIES">
<id name="id" type="long" column="ID">
<generator class="native"/>
</id>
<property name="name" type="string" column="NAME"/>
</class>
<!--Employee以及子類的映射-->
<class name="Employee" table="EMPLOYEES">
<id name="id" type="long" column="ID">
<generator class="native"/>
</id>
<property name="name" type="string" column="NAME"/>
<many-to-one name="company"
column="COMPANY_ID" class="Company"/>
<!--子類HourlyEmployee的映射-->
<joined-subclass name="HourlyEmployee" table="HE">
<key column="EMPLOYEE_ID"/>
<property name="rate" column="RATE" type="double"/>
</subclass>
<!--子類SalarilyEmployee的映射-->
<joined-subclass name="SalarilyEmployee" table="SE">
<key column="EMPLOYEE_ID"/>
<property name="salary" column="SALARY" type="double"/>
</subclass>
</class>
<!--注:HourlyEmployee類沒有單獨的映射,但在DB中有對應的表-->
<!--注:SalarilyEmployee類沒有單獨的映射,但在DB中有對應的表-->
</hibernate-mapping>
可見,兩個<joined-subclass>元素用於映射兩個子類,<joined-subclass>元素的<key>子元素指定HE表和SE表中既作爲主鍵又作爲外鍵的EMPLOYEE_ID字段。
三種映射方式的比較和選擇
爲了方便說明爲三種方式按順序標號爲[1][2][3]。
1、複雜度:
[1]包含重複字段;
[2]簡單;
[3]表較多且之間有外鍵約束;
2、查詢性能:
[1]若查詢父類需查所有子類表;
[2]效率高;
[3]需要表內連接或左外連接;
3、可維護性:
[1]若父類屬性變化需要修改所有子類對應的表;
[2]只需修改一個表;
[3]若某個類屬性變化只修改這個類對應的表;
綜上,選擇時,可以參考以下原則:
1、子類屬性不是非常多時,優先考慮[2],因爲其性能最佳。
2、子類屬性非常多,且對性能要求不是很嚴格時,優先考慮[3]