posts - 0, comments - 77, trackbacks - 0, articles - 356
            BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

          關(guān)聯(lián)關(guān)系映射

          Posted on 2007-08-10 14:01 semovy 閱讀(619) 評(píng)論(0)  編輯  收藏 所屬分類(lèi): Hibernate

          第 8 章 關(guān)聯(lián)關(guān)系映射

          8.1. 介紹

          關(guān)聯(lián)關(guān)系映射通常情況是最難配置正確的。在這個(gè)部分中,我們從單向關(guān)系映射開(kāi)始,然后考慮雙向關(guān)系映射,由淺至深講述一遍典型的案例。在所有的例子中,我們都使用 PersonAddress

          我們根據(jù)映射關(guān)系是否涉及連接表以及多樣性來(lái)劃分關(guān)聯(lián)類(lèi)型。

          在傳統(tǒng)的數(shù)據(jù)建模中,允許為Null值的外鍵被認(rèn)為是一種不好的實(shí)踐,因此我們所有的例子中都使用不允許為Null的外鍵。這并不是Hibernate的要求,即使你刪除掉不允許為Null的約束,Hibernate映射一樣可以工作的很好。

          8.2. 單向關(guān)聯(lián)(Unidirectional associations)

          8.2.1. 多對(duì)一(many to one)

          單向many-to-one關(guān)聯(lián)是最常見(jiàn)的單向關(guān)聯(lián)關(guān)系。

          <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 )
          

          8.2.2. 一對(duì)一(one to one)

          基于外鍵關(guān)聯(lián)的單向一對(duì)一關(guān)聯(lián)單向多對(duì)一關(guān)聯(lián)幾乎是一樣的。唯一的不同就是單向一對(duì)一關(guān)聯(lián)中的外鍵字段具有唯一性約束。

          <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 )
          

          基于主鍵關(guān)聯(lián)的單向一對(duì)一關(guān)聯(lián)通常使用一個(gè)特定的id生成器。(請(qǐng)注意,在這個(gè)例子中我們掉換了關(guān)聯(lián)的方向。)

          <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 )
          

          8.2.3. 一對(duì)多(one to many)

          基于外鍵關(guān)聯(lián)的單向一對(duì)多關(guān)聯(lián)是一種很少見(jiàn)的情況,并不推薦使用。

          <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 )
          

          我們認(rèn)為對(duì)于這種關(guān)聯(lián)關(guān)系最好使用連接表。

          8.3. 使用連接表的單向關(guān)聯(lián)(Unidirectional associations with join tables)

          8.3.1. 一對(duì)多(one to many)

          基于連接表的單向一對(duì)多關(guān)聯(lián) 應(yīng)該優(yōu)先被采用。請(qǐng)注意,通過(guò)指定unique="true",我們可以把多樣性從多對(duì)多改變?yōu)橐粚?duì)多。

          <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 )
          

          8.3.2. 多對(duì)一(many to one)

          基于連接表的單向多對(duì)一關(guān)聯(lián)在關(guān)聯(lián)關(guān)系可選的情況下應(yīng)用也很普遍。

          <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 )
          

          8.3.3. 一對(duì)一(one to one)

          基于連接表的單向一對(duì)一關(guān)聯(lián)非常少見(jiàn),但也是可行的。

          <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 )
          

          8.3.4. 多對(duì)多(many to many)

          最后,還有 單向多對(duì)多關(guān)聯(lián).

          <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 )
          

          8.4. 雙向關(guān)聯(lián)(Bidirectional associations)

          8.4.1. 一對(duì)多(one to many) / 多對(duì)一(many to one)

          雙向多對(duì)一關(guān)聯(lián) 是最常見(jiàn)的關(guān)聯(lián)關(guān)系。(這也是標(biāo)準(zhǔn)的父/子關(guān)聯(lián)關(guān)系。)

          <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 )
          

          8.4.2. 一對(duì)一(one to one)

          基于外鍵關(guān)聯(lián)的雙向一對(duì)一關(guān)聯(lián)也很常見(jiàn)。

          <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 )
          

          基于主鍵關(guān)聯(lián)的一對(duì)一關(guān)聯(lián)需要使用特定的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 )
          

          8.5. 使用連接表的雙向關(guān)聯(lián)(Bidirectional associations with join tables)

          8.5.1. 一對(duì)多(one to many) /多對(duì)一( many to one)

          基于連接表的雙向一對(duì)多關(guān)聯(lián)。注意inverse="true"可以出現(xiàn)在關(guān)聯(lián)的任意一端,即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 )
          

          8.5.2. 一對(duì)一(one to one)

          基于連接表的雙向一對(duì)一關(guān)聯(lián)極為罕見(jiàn),但也是可行的。

          <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 )
          

          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 )
          

          第 9 章 組件(Component)映射

          Component這個(gè)概念在Hibernate中幾處不同的地方為了不同的目的被重復(fù)使用.

          9.1. 依賴(lài)對(duì)象(Dependent objects)

          Component是一個(gè)被包含的對(duì)象,它作為值類(lèi)型被持久化,而非一個(gè)被引用的實(shí)體。“component(組件)”這一術(shù)語(yǔ)指的是面向?qū)ο蟮暮铣筛拍睿ǘ⒉皇窍到y(tǒng)構(gòu)架層次上的組件的概念)舉個(gè)例子, 你可以對(duì)人(Person)如以下這樣來(lái)建模:

          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, firstlast等字段。

          就像所有的值類(lèi)型一樣, Component不支持共享引用。 換句話說(shuō),兩個(gè)人可能重名,但是兩個(gè)person對(duì)象應(yīng)該包含兩個(gè)獨(dú)立的name對(duì)象,只不過(guò)是具有“同樣”的值。 Component的值為空從語(yǔ)義學(xué)上來(lái)講是專(zhuān)有的(ad hoc)。 每當(dāng) 重新加載一個(gè)包含組件的對(duì)象,如果component的所有字段為空,那么將Hibernate將假定整個(gè)component為 空。對(duì)于絕大多數(shù)目的,這樣假定是沒(méi)有問(wèn)題的。

          Component的屬性可以是Hibernate類(lèi)型(包括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>子元素 ,用來(lái)表明component類(lèi)中的一個(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)的依賴(lài)對(duì)象

          Hibernate支持component的集合(例如: 一個(gè)元素是“姓名”這種類(lèi)型的數(shù)組)。 你可以使用<composite-element>標(biāo)簽替代<element>標(biāo)簽來(lái)定義你的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)系模型和持久性語(yǔ)義上仍然存在輕微的區(qū)別。

          請(qǐng)注意如果你使用<set>標(biāo)簽,一個(gè)組合元素的映射不支持可能為空的屬性. 當(dāng)刪除對(duì)象時(shí), Hibernate必須使用每一個(gè)字段的來(lái)確定一條記錄(在組合元素表中,沒(méi)有單個(gè)的關(guān)鍵字段), 如果有為null的字段,這樣做就不可能了。你必須作出一個(gè)選擇,要么在組合元素中使用不能為空的屬性, 要么選擇使用<list>, <map>,<bag> 或者 <idbag>而不是 <set>

          組合元素有個(gè)特別的案例,是組合元素可以包含一個(gè)<many-to-one> 元素。類(lèi)似這樣的映射允許你映射一個(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.) 接下來(lái)的的例子是從OrderItem的一個(gè)多對(duì)多的關(guān)聯(lián)關(guān)系,而 purchaseDate, pricequantityItem的關(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)然,在另一方面,無(wú)法存在指向purchase的關(guān)聯(lián),因此不能實(shí)現(xiàn)雙向關(guān)聯(lián)查詢(xún)。記住組建是值類(lèi)型,并且不允許共享關(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>

          在查詢(xún)中,組合元素使用的語(yǔ)法是和關(guān)聯(lián)到其他實(shí)體的語(yǔ)法一樣的。

          9.3. 組件作為Map的索引(Components as Map indices )

          <composite-map-key>元素允許你映射一個(gè)Component類(lèi)作為Map的key, 但是你必須確定你正確的在這個(gè)類(lèi)中重寫(xiě)了hashCode()equals()方法。

          9.4. 組件作為聯(lián)合標(biāo)識(shí)符(Components as composite identifiers)

          你可以使用一個(gè)component作為一個(gè)實(shí)體類(lèi)的標(biāo)識(shí)符。 你的component類(lèi)必須滿(mǎn)足以下要求:

          • 它必須實(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類(lèi)具有一個(gè)依賴(lài)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ù)合的。在你的映射文件中,必須為其他類(lèi)也這樣聲明。指向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屬性的替代寫(xiě)法。)

          指向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本身?yè)碛幸粋€(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類(lèi)型的屬性:

          <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>映射的語(yǔ)義上來(lái)講,它和<component>是相同的。 這種映射類(lèi)型的優(yōu)點(diǎn)在于通過(guò)修改映射文件,就可以具有在部署時(shí)檢測(cè)真實(shí)屬性的能力.利用一個(gè)DOM解析器,是有可能在運(yùn)行時(shí)刻操作映射文件的。 更好的是,你可以通過(guò)Configuration對(duì)象來(lái)訪問(wèn)(或者修改)Hibernate的運(yùn)行時(shí)元模型。

          第 10 章 繼承映射(Inheritance Mappings)

          10.1.  三種策略

          Hibernate支持三種基本的繼承映射策略:

          • 每個(gè)類(lèi)分層結(jié)構(gòu)一張表(table per class hierarchy)

          • 每個(gè)子類(lèi)一張表(table per subclass)

          • 每個(gè)具體類(lèi)一張表(table per concrete class)

          此外,Hibernate還支持第四種稍有不同的多態(tài)映射策略:

          • 隱式多態(tài)(implicit polymorphism)

          對(duì)于同一個(gè)繼承層次內(nèi)的不同分支,可以采用不同的映射策略,然后用隱式多 態(tài)來(lái)完成跨越整個(gè)層次的多態(tài)。但是在同一個(gè)<class>根元素 下,Hibernate不支持混合了元素<subclass><joined-subclass><union-subclass> 的映射。在同一個(gè)<class>元素下,可以混合使用 “每個(gè)類(lèi)分層結(jié)構(gòu)一張表”(table per hierarchy) 和“每個(gè)子類(lèi)一張表”(table per subclass) 這兩種映射策略,這是通過(guò)結(jié)合元素<subclass><join>來(lái)實(shí)現(xiàn)的(見(jiàn)后)。

          10.1.1. 每個(gè)類(lèi)分層結(jié)構(gòu)一張表(Table per class hierarchy)

          假設(shè)我們有接口Payment和它的幾個(gè)實(shí)現(xiàn)類(lèi): CreditCardPayment, CashPayment, 和ChequePayment。則“每個(gè)類(lèi)分層結(jié)構(gòu)一張表”(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>

          采用這種策略只需要一張表即可。它有一個(gè)很大的限制:要求那些由子類(lèi)定義的字段, 如CCTYPE,不能有非空(NOT NULL)約束。

          10.1.2. 每個(gè)子類(lèi)一張表(Table per subclass)

          對(duì)于上例中的幾個(gè)類(lèi)而言,采用“每個(gè)子類(lèi)一張表”的映射策略,代碼如下所示:

          <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>

          需要四張表。三個(gè)子類(lèi)表通過(guò)主鍵關(guān)聯(lián)到超類(lèi)表(因而關(guān)系模型實(shí)際上是一對(duì)一關(guān)聯(lián))。

          10.1.3. 每個(gè)子類(lèi)一張表(Table per subclass),使用辨別標(biāo)志(Discriminator)

          注意,對(duì)“每個(gè)子類(lèi)一張表”的映射策略,Hibernate的實(shí)現(xiàn)不需要辨別字段,而其他 的對(duì)象/關(guān)系映射工具使用了一種不同于Hibernate的實(shí)現(xiàn)方法,該方法要求在超類(lèi) 表中有一個(gè)類(lèi)型辨別字段(type discriminator column)。Hibernate采用的方法更 難實(shí)現(xiàn),但從關(guān)系(數(shù)據(jù)庫(kù))這點(diǎn)上來(lái)看,按理說(shuō)它更正確。若你愿意使用帶有辨別字 段的“每個(gè)子類(lèi)一張表”的策略,你可以結(jié)合使用<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",是用來(lái)告訴Hibernate,在查詢(xún)超類(lèi)時(shí), 不要使用外部連接(outer join)來(lái)抓取子類(lèi)ChequePayment的數(shù)據(jù)。

          10.1.4. 混合使用“每個(gè)類(lèi)分層結(jié)構(gòu)一張表”和“每個(gè)子類(lèi)一張表”

          你甚至可以采取如下方法混和使用“每個(gè)類(lèi)分層結(jié)構(gòu)一張表”和“每個(gè)子類(lèi)一張表”這兩種策略:

          <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>

          對(duì)上述任何一種映射策略而言,指向根類(lèi)Payment的 關(guān)聯(lián)是使用<many-to-one>進(jìn)行映射的。

          <many-to-one name="payment" column="PAYMENT_ID" class="Payment"/>

          10.1.5. 每個(gè)具體類(lèi)一張表(Table per concrete class)

          對(duì)于“每個(gè)具體類(lèi)一張表”的映射策略,可以采用兩種方法。第一種方法是使用 <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>

          這里涉及三張表。每張表為對(duì)應(yīng)類(lèi)的所有屬性(包括從超類(lèi)繼承的屬性)定義相應(yīng)字段。

          這種方式的局限在于,如果一個(gè)屬性在超類(lèi)中做了映射,其字段名必須與所有子類(lèi) 表中定義的相同。(我們可能會(huì)在Hibernate的后續(xù)發(fā)布版本中放寬此限制。) 不允許在聯(lián)合子類(lèi)(union subclass)的繼承層次中使用標(biāo)識(shí)生成器策略(identity generator strategy), 實(shí)際上, 主鍵的種子(primary key seed)不得不為同一繼承層次中的全部被聯(lián)合子類(lèi)所共用.

          10.1.6. Table per concrete class, using implicit polymorphism

          10.1.6. Table per concrete class, using implicit polymorphism

          另一種可供選擇的方法是采用隱式多態(tài):

          <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>

          注意,我們沒(méi)有在任何地方明確的提及接口Payment。同時(shí)注意 Payment的屬性在每個(gè)子類(lèi)中都進(jìn)行了映射。如果你想避免重復(fù), 可以考慮使用XML實(shí)體(例如:位于DOCTYPE聲明內(nèi)的 [ <!ENTITY allproperties SYSTEM "allproperties.xml"> ] 和映射中的&allproperties;)。

          這種方法的缺陷在于,在Hibernate執(zhí)行多態(tài)查詢(xún)時(shí)(polymorphic queries)無(wú)法生成帶 UNION的SQL語(yǔ)句。

          對(duì)于這種映射策略而言,通常用<any>來(lái)實(shí)現(xiàn)到 Payment的多態(tài)關(guān)聯(lián)映射。

          <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>

          10.1.7. 隱式多態(tài)和其他繼承映射混合使用

          對(duì)這一映射還有一點(diǎn)需要注意。因?yàn)槊總€(gè)子類(lèi)都在各自獨(dú)立的元素<class> 中映射(并且Payment只是一個(gè)接口),每個(gè)子類(lèi)可以很容易的成為另一 個(gè)繼承體系中的一部分!(你仍然可以對(duì)接口Payment使用多態(tài)查詢(xún)。)

          <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>

          我們還是沒(méi)有明確的提到Payment。 如果我們針對(duì)接口Payment執(zhí)行查詢(xún) ——如from Payment—— Hibernate 自動(dòng)返回CreditCardPayment(和它的子類(lèi),因?yàn)?它們也實(shí)現(xiàn)了接口Payment)、 CashPaymentChequepayment的實(shí)例, 但不返回NonelectronicTransaction的實(shí)例。

          10.2. 限制

          對(duì)“每個(gè)具體類(lèi)映射一張表”(table per concrete-class)的映射策略而言,隱式多態(tài)的 方式有一定的限制。而<union-subclass>映射的限制則沒(méi)有那 么嚴(yán)格。

          下面表格中列出了在Hibernte中“每個(gè)具體類(lèi)一張表”的策略和隱式多態(tài)的限制。

          表 10.1. 繼承映射特性(Features of inheritance mappings)

          繼承策略(Inheritance strategy) 多態(tài)多對(duì)一 多態(tài)一對(duì)一 多態(tài)一對(duì)多 多態(tài)多對(duì)多 多態(tài) load()/get() 多態(tài)查詢(xún) 多態(tài)連接(join) 外連接(Outer join)抓取
          每個(gè)類(lèi)分層結(jié)構(gòu)一張表 <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 支持
          每個(gè)子類(lèi)一張表 <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 支持
          每個(gè)具體類(lèi)一張表(union-subclass) <many-to-one> <one-to-one> <one-to-many> (僅對(duì)于inverse="true"的情況) <many-to-many> s.get(Payment.class, id) from Payment p from Order o join o.payment p 支持
          每個(gè)具體類(lèi)一張表(隱式多態(tài)) <any> 不支持 不支持 <many-to-any> s.createCriteria(Payment.class).add( Restrictions.idEq(id) ).uniqueResult() from Payment p 不支持 不支持
          主站蜘蛛池模板: 广州市| 宝丰县| 巨鹿县| 阿图什市| 沛县| 凤台县| 钦州市| 永川市| 华容县| 榆林市| 房产| 永登县| 鹤山市| 万年县| 东阳市| 额尔古纳市| 建德市| 寻甸| 夏津县| 苏尼特右旗| 色达县| 黄骅市| 高安市| 克拉玛依市| 渝北区| 康平县| 崇义县| 涿鹿县| 临汾市| 南召县| 额敏县| 库车县| 天津市| 碌曲县| 海原县| 蓝田县| 南澳县| 金寨县| 大城县| 武冈市| 正阳县|