關聯關系映射通常情況是最難配置正確的。在這個部分中,我們從單向關系映射開始,然后考慮雙向關系映射,由淺至深講述一遍典型的案例。在所有的例子中,我們都使用 Person和Address。
我們根據映射關系是否涉及連接表以及多樣性來劃分關聯類型。
在傳統的數據建模中,允許為Null值的外鍵被認為是一種不好的實踐,因此我們所有的例子中都使用不允許為Null的外鍵。這并不是Hibernate的要求,即使你刪除掉不允許為Null的約束,Hibernate映射一樣可以工作的很好。
單向many-to-one關聯是最常見的單向關聯關系。
<class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <many-to-one name="address" column="addressId" not-null="true"/> </class> <class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> </class>
create table Person ( personId bigint not null primary key, addressId bigint not null ) create table Address ( addressId bigint not null primary key )
基于外鍵關聯的單向一對一關聯和單向多對一關聯幾乎是一樣的。唯一的不同就是單向一對一關聯中的外鍵字段具有唯一性約束。
<class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <many-to-one name="address" column="addressId" unique="true" not-null="true"/> </class> <class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> </class>
create table Person ( personId bigint not null primary key, addressId bigint not null unique ) create table Address ( addressId bigint not null primary key )
基于主鍵關聯的單向一對一關聯通常使用一個特定的id生成器。(請注意,在這個例子中我們掉換了關聯的方向。)
<class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> </class> <class name="Address"> <id name="id" column="personId"> <generator class="foreign"> <param name="property">person</param> </generator> </id> <one-to-one name="person" constrained="true"/> </class>
create table Person ( personId bigint not null primary key ) create table Address ( personId bigint not null primary key )
基于外鍵關聯的單向一對多關聯是一種很少見的情況,并不推薦使用。
<class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <set name="addresses"> <key column="personId" not-null="true"/> <one-to-many class="Address"/> </set> </class> <class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> </class>
create table Person ( personId bigint not null primary key ) create table Address ( addressId bigint not null primary key, personId bigint not null )
我們認為對于這種關聯關系最好使用連接表。
基于連接表的單向一對多關聯 應該優先被采用。請注意,通過指定unique="true",我們可以把多樣性從多對多改變為一對多。
<class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <set name="addresses" table="PersonAddress"> <key column="personId"/> <many-to-many column="addressId" unique="true" class="Address"/> </set> </class> <class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> </class>
create table Person ( personId bigint not null primary key ) create table PersonAddress ( personId not null, addressId bigint not null primary key ) create table Address ( addressId bigint not null primary key )
基于連接表的單向多對一關聯在關聯關系可選的情況下應用也很普遍。
<class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <join table="PersonAddress" optional="true"> <key column="personId" unique="true"/> <many-to-one name="address" column="addressId" not-null="true"/> </join> </class> <class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> </class>
create table Person ( personId bigint not null primary key ) create table PersonAddress ( personId bigint not null primary key, addressId bigint not null ) create table Address ( addressId bigint not null primary key )
基于連接表的單向一對一關聯非常少見,但也是可行的。
<class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <join table="PersonAddress" optional="true"> <key column="personId" unique="true"/> <many-to-one name="address" column="addressId" not-null="true" unique="true"/> </join> </class> <class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> </class>
create table Person ( personId bigint not null primary key ) create table PersonAddress ( personId bigint not null primary key, addressId bigint not null unique ) create table Address ( addressId bigint not null primary key )
最后,還有 單向多對多關聯.
<class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <set name="addresses" table="PersonAddress"> <key column="personId"/> <many-to-many column="addressId" class="Address"/> </set> </class> <class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> </class>
create table Person ( personId bigint not null primary key ) create table PersonAddress ( personId bigint not null, addressId bigint not null, primary key (personId, addressId) ) create table Address ( addressId bigint not null primary key )
雙向多對一關聯 是最常見的關聯關系。(這也是標準的父/子關聯關系。)
<class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <many-to-one name="address" column="addressId" not-null="true"/> </class> <class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> <set name="people" inverse="true"> <key column="addressId"/> <one-to-many class="Person"/> </set> </class>
create table Person ( personId bigint not null primary key, addressId bigint not null ) create table Address ( addressId bigint not null primary key )
基于外鍵關聯的雙向一對一關聯也很常見。
<class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <many-to-one name="address" column="addressId" unique="true" not-null="true"/> </class> <class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> <one-to-one name="person" property-ref="address"/> </class>
create table Person ( personId bigint not null primary key, addressId bigint not null unique ) create table Address ( addressId bigint not null primary key )
基于主鍵關聯的一對一關聯需要使用特定的id生成器。
<class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <one-to-one name="address"/> </class> <class name="Address"> <id name="id" column="personId"> <generator class="foreign"> <param name="property">person</param> </generator> </id> <one-to-one name="person" constrained="true"/> </class>
create table Person ( personId bigint not null primary key ) create table Address ( personId bigint not null primary key )
基于連接表的雙向一對多關聯。注意inverse="true"可以出現在關聯的任意一端,即collection端或者join端。
<class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <set name="addresses" table="PersonAddress"> <key column="personId"/> <many-to-many column="addressId" unique="true" class="Address"/> </set> </class> <class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> <join table="PersonAddress" inverse="true" optional="true"> <key column="addressId"/> <many-to-one name="person" column="personId" not-null="true"/> </join> </class>
create table Person ( personId bigint not null primary key ) create table PersonAddress ( personId bigint not null, addressId bigint not null primary key ) create table Address ( addressId bigint not null primary key )
基于連接表的雙向一對一關聯極為罕見,但也是可行的。
<class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <join table="PersonAddress" optional="true"> <key column="personId" unique="true"/> <many-to-one name="address" column="addressId" not-null="true" unique="true"/> </join> </class> <class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> <join table="PersonAddress" optional="true" inverse="true"> <key column="addressId" unique="true"/> <many-to-one name="address" column="personId" not-null="true" unique="true"/> </join> </class>
create table Person ( personId bigint not null primary key ) create table PersonAddress ( personId bigint not null primary key, addressId bigint not null unique ) create table Address ( addressId bigint not null primary key )
最后,還有 雙向多對多關聯.
<class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <set name="addresses"> <key column="personId"/> <many-to-many column="addressId" class="Address"/> </set> </class> <class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> <set name="people" inverse="true"> <key column="addressId"/> <many-to-many column="personId" class="Person"/> </set> </class>
create table Person ( personId bigint not null primary key ) create table PersonAddress ( personId bigint not null, addressId bigint not null, primary key (personId, addressId) ) create table Address ( addressId bigint not null primary key )
Component這個概念在Hibernate中幾處不同的地方為了不同的目的被重復使用.
Component是一個被包含的對象,它作為值類型被持久化,而非一個被引用的實體。“component(組件)”這一術語指的是面向對象的合成概念(而并不是系統構架層次上的組件的概念)舉個例子, 你可以對人(Person)如以下這樣來建模:
public class Person { private java.util.Date birthday; private Name name; private String key; public String getKey() { return key; } private void setKey(String key) { this.key=key; } public java.util.Date getBirthday() { return birthday; } public void setBirthday(java.util.Date birthday) { this.birthday = birthday; } public Name getName() { return name; } public void setName(Name name) { this.name = name; } ...... ...... }
public class Name { char initial; String first; String last; public String getFirst() { return first; } void setFirst(String first) { this.first = first; } public String getLast() { return last; } void setLast(String last) { this.last = last; } public char getInitial() { return initial; } void setInitial(char initial) { this.initial = initial; } }
現在,姓名(Name)是作為人(Person)的一個組成部分。需要注意的是:需要對姓名 的持久化屬性定義getter和setter方法,但是不需要實現任何的接口或申明標識符字段。
以下是這個例子的Hibernate映射文件:
<class name="eg.Person" table="person"> <id name="Key" column="pid" type="string"> <generator class="uuid.hex"/> </id> <property name="birthday" type="date"/> <component name="Name" class="eg.Name"> <!-- class attribute optional --> <property name="initial"/> <property name="first"/> <property name="last"/> </component> </class>
人員(Person)表中將包括pid, birthday, initial, first和 last等字段。
就像所有的值類型一樣, Component不支持共享引用。 換句話說,兩個人可能重名,但是兩個person對象應該包含兩個獨立的name對象,只不過是具有“同樣”的值。 Component的值為空從語義學上來講是專有的(ad hoc)。 每當 重新加載一個包含組件的對象,如果component的所有字段為空,那么將Hibernate將假定整個component為 空。對于絕大多數目的,這樣假定是沒有問題的。
Component的屬性可以是Hibernate類型(包括Collections, many-to-one 關聯, 以及其它Component 等等)。嵌套Component不應該作為特殊的應用被考慮(Nested components should not be considered an exotic usage)。 Hibernate趨向于支持設計細致(fine-grained)的對象模型。
<component> 元素還允許有 <parent>子元素 ,用來表明component類中的一個屬性返回包含它的實體的引用。
<class name="eg.Person" table="person"> <id name="Key" column="pid" type="string"> <generator class="uuid.hex"/> </id> <property name="birthday" type="date"/> <component name="Name" class="eg.Name" unique="true">> <parent name="namedPerson"/> <!-- reference back to the Person --> <property name="initial"/> <property name="first"/> <property name="last"/> </component> </class>
Hibernate支持component的集合(例如: 一個元素是“姓名”這種類型的數組)。 你可以使用<composite-element>標簽替代<element>標簽來定義你的component集合。
<set name="someNames" table="some_names" lazy="true"> <key column="id"/> <composite-element class="eg.Name"> <!-- class attribute required --> <property name="initial"/> <property name="first"/> <property name="last"/> </composite-element> </set>
注意,如果你決定定義一個元素是聯合元素的Set,正確地實現equals()和hashCode()是非常重要的。
組合元素可以包含component但是不能包含集合。如果你的組合元素自身包含component, 必須使用<nested-composite-element>標簽。這是一個相當特殊的案例 - 組合元素的集合自身可以包含component。 這個時候你就應該考慮一下使用one-to-many關聯是否會更恰當。 嘗試對這個組合元素重新建模為一個實體-但是需要注意的是,雖然Java模型和重新建模前 是一樣的,關系模型和持久性語義上仍然存在輕微的區別。
請注意如果你使用<set>標簽,一個組合元素的映射不支持可能為空的屬性. 當刪除對象時, Hibernate必須使用每一個字段的來確定一條記錄(在組合元素表中,沒有單個的關鍵字段), 如果有為null的字段,這樣做就不可能了。你必須作出一個選擇,要么在組合元素中使用不能為空的屬性, 要么選擇使用<list>, <map>,<bag> 或者 <idbag>而不是 <set>。
組合元素有個特別的案例,是組合元素可以包含一個<many-to-one> 元素。類似這樣的映射允許你映射一個many-to-mang關聯表作為組合元素額外的字段。(A mapping like this allows you to map extra columns of a many-to-many association table to the composite element class.) 接下來的的例子是從Order到Item的一個多對多的關聯關系,而 purchaseDate, price 和 quantity 是Item的關聯屬性。
<class name="eg.Order" .... > .... <set name="purchasedItems" table="purchase_items" lazy="true"> <key column="order_id"> <composite-element class="eg.Purchase"> <property name="purchaseDate"/> <property name="price"/> <property name="quantity"/> <many-to-one name="item" class="eg.Item"/> <!-- class attribute is optional --> </composite-element> </set> </class>
當然,在另一方面,無法存在指向purchase的關聯,因此不能實現雙向關聯查詢。記住組建是值類型,并且不允許共享關聯。單個Purchase 可以放在包含Order的集合中,但它不能同時被Item所關聯。
即使三重或多重管理都是可能的:
<class name="eg.Order" .... > .... <set name="purchasedItems" table="purchase_items" lazy="true"> <key column="order_id"> <composite-element class="eg.OrderLine"> <many-to-one name="purchaseDetails" class="eg.Purchase"/> <many-to-one name="item" class="eg.Item"/> </composite-element> </set> </class>
在查詢中,組合元素使用的語法是和關聯到其他實體的語法一樣的。
<composite-map-key>元素允許你映射一個Component類作為Map的key, 但是你必須確定你正確的在這個類中重寫了hashCode() 和 equals()方法。
你可以使用一個component作為一個實體類的標識符。 你的component類必須滿足以下要求:
-
它必須實現java.io.Serializable接口
-
它必須重新實現equals()和hashCode()方法, 始終和組合關鍵字在數據庫中的概念保持一致
注意:在Hibernate3中,第二種要求并非是Hibernate強制必須的。但最好這樣做。
你不能使用一個IdentifierGenerator產生組合關鍵字。作為替代應用程序必須分配它自己的標識符。
使用<composite-id> 標簽(并且內嵌<key-property>元素)代替通常的<id>標簽。 比如,OrderLine類具有一個依賴Order的(聯合)主鍵的主鍵。
<class name="OrderLine"> <composite-id name="id" class="OrderLineId"> <key-property name="lineId"/> <key-property name="orderId"/> <key-property name="customerId"/> </composite-id> <property name="name"/> <many-to-one name="order" class="Order" insert="false" update="false"> <column name="orderId"/> <column name="customerId"/> </many-to-one> .... </class>
現在,任何關聯到OrderLine 的外鍵都是復合的。在你的映射文件中,必須為其他類也這樣聲明。指向OrderLine的關聯可能被這樣映射:
<many-to-one name="orderLine" class="OrderLine"> <!-- the "class" attribute is optional, as usual --> <column name="lineId"/> <column name="orderId"/> <column name="customerId"/> </many-to-one>
(注意在各個地方<column>標簽都是column屬性的替代寫法。)
指向OrderLine的多對多關聯也使用聯合外鍵:
<set name="undeliveredOrderLines"> <key column name="warehouseId"/> <many-to-many class="OrderLine"> <column name="lineId"/> <column name="orderId"/> <column name="customerId"/> </many-to-many> </set>
在Order中, OrderLine的集合則是這樣:
<set name="orderLines" inverse="true"> <key> <column name="orderId"/> <column name="customerId"/> </key> <one-to-many class="OrderLine"/> </set>
(與通常一樣,<one-to-many>元素不聲明任何列.)
假若OrderLine本身擁有一個集合,它也具有組合外鍵。
<class name="OrderLine"> .... .... <list name="deliveryAttempts"> <key> <!-- a collection inherits the composite key type --> <column name="lineId"/> <column name="orderId"/> <column name="customerId"/> </key> <list-index column="attemptId" base="1"/> <composite-element class="DeliveryAttempt"> ... </composite-element> </set> </class>
你甚至可以映射Map類型的屬性:
<dynamic-component name="userAttributes"> <property name="foo" column="FOO"/> <property name="bar" column="BAR"/> <many-to-one name="baz" class="Baz" column="BAZ_ID"/> </dynamic-component>
從<dynamic-component>映射的語義上來講,它和<component>是相同的。 這種映射類型的優點在于通過修改映射文件,就可以具有在部署時檢測真實屬性的能力.利用一個DOM解析器,是有可能在運行時刻操作映射文件的。 更好的是,你可以通過Configuration對象來訪問(或者修改)Hibernate的運行時元模型。
Hibernate支持三種基本的繼承映射策略:
-
每個類分層結構一張表(table per class hierarchy)
-
每個子類一張表(table per subclass)
-
每個具體類一張表(table per concrete class)
此外,Hibernate還支持第四種稍有不同的多態映射策略:
-
隱式多態(implicit polymorphism)
對于同一個繼承層次內的不同分支,可以采用不同的映射策略,然后用隱式多 態來完成跨越整個層次的多態。但是在同一個<class>根元素 下,Hibernate不支持混合了元素<subclass>、 <joined-subclass>和<union-subclass> 的映射。在同一個<class>元素下,可以混合使用 “每個類分層結構一張表”(table per hierarchy) 和“每個子類一張表”(table per subclass) 這兩種映射策略,這是通過結合元素<subclass>和 <join>來實現的(見后)。
假設我們有接口Payment和它的幾個實現類: CreditCardPayment, CashPayment, 和ChequePayment。則“每個類分層結構一張表”(Table per class hierarchy)的映射代碼如下所示:
<class name="Payment" table="PAYMENT"> <id name="id" type="long" column="PAYMENT_ID"> <generator class="native"/> </id> <discriminator column="PAYMENT_TYPE" type="string"/> <property name="amount" column="AMOUNT"/> ... <subclass name="CreditCardPayment" discriminator-value="CREDIT"> <property name="creditCardType" column="CCTYPE"/> ... </subclass> <subclass name="CashPayment" discriminator-value="CASH"> ... </subclass> <subclass name="ChequePayment" discriminator-value="CHEQUE"> ... </subclass> </class>
采用這種策略只需要一張表即可。它有一個很大的限制:要求那些由子類定義的字段, 如CCTYPE,不能有非空(NOT NULL)約束。
對于上例中的幾個類而言,采用“每個子類一張表”的映射策略,代碼如下所示:
<class name="Payment" table="PAYMENT"> <id name="id" type="long" column="PAYMENT_ID"> <generator class="native"/> </id> <property name="amount" column="AMOUNT"/> ... <joined-subclass name="CreditCardPayment" table="CREDIT_PAYMENT"> <key column="PAYMENT_ID"/> ... </joined-subclass> <joined-subclass name="CashPayment" table="CASH_PAYMENT"> <key column="PAYMENT_ID"/> <property name="creditCardType" column="CCTYPE"/> ... </joined-subclass> <joined-subclass name="ChequePayment" table="CHEQUE_PAYMENT"> <key column="PAYMENT_ID"/> ... </joined-subclass> </class>
需要四張表。三個子類表通過主鍵關聯到超類表(因而關系模型實際上是一對一關聯)。
注意,對“每個子類一張表”的映射策略,Hibernate的實現不需要辨別字段,而其他 的對象/關系映射工具使用了一種不同于Hibernate的實現方法,該方法要求在超類 表中有一個類型辨別字段(type discriminator column)。Hibernate采用的方法更 難實現,但從關系(數據庫)這點上來看,按理說它更正確。若你愿意使用帶有辨別字 段的“每個子類一張表”的策略,你可以結合使用<subclass> 與<join>,如下所示:
<class name="Payment" table="PAYMENT"> <id name="id" type="long" column="PAYMENT_ID"> <generator class="native"/> </id> <discriminator column="PAYMENT_TYPE" type="string"/> <property name="amount" column="AMOUNT"/> ... <subclass name="CreditCardPayment" discriminator-value="CREDIT"> <join table="CREDIT_PAYMENT"> <property name="creditCardType" column="CCTYPE"/> ... </join> </subclass> <subclass name="CashPayment" discriminator-value="CASH"> <join table="CASH_PAYMENT"> ... </join> </subclass> <subclass name="ChequePayment" discriminator-value="CHEQUE"> <join table="CHEQUE_PAYMENT" fetch="select"> ... </join> </subclass> </class>
可選的聲明fetch="select",是用來告訴Hibernate,在查詢超類時, 不要使用外部連接(outer join)來抓取子類ChequePayment的數據。
你甚至可以采取如下方法混和使用“每個類分層結構一張表”和“每個子類一張表”這兩種策略:
<class name="Payment" table="PAYMENT"> <id name="id" type="long" column="PAYMENT_ID"> <generator class="native"/> </id> <discriminator column="PAYMENT_TYPE" type="string"/> <property name="amount" column="AMOUNT"/> ... <subclass name="CreditCardPayment" discriminator-value="CREDIT"> <join table="CREDIT_PAYMENT"> <property name="creditCardType" column="CCTYPE"/> ... </join> </subclass> <subclass name="CashPayment" discriminator-value="CASH"> ... </subclass> <subclass name="ChequePayment" discriminator-value="CHEQUE"> ... </subclass> </class>
對上述任何一種映射策略而言,指向根類Payment的 關聯是使用<many-to-one>進行映射的。
<many-to-one name="payment" column="PAYMENT_ID" class="Payment"/>
對于“每個具體類一張表”的映射策略,可以采用兩種方法。第一種方法是使用 <union-subclass>。
<class name="Payment"> <id name="id" type="long" column="PAYMENT_ID"> <generator class="sequence"/> </id> <property name="amount" column="AMOUNT"/> ... <union-subclass name="CreditCardPayment" table="CREDIT_PAYMENT"> <property name="creditCardType" column="CCTYPE"/> ... </union-subclass> <union-subclass name="CashPayment" table="CASH_PAYMENT"> ... </union-subclass> <union-subclass name="ChequePayment" table="CHEQUE_PAYMENT"> ... </union-subclass> </class>
這里涉及三張表。每張表為對應類的所有屬性(包括從超類繼承的屬性)定義相應字段。
這種方式的局限在于,如果一個屬性在超類中做了映射,其字段名必須與所有子類 表中定義的相同。(我們可能會在Hibernate的后續發布版本中放寬此限制。) 不允許在聯合子類(union subclass)的繼承層次中使用標識生成器策略(identity generator strategy), 實際上, 主鍵的種子(primary key seed)不得不為同一繼承層次中的全部被聯合子類所共用.
另一種可供選擇的方法是采用隱式多態:
<class name="CreditCardPayment" table="CREDIT_PAYMENT"> <id name="id" type="long" column="CREDIT_PAYMENT_ID"> <generator class="native"/> </id> <property name="amount" column="CREDIT_AMOUNT"/> ... </class> <class name="CashPayment" table="CASH_PAYMENT"> <id name="id" type="long" column="CASH_PAYMENT_ID"> <generator class="native"/> </id> <property name="amount" column="CASH_AMOUNT"/> ... </class> <class name="ChequePayment" table="CHEQUE_PAYMENT"> <id name="id" type="long" column="CHEQUE_PAYMENT_ID"> <generator class="native"/> </id> <property name="amount" column="CHEQUE_AMOUNT"/> ... </class>
注意,我們沒有在任何地方明確的提及接口Payment。同時注意 Payment的屬性在每個子類中都進行了映射。如果你想避免重復, 可以考慮使用XML實體(例如:位于DOCTYPE聲明內的 [ <!ENTITY allproperties SYSTEM "allproperties.xml"> ] 和映射中的&allproperties;)。
這種方法的缺陷在于,在Hibernate執行多態查詢時(polymorphic queries)無法生成帶 UNION的SQL語句。
對于這種映射策略而言,通常用<any>來實現到 Payment的多態關聯映射。
<any name="payment" meta-type="string" id-type="long"> <meta-value value="CREDIT" class="CreditCardPayment"/> <meta-value value="CASH" class="CashPayment"/> <meta-value value="CHEQUE" class="ChequePayment"/> <column name="PAYMENT_CLASS"/> <column name="PAYMENT_ID"/> </any>
對這一映射還有一點需要注意。因為每個子類都在各自獨立的元素<class> 中映射(并且Payment只是一個接口),每個子類可以很容易的成為另一 個繼承體系中的一部分!(你仍然可以對接口Payment使用多態查詢。)
<class name="CreditCardPayment" table="CREDIT_PAYMENT"> <id name="id" type="long" column="CREDIT_PAYMENT_ID"> <generator class="native"/> </id> <discriminator column="CREDIT_CARD" type="string"/> <property name="amount" column="CREDIT_AMOUNT"/> ... <subclass name="MasterCardPayment" discriminator-value="MDC"/> <subclass name="VisaPayment" discriminator-value="VISA"/> </class> <class name="NonelectronicTransaction" table="NONELECTRONIC_TXN"> <id name="id" type="long" column="TXN_ID"> <generator class="native"/> </id> ... <joined-subclass name="CashPayment" table="CASH_PAYMENT"> <key column="PAYMENT_ID"/> <property name="amount" column="CASH_AMOUNT"/> ... </joined-subclass> <joined-subclass name="ChequePayment" table="CHEQUE_PAYMENT"> <key column="PAYMENT_ID"/> <property name="amount" column="CHEQUE_AMOUNT"/> ... </joined-subclass> </class>
我們還是沒有明確的提到Payment。 如果我們針對接口Payment執行查詢 ——如from Payment—— Hibernate 自動返回CreditCardPayment(和它的子類,因為 它們也實現了接口Payment)、 CashPayment和Chequepayment的實例, 但不返回NonelectronicTransaction的實例。
對“每個具體類映射一張表”(table per concrete-class)的映射策略而言,隱式多態的 方式有一定的限制。而<union-subclass>映射的限制則沒有那 么嚴格。
下面表格中列出了在Hibernte中“每個具體類一張表”的策略和隱式多態的限制。
表 10.1. 繼承映射特性(Features of inheritance mappings)
繼承策略(Inheritance strategy) | 多態多對一 | 多態一對一 | 多態一對多 | 多態多對多 | 多態 load()/get() | 多態查詢 | 多態連接(join) | 外連接(Outer join)抓取 |
---|---|---|---|---|---|---|---|---|
每個類分層結構一張表 | <many-to-one> | <one-to-one> | <one-to-many> | <many-to-many> | s.get(Payment.class, id) | from Payment p | from Order o join o.payment p | 支持 |
每個子類一張表 | <many-to-one> | <one-to-one> | <one-to-many> | <many-to-many> | s.get(Payment.class, id) | from Payment p | from Order o join o.payment p | 支持 |
每個具體類一張表(union-subclass) | <many-to-one> | <one-to-one> | <one-to-many> (僅對于inverse="true"的情況) | <many-to-many> | s.get(Payment.class, id) | from Payment p | from Order o join o.payment p | 支持 |
每個具體類一張表(隱式多態) | <any> | 不支持 | 不支持 | <many-to-any> | s.createCriteria(Payment.class).add( Restrictions.idEq(id) ).uniqueResult() | from Payment p | 不支持 | 不支持 |