8.5.3. 多對(duì)多(many to many)
最后,還有 雙向多對(duì)多關(guān)聯(lián).
<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這個(gè)概念在Hibernate中幾處不同的地方為了不同的目的被重復(fù)使用.
9.1. 依賴對(duì)象(Dependent objects)
Component是一個(gè)被包含的對(duì)象,它作為值類型被持久化,而非一個(gè)被引用的實(shí)體。“component(組件)”這一術(shù)語指的是面向?qū)ο蟮暮铣筛拍睿ǘ⒉皇窍到y(tǒng)構(gòu)架層次上的組件的概念)舉個(gè)例子, 你可以對(duì)人(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;
}
}
現(xiàn)在,姓名(Name)是作為人(Person)的一個(gè)組成部分。需要注意的是:需要對(duì)姓名 的持久化屬性定義getter和setter方法,但是不需要實(shí)現(xiàn)任何的接口或申明標(biāo)識(shí)符字段。
以下是這個(gè)例子的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不支持共享引用。 換句話說,兩個(gè)人可能重名,但是兩個(gè)person對(duì)象應(yīng)該包含兩個(gè)獨(dú)立的name對(duì)象,只不過是具有“同樣”的值。 Component的值為空從語義學(xué)上來講是專有的(ad hoc)。 每當(dāng) 重新加載一個(gè)包含組件的對(duì)象,如果component的所有字段為空,那么將Hibernate將假定整個(gè)component為 空。對(duì)于絕大多數(shù)目的,這樣假定是沒有問題的。
Component的屬性可以是Hibernate類型(包括Collections, many-to-one 關(guān)聯(lián), 以及其它Component 等等)。嵌套Component不應(yīng)該作為特殊的應(yīng)用被考慮(Nested components should not be considered an exotic usage)。 Hibernate趨向于支持設(shè)計(jì)細(xì)致(fine-grained)的對(duì)象模型。
<component> 元素還允許有 <parent>子元素 ,用來表明component類中的一個(gè)屬性返回包含它的實(shí)體的引用。
<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>
9.2. 在集合中出現(xiàn)的依賴對(duì)象
Hibernate支持component的集合(例如: 一個(gè)元素是“姓名”這種類型的數(shù)組)。 你可以使用<composite-element>標(biāo)簽替代<element>標(biāo)簽來定義你的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>
注意,如果你決定定義一個(gè)元素是聯(lián)合元素的Set,正確地實(shí)現(xiàn)equals()和hashCode()是非常重要的。
組合元素可以包含component但是不能包含集合。如果你的組合元素自身包含component, 必須使用<nested-composite-element>標(biāo)簽。這是一個(gè)相當(dāng)特殊的案例 - 組合元素的集合自身可以包含component。 這個(gè)時(shí)候你就應(yīng)該考慮一下使用one-to-many關(guān)聯(lián)是否會(huì)更恰當(dāng)。 嘗試對(duì)這個(gè)組合元素重新建模為一個(gè)實(shí)體-但是需要注意的是,雖然Java模型和重新建模前 是一樣的,關(guān)系模型和持久性語義上仍然存在輕微的區(qū)別。
請(qǐng)注意如果你使用<set>標(biāo)簽,一個(gè)組合元素的映射不支持可能為空的屬性. 當(dāng)刪除對(duì)象時(shí), Hibernate必須使用每一個(gè)字段的來確定一條記錄(在組合元素表中,沒有單個(gè)的關(guān)鍵字段), 如果有為null的字段,這樣做就不可能了。你必須作出一個(gè)選擇,要么在組合元素中使用不能為空的屬性, 要么選擇使用<list>, <map>,<bag> 或者 <idbag>而不是 <set>。
組合元素有個(gè)特別的案例,是組合元素可以包含一個(gè)<many-to-one> 元素。類似這樣的映射允許你映射一個(gè)many-to-mang關(guān)聯(lián)表作為組合元素額外的字段。(A mapping like this allows you to map extra columns of a many-to-many association table to the composite element class.) 接下來的的例子是從Order到Item的一個(gè)多對(duì)多的關(guān)聯(lián)關(guān)系,而 purchaseDate, price 和 quantity 是Item的關(guān)聯(lián)屬性。
<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>
當(dāng)然,在另一方面,無法存在指向purchase的關(guān)聯(lián),因此不能實(shí)現(xiàn)雙向關(guān)聯(lián)查詢。記住組建是值類型,并且不允許共享關(guān)聯(lián)。單個(gè)Purchase 可以放在包含Order的集合中,但它不能同時(shí)被Item所關(guān)聯(lián)。
即使三重或多重管理都是可能的:
<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>
在查詢中,組合元素使用的語法是和關(guān)聯(lián)到其他實(shí)體的語法一樣的。
9.3. 組件作為Map的索引(Components as Map indices )
<composite-map-key>元素允許你映射一個(gè)Component類作為Map的key, 但是你必須確定你正確的在這個(gè)類中重寫了hashCode() 和 equals()方法。
9.4. 組件作為聯(lián)合標(biāo)識(shí)符(Components as composite identifiers)
你可以使用一個(gè)component作為一個(gè)實(shí)體類的標(biāo)識(shí)符。 你的component類必須滿足以下要求:
-
它必須實(shí)現(xiàn)java.io.Serializable接口
-
它必須重新實(shí)現(xiàn)equals()和hashCode()方法, 始終和組合關(guān)鍵字在數(shù)據(jù)庫(kù)中的概念保持一致
注意:在Hibernate3中,第二種要求并非是Hibernate強(qiáng)制必須的。但最好這樣做。
你不能使用一個(gè)IdentifierGenerator產(chǎn)生組合關(guān)鍵字。作為替代應(yīng)用程序必須分配它自己的標(biāo)識(shí)符。
使用<composite-id> 標(biāo)簽(并且內(nèi)嵌<key-property>元素)代替通常的<id>標(biāo)簽。 比如,OrderLine類具有一個(gè)依賴Order的(聯(lián)合)主鍵的主鍵。
<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>
現(xiàn)在,任何關(guān)聯(lián)到OrderLine 的外鍵都是復(fù)合的。在你的映射文件中,必須為其他類也這樣聲明。指向OrderLine的關(guān)聯(lián)可能被這樣映射:
<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>
(注意在各個(gè)地方<column>標(biāo)簽都是column屬性的替代寫法。)
指向OrderLine的多對(duì)多關(guān)聯(lián)也使用聯(lián)合外鍵:
<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本身擁有一個(gè)集合,它也具有組合外鍵。
<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>
9.5. 動(dòng)態(tài)組件 (Dynamic components)
你甚至可以映射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>是相同的。 這種映射類型的優(yōu)點(diǎn)在于通過修改映射文件,就可以具有在部署時(shí)檢測(cè)真實(shí)屬性的能力.利用一個(gè)DOM解析器,是有可能在運(yùn)行時(shí)刻操作映射文件的。 更好的是,你可以通過Configuration對(duì)象來訪問(或者修改)Hibernate的運(yùn)行時(shí)元模型。