程序 人生

          程序 人生

          BlogJava 首頁 新隨筆 聯(lián)系 聚合 管理
            11 Posts :: 2 Stories :: 18 Comments :: 0 Trackbacks

          6.1.?映射定義(Mapping declaration)

          對象和關(guān)系數(shù)據(jù)庫之間的映射通常是用一個XML文檔(XML document)來定義的。這個映射文檔被設(shè)計為易讀的, 并且可以手工修改。映射語言是以Java為中心,這意味著映射文檔是按照持久化類的定義來創(chuàng)建的, 而非表的定義。

          請注意,雖然很多Hibernate用戶選擇手寫XML映射文檔,但也有一些工具可以用來生成映射文檔, 包括XDoclet,Middlegen和AndroMDA。

          讓我們從一個映射的例子開始:

          <?xml version="1.0"?>
          <!DOCTYPE hibernate-mapping PUBLIC
                "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
                    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
          
          <hibernate-mapping package="eg">
          
                  <class name="Cat" 
                      table="cats"
                      discriminator-value="C">
                          
                          <id name="id">
                                  <generator class="native"/>
                          </id>
          
                          <discriminator column="subclass" 
                               type="character"/>
          
                          <property name="weight"/>
          
                          <property name="birthdate"
                              type="date" 
                              not-null="true" 
                              update="false"/>
          
                          <property name="color"
                              type="eg.types.ColorUserType"
                              not-null="true"
                              update="false"/>
          
                          <property name="sex"
                              not-null="true" 
                              update="false"/>
          
                          <property name="litterId"
                              column="litterId"
                              update="false"/>
          
                          <many-to-one name="mother"
                              column="mother_id"
                              update="false"/>
          
                          <set name="kittens"
                              inverse="true"
                              order-by="litter_id">
                                  <key column="mother_id"/>
                                  <one-to-many class="Cat"/>
                          </set>
          
                          <subclass name="DomesticCat"
                              discriminator-value="D">
          
                                  <property name="name" 
                                      type="string"/>
          
                          </subclass>
          
                  </class>
          
                  <class name="Dog">
                          <!-- mapping for Dog could go here -->
                  </class>
          
          </hibernate-mapping>

          我們現(xiàn)在開始討論映射文檔的內(nèi)容。我們只描述Hibernate在運(yùn)行時用到的文檔元素和屬性。 映射文檔還包括一些額外的可選屬性和元素,它們在使用schema導(dǎo)出工具的時候會影響導(dǎo)出的數(shù)據(jù)庫schema結(jié)果。 (比如, not-null 屬性。)

          6.1.1.?Doctype

          所有的XML映射都需要定義如上所示的doctype。DTD可以從上述URL中獲取, 從hibernate-x.x.x/src/net/sf/hibernate目錄中、 或hibernate.jar文件中找到。Hibernate總是會首先在它的classptah中搜索DTD文件。 如果你發(fā)現(xiàn)它是通過連接Internet查找DTD文件,就對照你的classpath目錄檢查XML文件里的DTD聲明。

          6.1.2.?hibernate-mapping

          這個元素包括一些可選的屬性。schemacatalog屬性, 指明了這個映射所連接(refer)的表所在的schema和/或catalog名稱。 假若指定了這個屬性,表名會加上所指定的schema和catalog的名字?jǐn)U展為全限定名。假若沒有指定,表名就不會使用全限定名。 default-cascade指定了未明確注明cascade屬性的Java屬性和 集合類Hibernate會采取什么樣的默認(rèn)級聯(lián)風(fēng)格。auto-import屬性默認(rèn)讓我們在查詢語言中可以使用 非全限定名的類名。

          <hibernate-mapping
                   schema="schemaName"                          (1)
                   catalog="catalogName"                        (2)
                   default-cascade="cascade_style"              (3)
                   default-access="field|property|ClassName"    (4)
                   default-lazy="true|false"                    (5)
                   auto-import="true|false"                     (6)
                   package="package.name"                       (7)
           />
          (1)

          schema (可選): 數(shù)據(jù)庫schema的名稱。

          (2)

          catalog (可選): 數(shù)據(jù)庫catalog的名稱。

          (3)

          default-cascade (可選 - 默認(rèn)為 none): 默認(rèn)的級聯(lián)風(fēng)格。

          (4)

          default-access (可選 - 默認(rèn)為 property): Hibernate用來訪問屬性的策略。可以通過實(shí)現(xiàn)PropertyAccessor接口 自定義。

          (5)

          default-lazy (可選 - 默認(rèn)為 true): 指定了未明確注明lazy屬性的Java屬性和集合類, Hibernate會采取什么樣的默認(rèn)加載風(fēng)格。

          (6)

          auto-import (可選 - 默認(rèn)為 true): 指定我們是否可以在查詢語言中使用非全限定的類名(僅限于本映射文件中的類)。

          (7)

          package (可選): 指定一個包前綴,如果在映射文檔中沒有指定全限定的類名, 就使用這個作為包名。

          假若你有兩個持久化類,它們的非全限定名是一樣的(就是兩個類的名字一樣,所在的包不一樣--譯者注), 你應(yīng)該設(shè)置auto-import="false"。假若說你把一個“import過”的名字同時對應(yīng)兩個類, Hibernate會拋出一個異常。

          注意hibernate-mapping 元素允許你嵌套多個如上所示的 <class>映射。但是最好的做法(也許一些工具需要的)是一個 持久化類(或一個類的繼承層次)對應(yīng)一個映射文件,并以持久化的超類名稱命名,例如: Cat.hbm.xmlDog.hbm.xml,或者如果使用繼承,Animal.hbm.xml

          6.1.3.?class

          你可以使用class元素來定義一個持久化類:

          <class
                  name="ClassName"                              (1)
                  table="tableName"                             (2)
                  discriminator-value="discriminator_value"     (3)
                  mutable="true|false"                          (4)
                  schema="owner"                                (5)
                  catalog="catalog"                             (6)
                  proxy="ProxyInterface"                        (7)
                  dynamic-update="true|false"                   (8)
                  dynamic-insert="true|false"                   (9)
                  select-before-update="true|false"             (10)
                  polymorphism="implicit|explicit"              (11)
                  where="arbitrary sql where condition"         (12)
                  persister="PersisterClass"                    (13)
                  batch-size="N"                                (14)
                  optimistic-lock="none|version|dirty|all"      (15)
                  lazy="true|false"                             (16)
                  entity-name="EntityName"                      (17)
                  check="arbitrary sql check condition"         (18)
                  rowid="rowid"                                 (19)
                  subselect="SQL expression"                    (20)
                  abstract="true|false"                         (21)
                  entity-name="EntityName"                      (22)
                  node="element-name"                           (23)
          />
          (1)

          name (可選): 持久化類(或者接口)的Java全限定名。 如果這個屬性不存在,Hibernate將假定這是一個非POJO的實(shí)體映射。

          (2)

          table (可選 - 默認(rèn)是類的非全限定名): 對應(yīng)的數(shù)據(jù)庫表名。

          (3)

          discriminator-value (可選 - 默認(rèn)和類名一樣): 一個用于區(qū)分不同的子類的值,在多態(tài)行為時使用。它可以接受的值包括 nullnot null

          (4)

          mutable (可選,默認(rèn)值為true): 表明該類的實(shí)例是可變的或者可變的。

          (5)

          schema (可選): 覆蓋在根<hibernate-mapping>元素中指定的schema名字。

          (6)

          catalog (可選): 覆蓋在根<hibernate-mapping>元素中指定的catalog名字。

          (7)

          proxy (可選): 指定一個接口,在延遲裝載時作為代理使用。 你可以在這里使用該類自己的名字。

          (8)

          dynamic-update (可選, 默認(rèn)為 false): 指定用于UPDATE 的SQL將會在運(yùn)行時動態(tài)生成,并且只更新那些改變過的字段。

          (9)

          dynamic-insert (可選, 默認(rèn)為 false): 指定用于INSERT的 SQL 將會在運(yùn)行時動態(tài)生成,并且只包含那些非空值字段。

          (10)

          select-before-update (可選, 默認(rèn)為 false): 指定Hibernate除非確定對象真正被修改了(如果該值為true-譯注),否則不會執(zhí)行SQL UPDATE操作。在特定場合(實(shí)際上,它只在一個瞬時對象(transient object)關(guān)聯(lián)到一個 新的session中時執(zhí)行的update()中生效),這說明Hibernate會在UPDATE 之前執(zhí)行一次額外的SQL SELECT操作,來決定是否應(yīng)該執(zhí)行 UPDATE

          (11)

          polymorphism(多態(tài)) (可選, 默認(rèn)值為 implicit (隱式) ): 界定是隱式還是顯式的使用多態(tài)查詢(這只在Hibernate的具體表繼承策略中用到-譯注)。

          (12)

          where (可選) 指定一個附加的SQLWHERE 條件, 在抓取這個類的對象時會一直增加這個條件。

          (13)

          persister (可選): 指定一個定制的ClassPersister

          (14)

          batch-size (可選,默認(rèn)是1) 指定一個用于 根據(jù)標(biāo)識符(identifier)抓取實(shí)例時使用的"batch size"(批次抓取數(shù)量)。

          (15)

          optimistic-lock(樂觀鎖定) (可選,默認(rèn)是version): 決定樂觀鎖定的策略。

          (16)

          lazy (optional): 通過設(shè)置lazy="false", 所有的延遲加載(Lazy fetching)功能將未被激活(disabled)。

          (17)

          entity-name (可選): Hibernate3允許一個類進(jìn)行多次映射( 默認(rèn)情況是映射到不同的表),并且允許使用Maps或XML代替Java層次的實(shí)體映射 (也就是實(shí)現(xiàn)動態(tài)領(lǐng)域模型,不用寫持久化類-譯注)。 更多信息請看第?5.4?節(jié) “動態(tài)模型(Dynamic models)” and 第?19?章 XML映射

          (18)

          check (可選): 這是一個SQL表達(dá)式, 用于為自動生成的schema添加多行(multi-row)約束檢查

          (19)

          rowid (可選): Hibernate可以使用數(shù)據(jù)庫支持的所謂的ROWIDs,例如: Oracle數(shù)據(jù)庫,如果你設(shè)置這個可選的rowid, Hibernate可以使用額外的字段rowid實(shí)現(xiàn)快速更新。ROWID是這個功能實(shí)現(xiàn)的重點(diǎn), 它代表了一個存儲元組(tuple)的物理位置。

          (20)

          subselect (可選): 它將一個不可變(immutable)并且只讀的實(shí)體映射到一個數(shù)據(jù)庫的 子查詢中。它用于實(shí)現(xiàn)一個視圖代替一張基本表,但是最好不要這樣做。更多的介紹請看下面內(nèi)容。

          (21)

          abstract (可選): 用于在<union-subclass>的繼承結(jié)構(gòu) (hierarchies)中標(biāo)識抽象超類。

          (22)

          entity-name (可選, 默認(rèn)為類名): 顯式指定實(shí)體名

          若指明的持久化類實(shí)際上是一個接口,這也是完全可以接受的。 之后你可以用<subclass>來指定該接口的實(shí)際實(shí)現(xiàn)類。 你可以持久化任何static(靜態(tài)的)內(nèi)部類。 你應(yīng)該使用標(biāo)準(zhǔn)的類名格式來指定類名,比如:Foo$Bar

          不可變類,mutable="false"不可以被應(yīng)用程序更新或者刪除。 這可以讓Hibernate做一些小小的性能優(yōu)化。

          可選的proxy屬性允許延遲加載類的持久化實(shí)例。 Hibernate開始會返回實(shí)現(xiàn)了這個命名接口的CGLIB代理。當(dāng)代理的某個方法被實(shí)際調(diào)用的時候, 真實(shí)的持久化對象才會被裝載。參見下面的“用于延遲裝載的代理”。

          Implicit (隱式)的多態(tài)是指,如果查詢時給出的是任何超類、該類實(shí)現(xiàn)的接口或者該類的 名字,都會返回這個類的實(shí)例;如果查詢中給出的是子類的名字,則會返回子類的實(shí)例。 Explicit (顯式)的多態(tài)是指,只有在查詢時給出明確的該類名字時才會返回這個類的實(shí)例; 同時只有在這個<class>的定義中作為<subclass> 或者<joined-subclass>出現(xiàn)的子類,才會可能返回。 在大多數(shù)情況下,默認(rèn)的polymorphism="implicit"都是合適的。 顯式的多態(tài)在有兩個不同的類映射到同一個表的時候很有用。(允許一個“輕型”的類,只包含部分表字段)。

          persister屬性可以讓你定制這個類使用的持久化策略。 你可以指定你自己實(shí)現(xiàn) org.hibernate.persister.EntityPersister的子類,你甚至可以完全從頭開始編寫一個 org.hibernate.persister.ClassPersister接口的實(shí)現(xiàn), 比如是用儲存過程調(diào)用、序列化到文件或者LDAP數(shù)據(jù)庫來實(shí)現(xiàn)。 參閱org.hibernate.test.CustomPersister,這是一個簡單的例子 (“持久化”到一個Hashtable)。

          請注意dynamic-updatedynamic-insert的設(shè)置并不會繼承到子類, 所以在<subclass>或者<joined-subclass>元素中可能 需要再次設(shè)置。這些設(shè)置是否能夠提高效率要視情形而定。請用你的智慧決定是否使用。

          使用select-before-update通常會降低性能。如果你重新連接一個脫管(detache)對象實(shí)例 到一個Session中時,它可以防止數(shù)據(jù)庫不必要的觸發(fā)update。 這就很有用了。

          如果你打開了dynamic-update,你可以選擇幾種樂觀鎖定的策略:

          • version(版本檢查) 檢查version/timestamp字段

          • all(全部) 檢查全部字段

          • dirty(臟檢查)只檢察修改過的字段

          • none(不檢查)不使用樂觀鎖定

          我們非常強(qiáng)烈建議你在Hibernate中使用version/timestamp字段來進(jìn)行樂觀鎖定。 對性能來說,這是最好的選擇,并且這也是唯一能夠處理在session外進(jìn)行操作的策略(例如: 在使用Session.merge()的時候)。

          對Hibernate映射來說視圖和表是沒有區(qū)別的,這是因?yàn)樗鼈冊跀?shù)據(jù)層都是透明的( 注意:一些數(shù)據(jù)庫不支持視圖屬性,特別是更新的時候)。有時你想使用視圖,但卻不能在數(shù)據(jù)庫 中創(chuàng)建它(例如:在遺留的schema中)。這樣的話,你可以映射一個不可變的(immutable)并且是 只讀的實(shí)體到一個給定的SQL子查詢表達(dá)式:

          <class name="Summary">
              <subselect>
                  select item.name, max(bid.amount), count(*)
                  from item
                  join bid on bid.item_id = item.id
                  group by item.name
              </subselect>
              <synchronize table="item"/>
              <synchronize table="bid"/>
              <id name="name"/>
              ...
          </class>

          定義這個實(shí)體用到的表為同步(synchronize),確保自動刷新(auto-flush)正確執(zhí)行, 并且依賴原實(shí)體的查詢不會返回過期數(shù)據(jù)。<subselect>在屬性元素 和一個嵌套映射元素中都可見。

          6.1.4.?id

          被映射的類必須定義對應(yīng)數(shù)據(jù)庫表主鍵字段。大多數(shù)類有一個JavaBeans風(fēng)格的屬性, 為每一個實(shí)例包含唯一的標(biāo)識。<id> 元素定義了該屬性到數(shù)據(jù)庫表主鍵字段的映射。

          <id
                  name="propertyName"                                          (1)
                  type="typename"                                              (2)
                  column="column_name"                                         (3)
                  unsaved-value="null|any|none|undefined|id_value"             (4)
                  access="field|property|ClassName"                            (5)
                  node="element-name|@attribute-name|element/@attribute|.">
          
                  <generator class="generatorClass"/>
          </id>
          (1)

          name (可選): 標(biāo)識屬性的名字。

          (2)

          type (可選): 標(biāo)識Hibernate類型的名字。

          (3)

          column (可選 - 默認(rèn)為屬性名): 主鍵字段的名字。

          (4)

          unsaved-value (可選 - 默認(rèn)為一個字段判斷(sensible)的值): 一個特定的標(biāo)識屬性值,用來標(biāo)志該實(shí)例是剛剛創(chuàng)建的,尚未保存。 這可以把這種實(shí)例和從以前的session中裝載過(可能又做過修改--譯者注) 但未再次持久化的實(shí)例區(qū)分開來。

          (5)

          access (可選 - 默認(rèn)為property): Hibernate用來訪問屬性值的策略。

          如果 name屬性不存在,會認(rèn)為這個類沒有標(biāo)識屬性。

          unsaved-value 屬性很重要!如果你的類的標(biāo)識屬性不是默認(rèn)為 正常的Java默認(rèn)值(null或零),你應(yīng)該指定正確的默認(rèn)值。

          還有一個另外的<composite-id>定義可以訪問舊式的多主鍵數(shù)據(jù)。 我們強(qiáng)烈不建議使用這種方式。

          6.1.4.1.?Generator

          可選的<generator>子元素是一個Java類的名字, 用來為該持久化類的實(shí)例生成唯一的標(biāo)識。如果這個生成器實(shí)例需要某些配置值或者初始化參數(shù), 用<param>元素來傳遞。

          <id name="id" type="long" column="cat_id">
                  <generator class="org.hibernate.id.TableHiLoGenerator">
                          <param name="table">uid_table</param>
                          <param name="column">next_hi_value_column</param>
                  </generator>
          </id>

          所有的生成器都實(shí)現(xiàn)net.sf.hibernate.id.IdentifierGenerator接口。 這是一個非常簡單的接口;某些應(yīng)用程序可以選擇提供他們自己特定的實(shí)現(xiàn)。當(dāng)然, Hibernate提供了很多內(nèi)置的實(shí)現(xiàn)。下面是一些內(nèi)置生成器的快捷名字:

          increment

          用于為long, short或者int類型生成 唯一標(biāo)識。只有在沒有其他進(jìn)程往同一張表中插入數(shù)據(jù)時才能使用。 在集群下不要使用。

          identity

          對DB2,MySQL, MS SQL Server, Sybase和HypersonicSQL的內(nèi)置標(biāo)識字段提供支持。 返回的標(biāo)識符是long, short 或者int類型的。

          sequence

          在DB2,PostgreSQL, Oracle, SAP DB, McKoi中使用序列(sequence), 而在Interbase中使用生成器(generator)。返回的標(biāo)識符是long, short或者 int類型的。

          hilo

          使用一個高/低位算法高效的生成long, short 或者 int類型的標(biāo)識符。給定一個表和字段(默認(rèn)分別是是 hibernate_unique_keynext_hi)作為高位值的來源。 高/低位算法生成的標(biāo)識符只在一個特定的數(shù)據(jù)庫中是唯一的。

          seqhilo

          使用一個高/低位算法來高效的生成long, short 或者 int類型的標(biāo)識符,給定一個數(shù)據(jù)庫序列(sequence)的名字。

          uuid

          用一個128-bit的UUID算法生成字符串類型的標(biāo)識符, 這在一個網(wǎng)絡(luò)中是唯一的(使用了IP地址)。UUID被編碼為一個32位16進(jìn)制數(shù)字的字符串。

          guid

          在MS SQL Server 和 MySQL 中使用數(shù)據(jù)庫生成的GUID字符串。

          native

          根據(jù)底層數(shù)據(jù)庫的能力選擇identity, sequence 或者hilo中的一個。

          assigned

          讓應(yīng)用程序在save()之前為對象分配一個標(biāo)示符。這是 <generator>元素沒有指定時的默認(rèn)生成策略。

          select

          通過數(shù)據(jù)庫觸發(fā)器選擇一些唯一主鍵的行并返回主鍵值來分配一個主鍵。

          foreign

          使用另外一個相關(guān)聯(lián)的對象的標(biāo)識符。通常和<one-to-one>聯(lián)合起來使用。

          6.1.4.2.?高/低位算法(Hi/Lo Algorithm)

          hiloseqhilo生成器給出了兩種hi/lo算法的實(shí)現(xiàn), 這是一種很令人滿意的標(biāo)識符生成算法。第一種實(shí)現(xiàn)需要一個“特殊”的數(shù)據(jù)庫表來保存下一個可用的“hi”值。 第二種實(shí)現(xiàn)使用一個Oracle風(fēng)格的序列(在被支持的情況下)。

          <id name="id" type="long" column="cat_id">
                  <generator class="hilo">
                          <param name="table">hi_value</param>
                          <param name="column">next_value</param>
                          <param name="max_lo">100</param>
                  </generator>
          </id>
          <id name="id" type="long" column="cat_id">
                  <generator class="seqhilo">
                          <param name="sequence">hi_value</param>
                          <param name="max_lo">100</param>
                  </generator>
          </id>

          很不幸,你在為Hibernate自行提供Connection時無法使用hilo。 當(dāng)Hibernate使用JTA獲取應(yīng)用服務(wù)器的數(shù)據(jù)源連接時,你必須正確地配置 hibernate.transaction.manager_lookup_class

          6.1.4.3.?UUID算法(UUID Algorithm )

          UUID包含:IP地址,JVM的啟動時間(精確到1/4秒),系統(tǒng)時間和一個計數(shù)器值(在JVM中唯一)。 在Java代碼中不可能獲得MAC地址或者內(nèi)存地址,所以這已經(jīng)是我們在不使用JNI的前提下的能做的最好實(shí)現(xiàn)了。

          6.1.4.4.?標(biāo)識字段和序列(Identity columns and Sequences)

          對于內(nèi)部支持標(biāo)識字段的數(shù)據(jù)庫(DB2,MySQL,Sybase,MS SQL),你可以使用identity關(guān)鍵字生成。 對于內(nèi)部支持序列的數(shù)據(jù)庫(DB2,Oracle, PostgreSQL, Interbase, McKoi,SAP DB), 你可以使用sequence風(fēng)格的關(guān)鍵字生成。 這兩種方式對于插入一個新的對象都需要兩次SQL查詢。

          <id name="id" type="long" column="person_id">
                  <generator class="sequence">
                          <param name="sequence">person_id_sequence</param>
                  </generator>
          </id>
          <id name="id" type="long" column="person_id" unsaved-value="0">
                  <generator class="identity"/>
          </id>

          對于跨平臺開發(fā),native策略會從identity, sequencehilo中進(jìn)行選擇,選擇哪一個,這取決于底層數(shù)據(jù)庫的支持能力。

          6.1.4.5.?程序分配的標(biāo)識符(Assigned Identifiers)

          如果你需要應(yīng)用程序分配一個標(biāo)示符(而非Hibernate來生成),你可以使用assigned 生成器。這種特殊的生成器會使用已經(jīng)分配給對象的標(biāo)識符屬性的標(biāo)識符值。 這個生成器使用一個自然鍵(natural key,有商業(yè)意義的列-譯注)作為主鍵,而不是使用一個代理鍵( surrogate key,沒有商業(yè)意義的列-譯注)。

          當(dāng)選擇assigned生成器時,除非有一個version或timestamp屬性,或者你定義了 Interceptor.isUnsaved(),否則需要讓Hiberante使用 unsaved-value="undefined",強(qiáng)制Hibernatet查詢數(shù)據(jù)庫來確定一個實(shí)例是瞬時的(transient) 還是脫管的(detached)。

          6.1.4.6.?觸發(fā)器實(shí)現(xiàn)的主鍵生成器(Primary keys assigned by triggers)

          僅僅用于遺留的schema中 (Hibernate不能使用觸發(fā)器生成DDL)。

          <id name="id" type="long" column="person_id">
                  <generator class="select">
                          <param name="key">socialSecurityNumber</param>
                  </generator>
          </id>

          在上面的例子中,類定義了一個命名為socialSecurityNumber的唯一值屬性, 它是一個自然鍵(natural key),命名為person_id的代理鍵(surrogate key) 的值由觸發(fā)器生成。

          6.1.5.?composite-id

          <composite-id
                  name="propertyName"
                  class="ClassName"
                  unsaved-value="undefined|any|none"
                  access="field|property|ClassName"
                  node="element-name|."
                  >
          
                  <key-property name="propertyName" type="typename" column="column_name"/>
                  <key-many-to-one name="propertyName class="ClassName" column="column_name"/>
                  ......
          </composite-id>

          For a table with a composite key, you may map multiple properties of the class as identifier properties. The <composite-id> element accepts <key-property> property mappings and <key-many-to-one> mappings as child elements.

          如果表使用聯(lián)合主鍵,你可以映射類的多個屬性為標(biāo)識符屬性。 <composite-id>元素接受<key-property> 屬性映射和<key-many-to-one>屬性映射作為子元素。

          <composite-id>
                  <key-property name="medicareNumber"/>
                  <key-property name="dependent"/>
          </composite-id>

          你的持久化類必須重載equals()hashCode()方法,來實(shí)現(xiàn)組合的標(biāo)識符的相等判斷。 實(shí)現(xiàn)Serializable接口也是必須的。

          不幸的是,這種組合關(guān)鍵字的方法意味著一個持久化類是它自己的標(biāo)識。除了對象自己之外, 沒有什么方便的“把手”可用。你必須自己初始化持久化類的實(shí)例,在使用組合關(guān)鍵字load() 持久化狀態(tài)之前,必須填充他的聯(lián)合屬性。我們會在第?9.4?節(jié) “組件作為聯(lián)合標(biāo)識符(Components as composite identifiers)”章中說明一種 更加便捷的方法,把聯(lián)合標(biāo)識實(shí)現(xiàn)為一個獨(dú)立的類,下面描述的屬性只對這種備用方法有效:

          • name (可選):一個組件類型,持有復(fù)合標(biāo)識(參見下一節(jié))。

          • class (可選 - 默認(rèn)為通過反射(reflection)得到的屬性類型) : 作為聯(lián)合標(biāo)識的組件類名(參見下一節(jié))。

          • unsaved-value (可選 - 默認(rèn)為 undefined): 如果設(shè)置為any,就表示瞬時(transient)實(shí)例應(yīng)該被重新初始化,或者如果 設(shè)置為none,則表示該實(shí)例是脫管對象。最好在所有的情況下都保持默認(rèn)的值。

          6.1.6.?鑒別器(discriminator)

          在"一棵對象繼承樹對應(yīng)一個表"的策略中,<discriminator>元素是必需的, 它定義了表的鑒別器字段。鑒別器字段包含標(biāo)志值,用于告知持久化層應(yīng)該為某個特定的行創(chuàng)建哪一個子類的實(shí)例。 如下這些受到限制的類型可以使用: string, character, integer, byte, short, boolean, yes_no, true_false.

          <discriminator
                  column="discriminator_column"                      (1)
                  type="discriminator_type"                          (2)
                  force="true|false"                                 (3)
                  insert="true|false"                                (4)
                  formula="arbitrary sql expression"                 (5)
          />
          (1)

          column (可選 - 默認(rèn)為 class) 鑒別器字段的名字

          (2)

          type (可選 - 默認(rèn)為 string) 一個Hibernate字段類型的名字

          (3)

          force(強(qiáng)制) (可選 - 默認(rèn)為 false) "強(qiáng)制"Hibernate指定允許的鑒別器值,就算取得的所有實(shí)例都是根類的。

          (4)

          insert (可選 - 默認(rèn)為true) 如果你的鑒別器字段也是映射為復(fù)合標(biāo)識(composite identifier)的一部分,則需將 這個值設(shè)為false。(告訴Hibernate在做SQL INSERT 時不包含該列)

          (5)

          formula (可選) 一個SQL表達(dá)式,在類型判斷(判斷是父類還是具體子類-譯注)時執(zhí)行。可用于基于內(nèi)容的鑒別器。

          鑒別器字段的實(shí)際值是根據(jù)<class><subclass>元素中 的discriminator-value屬性得來的。

          force屬性僅僅是在表包含一些未指定應(yīng)該映射到哪個持久化類的時候才是有用的。 這種情況不會經(jīng)常遇到。

          使用formula屬性你可以定義一個SQL表達(dá)式,用來判斷一個行數(shù)據(jù)的類型。

          <discriminator
              formula="case when CLASS_TYPE in ('a', 'b', 'c') then 0 else 1 end"
              type="integer"/>

          6.1.7.?版本(version)(可選)

          <version>元素是可選的,表明表中包含附帶版本信息的數(shù)據(jù)。 這在你準(zhǔn)備使用 長事務(wù)(long transactions)的時候特別有用。(見后)

          <version
                  column="version_column"                                      (1)
                  name="propertyName"                                          (2)
                  type="typename"                                              (3)
                  access="field|property|ClassName"                            (4)
                  unsaved-value="null|negative|undefined"                      (5)
                  node="element-name|@attribute-name|element/@attribute|."
          />
          (1)

          column (可選 - 默認(rèn)為屬性名): 指定持有版本號的字段名。

          (2)

          name: 持久化類的屬性名。

          (3)

          type (可選 - 默認(rèn)是 integer): 版本號的類型。

          (4)

          access (可選 - 默認(rèn)是 property): Hibernate用于訪問屬性值的策略。

          (5)

          unsaved-value (可選 - 默認(rèn)是undefined): 用于標(biāo)明某個實(shí)例時剛剛被實(shí)例化的(尚未保存)版本屬性值,依靠這個值就可以把這種情況 和已經(jīng)在先前的session中保存或裝載的脫管(detached)實(shí)例區(qū)分開來。 (undefined指明使用標(biāo)識屬性值進(jìn)行判斷。)

          版本號必須是以下類型:long, integer, short, timestamp或者calendar

          一個脫管(detached)實(shí)例的version或timestamp不能為空(null),因?yàn)镠ibernate不管 unsaved-value指定為何種策略,它將分離任何屬性為空的version或timestamp 實(shí)例為瞬時(transient)實(shí)例。 避免Hibernate中的傳遞重附(transitive reattachment)問題的一個簡單方法是 定義一個不能為空的version或timestamp屬性,特別是在人們使用程序分配的標(biāo)識符(assigned identifiers) 或復(fù)合主鍵時非常有用!

          6.1.8.?timestamp (optional)

          可選的<timestamp>元素指明了表中包含時間戳數(shù)據(jù)。 這用來作為版本的替代。時間戳本質(zhì)上是一種對樂觀鎖定的一種不是特別安全的實(shí)現(xiàn)。當(dāng)然, 有時候應(yīng)用程序可能在其他方面使用時間戳。

          <timestamp
                  column="timestamp_column"                                    (1)
                  name="propertyName"                                          (2)
                  access="field|property|ClassName"                            (3)
                  unsaved-value="null|undefined"                               (4)
                  node="element-name|@attribute-name|element/@attribute|."
          />
          (1)

          column (可選 - 默認(rèn)為屬性名): 持有時間戳的字段名。

          (2)

          name: 在持久化類中的JavaBeans風(fēng)格的屬性名, 其Java類型是 Date 或者 Timestamp的。

          (3)

          access (可選 - 默認(rèn)是 property): Hibernate用于訪問屬性值的策略。

          (4)

          unsaved-value (可選 - 默認(rèn)是null): 用于標(biāo)明某個實(shí)例時剛剛被實(shí)例化的(尚未保存)版本屬性值,依靠這個值就可以把這種情況和 已經(jīng)在先前的session中保存或裝載的脫管(detached)實(shí)例區(qū)分開來。(undefined 指明使用標(biāo)識屬性值進(jìn)行這種判斷。)

          注意,<timestamp><version type="timestamp">是等價的。

          6.1.9.?property

          <property>元素為類定義了一個持久化的,JavaBean風(fēng)格的屬性。

          <property
                  name="propertyName"                                          (1)
                  column="column_name"                                         (2)
                  type="typename"                                              (3)
                  update="true|false"                                          (4)
                  insert="true|false"                                          (4)
                  formula="arbitrary SQL expression"                           (5)
                  access="field|property|ClassName"                            (6)
                  lazy="true|false"                                            (7)
                  unique="true|false"                                          (8)
                  not-null="true|false"                                        (9)
                  optimistic-lock="true|false"                                 (10)
                  node="element-name|@attribute-name|element/@attribute|."
          />
          (1)

          name: 屬性的名字,以小寫字母開頭。

          (2)

          column (可選 - 默認(rèn)為屬性名字): 對應(yīng)的數(shù)據(jù)庫字段名。 也可以通過嵌套的<column>元素指定。

          (3)

          type (可選): 一個Hibernate類型的名字。

          (4)

          update, insert (可選 - 默認(rèn)為 true) : 表明用于UPDATE 和/或 INSERT 的SQL語句中是否包含這個被映射了的字段。這二者如果都設(shè)置為false 則表明這是一個“外源性(derived)”的屬性,它的值來源于映射到同一個(或多個) 字段的某些其他屬性,或者通過一個trigger(觸發(fā)器)或其他程序。

          (5)

          formula (可選): 一個SQL表達(dá)式,定義了這個計算 (computed) 屬性的值。計算屬性沒有和它對應(yīng)的數(shù)據(jù)庫字段。

          (6)

          access (可選 - 默認(rèn)值為 property): Hibernate用來訪問屬性值的策略。

          (7)

          lazy (可選 - 默認(rèn)為 false): 指定 指定實(shí)例變量第一次被訪問時,這個屬性是否延遲抓取(fetched lazily)( 需要運(yùn)行時字節(jié)碼增強(qiáng))。

          (8)

          unique (可選): 使用DDL為該字段添加唯一的約束。 此外,這也可以用作property-ref的目標(biāo)屬性。

          (9)

          not-null (可選): 使用DDL為該字段添加可否為空(nullability)的約束。

          (10)

          optimistic-lock (可選 - 默認(rèn)為 true): 指定這個屬性在做更新時是否需要獲得樂觀鎖定(optimistic lock)。 換句話說,它決定這個屬性發(fā)生臟數(shù)據(jù)時版本(version)的值是否增長。

          typename 可以是如下幾種:

          1. Hibernate基礎(chǔ)類型之一(比如:integer, string, character,date, timestamp, float, binary, serializable, object, blob)。

          2. 一個Java類的名字,這個類屬于一種默認(rèn)基礎(chǔ)類型 (比如: int, float,char, java.lang.String, java.util.Date, java.lang.Integer, java.sql.Clob)。

          3. 一個可以序列化的Java類的名字。

          4. 一個自定義類型的類的名字。(比如: com.illflow.type.MyCustomType)。

          如果你沒有指定類型,Hibernarte會使用反射來得到這個名字的屬性,以此來猜測正確的Hibernate類型。 Hibernate會按照規(guī)則2,3,4的順序?qū)傩宰x取器(getter方法)的返回類進(jìn)行解釋。然而,這還不夠。 在某些情況下你仍然需要type屬性。(比如,為了區(qū)別Hibernate.DATEHibernate.TIMESTAMP,或者為了指定一個自定義類型。)

          access屬性用來讓你控制Hibernate如何在運(yùn)行時訪問屬性。在默認(rèn)情況下, Hibernate會使用屬性的get/set方法對(pair)。如果你指明access="field", Hibernate會忽略get/set方法對,直接使用反射來訪問成員變量。你也可以指定你自己的策略, 這就需要你自己實(shí)現(xiàn)org.hibernate.property.PropertyAccessor接口, 再在access中設(shè)置你自定義策略類的名字。

          衍生屬性(derive propertie)是一個特別強(qiáng)大的特征。這些屬性應(yīng)該定義為只讀,屬性值在裝載時計算生成。 你用一個SQL表達(dá)式生成計算的結(jié)果,它會在這個實(shí)例轉(zhuǎn)載時翻譯成一個SQL查詢的SELECT 子查詢語句。

          <property name="totalPrice"
              formula="( SELECT SUM (li.quantity*p.price) FROM LineItem li, Product p
                          WHERE li.productId = p.productId
                          AND li.customerId = customerId
                          AND li.orderNumber = orderNumber )"/>

          注意,你可以使用實(shí)體自己的表,而不用為這個特別的列定義別名( 上面例子中的customerId)。同時注意,如果你不喜歡使用屬性, 你可以使用嵌套的<formula>映射元素。

          6.1.10.?多對一(many-to-one)

          通過many-to-one元素,可以定義一種常見的與另一個持久化類的關(guān)聯(lián)。 這種關(guān)系模型是多對一關(guān)聯(lián)(實(shí)際上是一個對象引用-譯注):這個表的一個外鍵引用目標(biāo)表的 主鍵字段。

          <many-to-one
                  name="propertyName"                                          (1)
                  column="column_name"                                         (2)
                  class="ClassName"                                            (3)
                  cascade="cascade_style"                                      (4)
                  fetch="join|select"                                          (5)
                  update="true|false"                                          (6)
                  insert="true|false"                                          (6)
                  property-ref="propertyNameFromAssociatedClass"               (7)
                  access="field|property|ClassName"                            (8)
                  unique="true|false"                                          (9)
                  not-null="true|false"                                        (10)
                  optimistic-lock="true|false"                                 (11)
                  lazy="true|proxy|false"                                      (12)
                  not-found="ignore|exception"                                 (13)
                  entity-name="EntityName"                                     (14)
                  node="element-name|@attribute-name|element/@attribute|."
                  embed-xml="true|false"
                  
          />
          (1)

          name: 屬性名。

          (2)

          column (可選): 外間字段名。它也可以通過嵌套的 <column>元素指定。

          (3)

          class (可選 - 默認(rèn)是通過反射得到屬性類型): 關(guān)聯(lián)的類的名字。

          (4)

          cascade(級聯(lián)) (可選): 指明哪些操作會從父對象級聯(lián)到關(guān)聯(lián)的對象。

          (5)

          fetch (可選 - 默認(rèn)為 select): 在外連接抓取(outer-join fetching)和序列選擇抓取(sequential select fetching)兩者中選擇其一。

          (6)

          update, insert (可選 - defaults to true) 指定對應(yīng)的字段是否包含在用于UPDATE 和/或 INSERT 的SQL語句中。如果二者都是false,則這是一個純粹的 “外源性(derived)”關(guān)聯(lián),它的值是通過映射到同一個(或多個)字段的某些其他屬性得到 或者通過trigger(觸發(fā)器)、或其他程序。

          (7)

          property-ref: (可選) 指定關(guān)聯(lián)類的一個屬性,這個屬性將會和本外鍵相對應(yīng)。 如果沒有指定,會使用對方關(guān)聯(lián)類的主鍵。

          (8)

          access (可選 - 默認(rèn)是 property): Hibernate用來訪問屬性的策略。

          (9)

          unique (可選): 使用DDL為外鍵字段生成一個唯一約束。此外, 這也可以用作property-ref的目標(biāo)屬性。這使關(guān)聯(lián)同時具有 一對一的效果。

          (10)

          not-null (可選): 使用DDL為外鍵字段生成一個非空約束。

          (11)

          optimistic-lock (可選 - 默認(rèn)為 true): 指定這個屬性在做更新時是否需要獲得樂觀鎖定(optimistic lock)。 換句話說,它決定這個屬性發(fā)生臟數(shù)據(jù)時版本(version)的值是否增長。

          (12)

          lazy (可選 - 默認(rèn)為 proxy): 默認(rèn)情況下,單點(diǎn)關(guān)聯(lián)是經(jīng)過代理的。lazy="true"指定此屬性應(yīng)該在實(shí)例變量第一次被訪問時應(yīng)該延遲抓取(fetche lazily)(需要運(yùn)行時字節(jié)碼的增強(qiáng))。 lazy="false"指定此關(guān)聯(lián)總是被預(yù)先抓取。

          (13)

          not-found (可選 - 默認(rèn)為 exception): 指定外鍵引用的數(shù)據(jù)不存在時如何處理: ignore會將數(shù)據(jù)不存在作為關(guān)聯(lián)到一個空對象(null)處理。

          (14)

          entity-name (optional): 被關(guān)聯(lián)的類的實(shí)體名。

          cascade屬性設(shè)置為除了none以外任何有意義的值, 它將把特定的操作傳播到關(guān)聯(lián)對象中。這個值就代表著Hibernate基本操作的名稱, persist, merge, delete, save-update, evict, replicate, lock, refresh, 以及特別的值delete-orphanall,并且可以用逗號分隔符 來合并這些操作,例如,cascade="persist,merge,evict"cascade="all,delete-orphan"。更全面的解釋請參考第?11.11?節(jié) “傳播性持久化(transitive persistence)”.

          一個典型的簡單many-to-one定義例子:

          <many-to-one name="product" class="Product" column="PRODUCT_ID"/>

          property-ref屬性只應(yīng)該用來對付老舊的數(shù)據(jù)庫系統(tǒng), 可能有外鍵指向?qū)Ψ疥P(guān)聯(lián)表的是個非主鍵字段(但是應(yīng)該是一個惟一關(guān)鍵字)的情況下。 這是一種十分丑陋的關(guān)系模型。比如說,假設(shè)Product類有一個惟一的序列號, 它并不是主鍵。(unique屬性控制Hibernate通過SchemaExport工具生成DDL的過程。)

          <property name="serialNumber" unique="true" type="string" column="SERIAL_NUMBER"/>

          那么關(guān)于OrderItem 的映射可能是:

          <many-to-one name="product" property-ref="serialNumber" column="PRODUCT_SERIAL_NUMBER"/>

          當(dāng)然,我們決不鼓勵這種用法。

          如果被引用的唯一主鍵由關(guān)聯(lián)實(shí)體的多個屬性組成,你應(yīng)該在名稱為<properties>的元素 里面映射所有關(guān)聯(lián)的屬性。

          6.1.11.?一對一

          持久化對象之間一對一的關(guān)聯(lián)關(guān)系是通過one-to-one元素定義的。

          <one-to-one
                  name="propertyName"                                          (1)
                  class="ClassName"                                            (2)
                  cascade="cascade_style"                                      (3)
                  constrained="true|false"                                     (4)
                  fetch="join|select"                                          (5)
                  property-ref="propertyNameFromAssociatedClass"               (6)
                  access="field|property|ClassName"                            (7)
                  formula="any SQL expression"                                 (8)
                  lazy="true|proxy|false"                                      (9)
                  entity-name="EntityName"                                     (10)
                  node="element-name|@attribute-name|element/@attribute|."
                  embed-xml="true|false"
          />
          (1)

          name: 屬性的名字。

          (2)

          class (可選 - 默認(rèn)是通過反射得到的屬性類型):被關(guān)聯(lián)的類的名字。

          (3)

          cascade(級聯(lián)) (可選) 表明操作是否從父對象級聯(lián)到被關(guān)聯(lián)的對象。

          (4)

          constrained(約束) (可選) 表明該類對應(yīng)的表對應(yīng)的數(shù)據(jù)庫表,和被關(guān)聯(lián)的對象所對應(yīng)的數(shù)據(jù)庫表之間,通過一個外鍵引用對主鍵進(jìn)行約束。 這個選項(xiàng)影響save()delete()在級聯(lián)執(zhí)行時的先后順序以及 決定該關(guān)聯(lián)能否被委托(也在schema export tool中被使用).

          (5)

          fetch (可選 - 默認(rèn)設(shè)置為選擇): 在外連接抓取或者序列選擇抓取選擇其一.

          (6)

          property-ref: (可選) 指定關(guān)聯(lián)類的屬性名,這個屬性將會和本類的主鍵相對應(yīng)。如果沒有指定,會使用對方關(guān)聯(lián)類的主鍵。

          (7)

          access (可選 - 默認(rèn)是 property): Hibernate用來訪問屬性的策略。

          (8)

          formula (可選):絕大多數(shù)一對一的關(guān)聯(lián)都指向其實(shí)體的主鍵。在一些少見的情況中, 你可能會指向其他的一個或多個字段,或者是一個表達(dá)式,這些情況下,你可以用一個SQL公式來表示。 (可以在org.hibernate.test.onetooneformula找到例子)

          (9)

          lazy (可選 - 默認(rèn)為 proxy): 默認(rèn)情況下,單點(diǎn)關(guān)聯(lián)是經(jīng)過代理的。lazy="true"指定此屬性應(yīng)該在實(shí)例變量第一次被訪問時應(yīng)該延遲抓取(fetche lazily)(需要運(yùn)行時字節(jié)碼的增強(qiáng))。 lazy="false"指定此關(guān)聯(lián)總是被預(yù)先抓取。注意,如果constrained="false", 不可能使用代理,Hibernate會采取預(yù)先抓取!

          (10)

          entity-name (可選): 被關(guān)聯(lián)的類的實(shí)體名。

          有兩種不同的一對一關(guān)聯(lián):

          • 主鍵關(guān)聯(lián)

          • 惟一外鍵關(guān)聯(lián)

          主鍵關(guān)聯(lián)不需要額外的表字段;如果兩行是通過這種一對一關(guān)系相關(guān)聯(lián)的,那么這兩行就共享同樣的主關(guān)鍵字值。所以如果你希望兩個對象通過主鍵一對一關(guān)聯(lián),你必須確認(rèn)它們被賦予同樣的標(biāo)識值!

          比如說,對下面的EmployeePerson進(jìn)行主鍵一對一關(guān)聯(lián):

          <one-to-one name="person" class="Person"/>
          <one-to-one name="employee" class="Employee" constrained="true"/>

          現(xiàn)在我們必須確保PERSON和EMPLOYEE中相關(guān)的字段是相等的。我們使用一個被成為foreign的特殊的hibernate標(biāo)識符生成策略:

          <class name="person" table="PERSON">
              <id name="id" column="PERSON_ID">
                  <generator class="foreign">
                      <param name="property">employee</param>
                  </generator>
              </id>
              ...
              <one-to-one name="employee"
                  class="Employee"
                  constrained="true"/>
          </class>

          一個剛剛保存的Person實(shí)例被賦予和該Personemployee屬性所指向的Employee實(shí)例同樣的關(guān)鍵字值。

          另一種方式是一個外鍵和一個惟一關(guān)鍵字對應(yīng),上面的EmployeePerson的例子,如果使用這種關(guān)聯(lián)方式,可以表達(dá)成:

          <many-to-one name="person" class="Person" column="PERSON_ID" unique="true"/>

          如果在Person的映射加入下面幾句,這種關(guān)聯(lián)就是雙向的:

          <one-to-one name"employee" class="Employee" property-ref="person"/>

          6.1.12.?組件(component), 動態(tài)組件(dynamic-component)

          <component>元素把子對象的一些元素與父類對應(yīng)的表的一些字段映射起來。 然后組件可以定義它們自己的屬性、組件或者集合。參見后面的“Components”一章。

          <component 
                  name="propertyName"                 (1)
                  class="className"                   (2)
                  insert="true|false"                 (3)
                  update="true|false"                 (4)
                  access="field|property|ClassName"   (5)
                  lazy="true|false"                   (6)
                  optimistic-lock="true|false"        (7)
                  unique="true|false"                 (8)
                  node="element-name|."
          >
                  
                  <property ...../>
                  <many-to-one .... />
                  ........
          </component>
          (1)

          name: 屬性名

          (2)

          class (可選 - 默認(rèn)為通過反射得到的屬性類型):組件(子)類的名字。

          (3)

          insert: 被映射的字段是否出現(xiàn)在SQL的INSERT語句中?

          (4)

          update: 被映射的字段是否出現(xiàn)在SQL的UPDATE語句中?

          (5)

          access (可選 - 默認(rèn)是 property): Hibernate用來訪問屬性的策略。

          (6)

          lazy (可選 - 默認(rèn)是 false): 表明此組件應(yīng)在實(shí)例變量第一次被訪問的時候延遲加載(需要編譯時字節(jié)碼裝置器)

          (7)

          optimistic-lock (可選 - 默認(rèn)是 true):表明更新此組件是否需要獲取樂觀鎖。換句話說,當(dāng)這個屬性變臟時,是否增加版本號(Version)

          (8)

          unique (可選 - 默認(rèn)是 false):表明組件映射的所有字段上都有唯一性約束

          <property>子標(biāo)簽為子類的一些屬性與表字段之間建立映射。

          <component>元素允許加入一個<parent>子元素,在組件類內(nèi)部就可以有一個指向其容器的實(shí)體的反向引用。

          <dynamic-component>元素允許把一個Map映射為組件,其屬性名對應(yīng)map的鍵值。 參見第?9.5?節(jié) “動態(tài)組件 (Dynamic components)”.

          6.1.13.?properties

          <properties> 元素允許定義一個命名的邏輯分組(grouping)包含一個類中的多個屬性。 這個元素最重要的用處是允許多個屬性的組合作為property-ref的目標(biāo)(target)。 這也是定義多字段唯一約束的一種方便途徑。

          <properties 
                  name="logicalName"                  (1)
                  insert="true|false"                 (2)
                  update="true|false"                 (3)
                  optimistic-lock="true|false"        (4)
                  unique="true|false"                 (5)
          >
                  
                  <property ...../>
                  <many-to-one .... />
                  ........
          </properties>
          (1)

          name: 分組的邏輯名稱 - 不是 實(shí)際屬性的名稱.

          (2)

          insert: 被映射的字段是否出現(xiàn)在SQL的 INSERT語句中?

          (3)

          update: 被映射的字段是否出現(xiàn)在SQL的 UPDATE語句中?

          (4)

          optimistic-lock (可選 - 默認(rèn)是 true):表明更新此組件是否需要獲取樂觀鎖。換句話說,當(dāng)這個屬性變臟時,是否增加版本號(Version)

          (5)

          unique (可選 - 默認(rèn)是 false):表明組件映射的所有字段上都有唯一性約束

          例如,如果我們有如下的<properties>映射:

          <class name="Person">
              <id name="personNumber"/>
              ...
              <properties name="name" 
                      unique="true" update="false">
                  <property name="firstName"/>
                  <property name="initial"/>
                  <property name="lastName"/>
              </properties>
          </class>

          然后,我們可能有一些遺留的數(shù)據(jù)關(guān)聯(lián),引用 Person表的這個唯一鍵,而不是主鍵。

          <many-to-one name="person" 
                   class="Person" property-ref="name">
              <column name="firstName"/>
              <column name="initial"/>
              <column name="lastName"/>
          </many-to-one>

          我們并不推薦這樣使用,除非在映射遺留數(shù)據(jù)的情況下。

          6.1.14.?子類(subclass)

          最后,多態(tài)持久化需要為父類的每個子類都進(jìn)行定義。對于“每一棵類繼承樹對應(yīng)一個表”的策略來說,就需要使用<subclass>定義。

          <subclass
                  name="ClassName"                              (1)
                  discriminator-value="discriminator_value"     (2)
                  proxy="ProxyInterface"                        (3)
                  lazy="true|false"                             (4)
                  dynamic-update="true|false"
                  dynamic-insert="true|false"
                  entity-name="EntityName"
                  node="element-name">
                  <property .... />
                  .....
          </subclass>
          (1)

          name: 子類的全限定名。

          (2)

          discriminator-value(辨別標(biāo)志) (可選 - 默認(rèn)為類名):一個用于區(qū)分每個獨(dú)立的子類的值。

          (3)

          proxy(代理) (可選): 指定一個類或者接口,在延遲裝載時作為代理使用。

          (4)

          lazy (可選, 默認(rèn)是true): 設(shè)置為 lazy="false" 禁止使用延遲抓取

          每個子類都應(yīng)該定義它自己的持久化屬性和子類。 <version><id> 屬性可以從根父類繼承下來。在一棵繼承樹上的每個子類都必須定義一個唯一的discriminator-value。如果沒有指定,就會使用Java類的全限定名。

          可以在單獨(dú)的映射文件中,直接在hibernate-mapping下定義subclassunion-subclassjoined-subclass映射。這樣你只要增加一個新的映射文件就可以繼承一棵類繼承樹。你必須在子類的映射中指定extends 屬性來指定已映射的超類。注意:以前,這個特性使得映射文件的順序變得很重要。從Hibernate3開始,當(dāng)使用extends關(guān)鍵字的時候,映射文件的次序便不重要了。而在單一映射文件中,依舊需要保持將超類定義在子類之前這樣的次序。

          <hibernate-mapping>
              <subclass name="DomesticCat" extends="Cat" discriminator-value="D">
                   <property name="name" type="string"/>
              </subclass>
          </hibernate-mapping>

          更多關(guān)于繼承映射的信息, 參考 第?10?章 繼承映射(Inheritance Mappings)章節(jié).

          6.1.15.?連接的子類(joined-subclass)

          此外,每個子類可能被映射到他自己的表中(每個子類一個表的策略)。被繼承的狀態(tài)通過和超類的表關(guān)聯(lián)得到。我們使用<joined-subclass>元素。

          <joined-subclass
                  name="ClassName"                    (1)
                  table="tablename"                   (2)
                  proxy="ProxyInterface"              (3)
                  lazy="true|false"                   (4)
                  dynamic-update="true|false"
                  dynamic-insert="true|false"
                  schema="schema"
                  catalog="catalog"
                  extends="SuperclassName"
                  persister="ClassName"
                  subselect="SQL expression"
                  entity-name="EntityName"
                  node="element-name">
          
                  <key .... >
          
                  <property .... />
                  .....
          </joined-subclass>
          (1)

          name: 子類的全限定名。

          (2)

          table: 子類的表名.

          (3)

          proxy (可選): 指定一個類或者接口,在延遲裝載時作為代理使用。

          (4)

          lazy (可選, 默認(rèn)是 true): 設(shè)置為 lazy="false" 禁止使用延遲裝載。

          這種映射策略不需要指定辨別標(biāo)志(discriminator)字段。但是,每一個子類都必須使用<key>元素指定一個表字段來持有對象的標(biāo)識符。本章開始的映射可以被用如下方式重寫:

          <?xml version="1.0"?>
          <!DOCTYPE hibernate-mapping PUBLIC
                  "-//Hibernate/Hibernate Mapping DTD//EN"
                  "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
          
          <hibernate-mapping package="eg">
          
                  <class name="Cat" table="CATS">
                          <id name="id" column="uid" type="long">
                                  <generator class="hilo"/>
                          </id>
                          <property name="birthdate" type="date"/>
                          <property name="color" not-null="true"/>
                          <property name="sex" not-null="true"/>
                          <property name="weight"/>
                          <many-to-one name="mate"/>
                          <set name="kittens">
                                  <key column="MOTHER"/>
                                  <one-to-many class="Cat"/>
                          </set>
                          <joined-subclass name="DomesticCat" table="DOMESTIC_CATS">
                              <key column="CAT"/>
                              <property name="name" type="string"/>
                          </joined-subclass>
                  </class>
          
                  <class name="eg.Dog">
                          <!-- mapping for Dog could go here -->
                  </class>
          
          </hibernate-mapping>

          更多關(guān)于繼承映射的信息,參考第?10?章 繼承映射(Inheritance Mappings)

          6.1.16.?聯(lián)合子類(union-subclass)

          第三種選擇是僅僅映射類繼承樹中具體類部分到表中(每個具體類一張表的策略)。其中,每張表定義了類的所有持久化狀態(tài),包括繼承的狀態(tài)。在 Hibernate 中,并不需要完全顯式地映射這樣的繼承樹。你可以簡單地使用單獨(dú)的<class>定義映射每個類。然而,如果你想使用多態(tài)關(guān)聯(lián)(例如,一個對類繼承樹中超類的關(guān)聯(lián)),你需要使用<union-subclass>映射。

          <union-subclass
                  name="ClassName"                    (1)
                  table="tablename"                   (2)
                  proxy="ProxyInterface"              (3)
                  lazy="true|false"                   (4)
                  dynamic-update="true|false"
                  dynamic-insert="true|false"
                  schema="schema"
                  catalog="catalog"
                  extends="SuperclassName"
                  abstract="true|false"
                  persister="ClassName"
                  subselect="SQL expression"
                  entity-name="EntityName"
                  node="element-name">
          
                  <property .... />
                  .....
          </union-subclass>
          (1)

          name: 子類的全限定名。

          (2)

          table: 子類的表名

          (3)

          proxy (可選): 指定一個類或者接口,在延遲裝載時作為代理使用。

          (4)

          lazy (可選, 默認(rèn)是 true): 設(shè)置為 lazy="false" 禁止使用延遲裝載。

          這種映射策略不需要指定辨別標(biāo)志(discriminator)字段。

          更多關(guān)于繼承映射的信息,參考第?10?章 繼承映射(Inheritance Mappings)

          6.1.17.?連接(join)

          使用 <join> 元素,可以將一個類的屬性映射到多張表中。

          <join
                  table="tablename"                        (1)
                  schema="owner"                           (2)
                  catalog="catalog"                        (3)
                  fetch="join|select"                      (4)
                  inverse="true|false"                     (5)
                  optional="true|false">                   (6)
                  
                  <key ... />
                  
                  <property ... />
                  ...
          </join>
          (1)

          table: 被連接表的名稱。

          (2)

          schema (可選):覆蓋由根<hibernate-mapping>元素指定的模式名稱。

          (3)

          catalog (可選): 覆蓋由根 <hibernate-mapping>元素指定的目錄名稱。

          (4)

          fetch (可選 - 默認(rèn)是 join): 如果設(shè)置為默認(rèn)值join, Hibernate 將使用一個內(nèi)連接來得到這個類或其超類定義的<join>,而使用一個外連接來得到其子類定義的<join>。如果設(shè)置為select,則 Hibernate 將為子類定義的 <join>使用順序選擇。這僅在一行數(shù)據(jù)表示一個子類的對象的時候才會發(fā)生。對這個類和其超類定義的<join>,依然會使用內(nèi)連接得到。

          (5)

          inverse (可選 - 默認(rèn)是 false): 如果打開,Hibernate 不會插入或者更新此連接定義的屬性。

          (6)

          optional (可選 - 默認(rèn)是 false): 如果打開,Hibernate 只會在此連接定義的屬性非空時插入一行數(shù)據(jù),并且總是使用一個外連接來得到這些屬性。

          例如,一個人(person)的地址(address)信息可以被映射到單獨(dú)的表中(并保留所有屬性的值類型語義):

          <class name="Person"
              table="PERSON">
          
              <id name="id" column="PERSON_ID">...</id>
          
              <join table="ADDRESS">
                  <key column="ADDRESS_ID"/>
                  <property name="address"/>
                  <property name="zip"/>
                  <property name="country"/>
              </join>
              ...

          此特性常常對遺留數(shù)據(jù)模型有用,我們推薦表個數(shù)比類個數(shù)少,以及細(xì)粒度的領(lǐng)域模型。然而,在單獨(dú)的繼承樹上切換繼承映射策略是有用的,后面會解釋這點(diǎn)。

          6.1.18.?鍵(key)

          我們目前已經(jīng)見到過<key>元素多次了。 這個元素在父映射元素定義了對新表的連接,并且在被連接表中定義了一個外鍵引用原表的主鍵的情況下經(jīng)常使用。

          <key
                  column="columnname"                      (1)
                  on-delete="noaction|cascade"             (2)
                  property-ref="propertyName"              (3)
                  not-null="true|false"                    (4)
                  update="true|false"                      (5)
                  unique="true|false"                      (6)
          />
          (1)

          column (可選): 外鍵字段的名稱。也可以通過嵌套的 <column>指定。

          (2)

          on-delete (可選, 默認(rèn)是 noaction): 表明外鍵關(guān)聯(lián)是否打開數(shù)據(jù)庫級別的級聯(lián)刪除。

          (3)

          property-ref (可選): 表明外鍵引用的字段不是原表的主鍵(提供給遺留數(shù)據(jù))。

          (4)

          not-null (可選): 表明外鍵的字段不可為空(這意味著無論何時外鍵都是主鍵的一部分)。

          (5)

          update (可選): 表明外鍵決不應(yīng)該被更新(這意味著無論何時外鍵都是主鍵的一部分)。

          (6)

          unique (可選): 表明外鍵應(yīng)有唯一性約束 (這意味著無論何時外鍵都是主鍵的一部分)。

          對那些看重刪除性能的系統(tǒng),我們推薦所有的鍵都應(yīng)該定義為on-delete="cascade",這樣 Hibernate 將使用數(shù)據(jù)庫級的ON CASCADE DELETE約束,而不是多個DELETE語句。 注意,這個特性會繞過 Hibernate 通常對版本數(shù)據(jù)(versioned data)采用的樂觀鎖策略。

          not-nullupdate 屬性在映射單向一對多關(guān)聯(lián)的時候有用。如果你映射一個單向一對多關(guān)聯(lián)到非空的(non-nullable)外鍵,你必須<key not-null="true">定義此鍵字段。

          6.1.19.?字段和規(guī)則元素(column and formula elements)

          任何接受column屬性的映射元素都可以選擇接受<column> 子元素。同樣的,formula也可以替換<formula>屬性。

          <column
                  name="column_name"
                  length="N"
                  precision="N"
                  scale="N"
                  not-null="true|false"
                  unique="true|false"
                  unique-key="multicolumn_unique_key_name"
                  index="index_name"
                  sql-type="sql_type_name"
                  check="SQL expression"/>
          <formula>SQL expression</formula>

          columnformula 屬性甚至可以在同一個屬性或關(guān)聯(lián)映射中被合并來表達(dá),例如,一些奇異的連接條件。

          <many-to-one name="homeAddress" class="Address"
                  insert="false" update="false">
              <column name="person_id" not-null="true" length="10"/>
              <formula>'MAILING'</formula>
          </many-to-one>

          6.1.20.?引用(import)

          假設(shè)你的應(yīng)用程序有兩個同樣名字的持久化類,但是你不想在Hibernate查詢中使用他們的全限定名。除了依賴auto-import="true"以外,類也可以被顯式地“import(引用)”。你甚至可以引用沒有明確被映射的類和接口。

          <import class="java.lang.Object" rename="Universe"/>
          <import
                  class="ClassName"              (1)
                  rename="ShortName"             (2)
          />
          (1)

          class: 任何Java類的全限定名。

          (2)

          rename (可選 - 默認(rèn)為類的全限定名): 在查詢語句中可以使用的名字。

          6.1.21.?any

          這是屬性映射的又一種類型。<any> 映射元素定義了一種從多個表到類的多態(tài)關(guān)聯(lián)。這種類型的映射常常需要多于一個字段。第一個字段持有被關(guān)聯(lián)實(shí)體的類型,其他的字段持有標(biāo)識符。對這種類型的關(guān)聯(lián)來說,不可能指定一個外鍵約束,所以這當(dāng)然不是映射(多態(tài))關(guān)聯(lián)的通常的方式。你只應(yīng)該在非常特殊的情況下使用它(比如,審計log,用戶會話數(shù)據(jù)等等)。

          meta-type 屬性使得應(yīng)用程序能指定一個將數(shù)據(jù)庫字段的值映射到持久化類的自定義類型。這個持久化類包含有用id-type指定的標(biāo)識符屬性。 你必須指定從meta-type的值到類名的映射。

          <any name="being" id-type="long" meta-type="string">
              <meta-value value="TBL_ANIMAL" class="Animal"/>
              <meta-value value="TBL_HUMAN" class="Human"/>
              <meta-value value="TBL_ALIEN" class="Alien"/>
              <column name="table_name"/>
              <column name="id"/>
          </any>
          <any
                  name="propertyName"                      (1)
                  id-type="idtypename"                     (2)
                  meta-type="metatypename"                 (3)
                  cascade="cascade_style"                  (4)
                  access="field|property|ClassName"        (5)
                  optimistic-lock="true|false"             (6)
          >
                  <meta-value ... />
                  <meta-value ... />
                  .....
                  <column .... />
                  <column .... />
                  .....
          </any>
          (1)

          name: 屬性名

          (2)

          id-type: 標(biāo)識符類型

          (3)

          meta-type (可選 -默認(rèn)是 string): 允許辨別標(biāo)志(discriminator)映射的任何類型

          (4)

          cascade (可選 -默認(rèn)是none): 級聯(lián)的類型

          (5)

          access (可選 -默認(rèn)是 property): Hibernate 用來訪問屬性值的策略。

          (6)

          optimistic-lock (可選 -默認(rèn)是 true): 表明更新此組件是否需要獲取樂觀鎖。換句話說,當(dāng)這個屬性變臟時,是否增加版本號(Version)

          6.2.?Hibernate 的類型

          6.2.1.?實(shí)體(Entities)和值(values)

          為了理解很多與持久化服務(wù)相關(guān)的Java語言級對象的行為,我們需要把它們分為兩類:

          實(shí)體entity 獨(dú)立于任何持有實(shí)體引用的對象。與通常的Java模型相比,不再被引用的對象會被當(dāng)作垃圾收集掉。實(shí)體必須被顯式的保存和刪除(除非保存和刪除是從父實(shí)體向子實(shí)體引發(fā)的級聯(lián))。這和ODMG模型中關(guān)于對象通過可觸及保持持久性有一些不同——比較起來更加接近應(yīng)用程序?qū)ο笸ǔT谝粋€大系統(tǒng)中的使用方法。實(shí)體支持循環(huán)引用和交叉引用,它們也可以加上版本信息。

          一個實(shí)體的持久狀態(tài)包含指向其他實(shí)體和類型實(shí)例的引用。值可以是原始類型,集合(不是集合中的對象),組件或者特定的不可變對象。與實(shí)體不同,值(特別是集合和組件)是通過可觸及性來進(jìn)行持久化和刪除的。因?yàn)橹祵ο?和原始類型數(shù)據(jù))是隨著包含他們的實(shí)體而被持久化和刪除的,他們不能被獨(dú)立的加上版本信息。值沒有獨(dú)立的標(biāo)識,所以他們不能被兩個實(shí)體或者集合共享。

          直到現(xiàn)在,我們都一直使用術(shù)語“持久類”(persistent class)來代表實(shí)體。我們?nèi)匀粫@么做。 然而嚴(yán)格說來,不是所有的用戶自定義的,帶有持久化狀態(tài)的類都是實(shí)體。組件就是用戶自定義類,卻是值語義的。java.lang.String類型的java屬性也是值語義的。給了這個定義以后,我們可以說所有JDK提供的類型(類)都是值類型的語義,而用于自定義類型可能被映射為實(shí)體類型或值類型語義。采用哪種類型的語義取決于開發(fā)人員。在領(lǐng)域模型中,尋找實(shí)體類的一個好線索是共享引用指向這個類的單一實(shí)例,而組合或聚合通常被轉(zhuǎn)化為值類型。

          我們會在本文檔中重復(fù)碰到這兩個概念。

          挑戰(zhàn)在于將java類型系統(tǒng)(和開發(fā)者定義的實(shí)體和值類型)映射到 SQL/數(shù)據(jù)庫類型系統(tǒng)。Hibernate提供了連接兩個系統(tǒng)之間的橋梁:對于實(shí)體類型,我們使用<class>, <subclass> 等等。對于值類型,我們使用 <property>, <component> 及其他,通常跟隨著type屬性。這個屬性的值是Hibernate 的映射類型的名字。Hibernate提供了許多現(xiàn)成的映射(標(biāo)準(zhǔn)的JDK值類型)。你也可以編寫自己的映射類型并實(shí)現(xiàn)自定義的變換策略,隨后我們會看到這點(diǎn)。

          所有的Hibernate內(nèi)建類型,除了collections以外,都支持空(null)語義。

          6.2.2.?基本值類型

          The built-in basic mapping types may be roughly categorized into 內(nèi)建的 基本映射類型可以大致分為

          integer, long, short, float, double, character, byte, boolean, yes_no, true_false

          這些類型都對應(yīng)Java的原始類型或者其封裝類,來符合(特定廠商的)SQL 字段類型。boolean, yes_notrue_false都是Java 中boolean 或者java.lang.Boolean的另外說法。

          string

          java.lang.StringVARCHAR (或者 Oracle的 VARCHAR2)的映射。

          date, time, timestamp

          java.util.Date和其子類到SQL類型DATE, TIMETIMESTAMP (或等價類型)的映射。

          calendar, calendar_date

          java.util.Calendar 到SQL 類型TIMESTAMPDATE(或等價類型)的映射。

          big_decimal, big_integer

          java.math.BigDecimaljava.math.BigIntegerNUMERIC (或者 Oracle 的NUMBER類型)的映射。

          locale, timezone, currency

          java.util.Locale, java.util.TimeZonejava.util.CurrencyVARCHAR (或者 Oracle 的VARCHAR2類型)的映射. LocaleCurrency 的實(shí)例被映射為它們的ISO代碼。TimeZone的實(shí)例被影射為它的ID

          class

          java.lang.ClassVARCHAR (或者 Oracle 的VARCHAR2類型)的映射。Class被映射為它的全限定名。

          binary

          把字節(jié)數(shù)組(byte arrays)映射為對應(yīng)的 SQL二進(jìn)制類型。

          text

          把長Java字符串映射為SQL的CLOB或者TEXT類型。

          serializable

          把可序列化的Java類型映射到對應(yīng)的SQL二進(jìn)制類型。你也可以為一個并非默認(rèn)為基本類型的可序列化Java類或者接口指定Hibernate類型serializable

          clob, blob

          JDBC 類 java.sql.Clobjava.sql.Blob的映射。某些程序可能不適合使用這個類型,因?yàn)閎lob和clob對象可能在一個事務(wù)之外是無法重用的。(而且, 驅(qū)動程序?qū)@種類型的支持充滿著補(bǔ)丁和前后矛盾。)

          實(shí)體及其集合的唯一標(biāo)識可以是除了binaryblobclob之外的任何基礎(chǔ)類型。(聯(lián)合標(biāo)識也是允許的,后面會說到。)

          org.hibernate.Hibernate中,定義了基礎(chǔ)類型對應(yīng)的Type常量。比如,Hibernate.STRING代表string 類型。

          6.2.3.?自定義值類型

          開發(fā)者創(chuàng)建屬于他們自己的值類型也是很容易的。比如說,你可能希望持久化java.lang.BigInteger類型的屬性,持久化成為VARCHAR字段。Hibernate沒有內(nèi)置這樣一種類型。自定義類型能夠映射一個屬性(或集合元素)到不止一個數(shù)據(jù)庫表字段。比如說,你可能有這樣的Java屬性:getName()/setName(),這是java.lang.String類型的,對應(yīng)的持久化到三個字段:FIRST_NAME, INITIAL, SURNAME

          要實(shí)現(xiàn)一個自定義類型,可以實(shí)現(xiàn)org.hibernate.UserTypeorg.hibernate.CompositeUserType中的任一個,并且使用類型的Java全限定類名來定義屬性。請查看org.hibernate.test.DoubleStringType這個例子,看看它是怎么做的。

          <property name="twoStrings" type="org.hibernate.test.DoubleStringType">
              <column name="first_string"/>
              <column name="second_string"/>
          </property>

          注意使用<column>標(biāo)簽來把一個屬性映射到多個字段的做法。

          CompositeUserType, EnhancedUserType, UserCollectionType, 和 UserVersionType 接口為更特殊的使用方式提供支持。

          你甚至可以在一個映射文件中提供參數(shù)給一個UserType。 為了這樣做,你的UserType必須實(shí)現(xiàn)org.hibernate.usertype.ParameterizedType接口。為了給自定義類型提供參數(shù),你可以在映射文件中使用<type>元素。

          <property name="priority">
              <type name="com.mycompany.usertypes.DefaultValueIntegerType">
                  <param name="default">0</param>
              </type>
          </property>

          現(xiàn)在,UserType 可以從傳入的Properties對象中得到default 參數(shù)的值。

          如果你非常頻繁地使用某一UserType,可以為他定義一個簡稱。這可以通過使用 <typedef>元素來實(shí)現(xiàn)。Typedefs為一自定義類型賦予一個名稱,并且如果此類型是參數(shù)化的,還可以包含一系列默認(rèn)的參數(shù)值。

          <typedef class="com.mycompany.usertypes.DefaultValueIntegerType" name="default_zero">
              <param name="default">0</param>
          </typedef>
          <property name="priority" type="default_zero"/>

          也可以根據(jù)具體案例通過屬性映射中的類型參數(shù)覆蓋在typedef中提供的參數(shù)。

          盡管 Hibernate 內(nèi)建的豐富的類型和對組件的支持意味著你可能很少 需要使用自定義類型。不過,為那些在你的應(yīng)用中經(jīng)常出現(xiàn)的(非實(shí)體)類使用自定義類型也是一個好方法。例如,一個MonetaryAmount類使用CompositeUserType來映射是不錯的選擇,雖然他可以很容易地被映射成組件。這樣做的動機(jī)之一是抽象。使用自定義類型,以后假若你改變表示金額的方法時,它可以保證映射文件不需要修改。

          6.3.?SQL中引號包圍的標(biāo)識符

          你可通過在映射文檔中使用反向引號(`)把表名或者字段名包圍起來,以強(qiáng)制Hibernate在生成的SQL中把標(biāo)識符用引號包圍起來。Hibernate會使用相應(yīng)的SQLDialect(方言)來使用正確的引號風(fēng)格(通常是雙引號,但是在SQL Server中是括號,MySQL中是反向引號)。

          <class name="LineItem" table="`Line Item`">
              <id name="id" column="`Item Id`"/><generator class="assigned"/></id>
              <property name="itemNumber" column="`Item #`"/>
              ...
          </class>

          6.4.?其他元數(shù)據(jù)(Metadata)

          XML 并不適用于所有人, 因此有其他定義Hibernate O/R 映射元數(shù)據(jù)(metadata)的方法。

          6.4.1.?使用 XDoclet 標(biāo)記

          很多Hibernate使用者更喜歡使用XDoclet@hibernate.tags將映射信息直接嵌入到源代碼中。我們不會在本文檔中涉及這個方法,因?yàn)閲?yán)格說來,這屬于XDoclet的一部分。然而,我們包含了如下使用XDoclet映射的Cat類的例子。

          package eg;
          import java.util.Set;
          import java.util.Date;
          
          /**
           * @hibernate.class
           *  table="CATS"
           */
          public class Cat {
              private Long id; // identifier
              private Date birthdate;
              private Cat mother;
              private Set kittens
              private Color color;
              private char sex;
              private float weight;
          
              /*
               * @hibernate.id
               *  generator-class="native"
               *  column="CAT_ID"
               */
              public Long getId() {
                  return id;
              }
              private void setId(Long id) {
                  this.id=id;
              }
          
              /**
               * @hibernate.many-to-one
               *  column="PARENT_ID"
               */
              public Cat getMother() {
                  return mother;
              }
              void setMother(Cat mother) {
                  this.mother = mother;
              }
          
              /**
               * @hibernate.property
               *  column="BIRTH_DATE"
               */
              public Date getBirthdate() {
                  return birthdate;
              }
              void setBirthdate(Date date) {
                  birthdate = date;
              }
              /**
               * @hibernate.property
               *  column="WEIGHT"
               */
              public float getWeight() {
                  return weight;
              }
              void setWeight(float weight) {
                  this.weight = weight;
              }
          
              /**
               * @hibernate.property
               *  column="COLOR"
               *  not-null="true"
               */
              public Color getColor() {
                  return color;
              }
              void setColor(Color color) {
                  this.color = color;
              }
              /**
               * @hibernate.set
               *  inverse="true"
               *  order-by="BIRTH_DATE"
               * @hibernate.collection-key
               *  column="PARENT_ID"
               * @hibernate.collection-one-to-many
               */
              public Set getKittens() {
                  return kittens;
              }
              void setKittens(Set kittens) {
                  this.kittens = kittens;
              }
              // addKitten not needed by Hibernate
              public void addKitten(Cat kitten) {
                  kittens.add(kitten);
              }
          
              /**
               * @hibernate.property
               *  column="SEX"
               *  not-null="true"
               *  update="false"
               */
              public char getSex() {
                  return sex;
              }
              void setSex(char sex) {
                  this.sex=sex;
              }
          }

          參考Hibernate網(wǎng)站更多的Xdoclet和Hibernate的例子

          6.4.2.?使用 JDK 5.0 的注解(Annotation)

          JDK 5.0 在語言級別引入了 XDoclet 風(fēng)格的標(biāo)注,并且是類型安全的,在編譯期進(jìn)行檢查。這一機(jī)制比XDoclet的注解更為強(qiáng)大,有更好的工具和IDE支持。例如, IntelliJ IDEA,支持JDK 5.0注解的自動完成和語法高亮 。EJB規(guī)范的新修訂版(JSR-220)使用 JDK 5.0的注解作為entity beans的主要元數(shù)據(jù)(metadata)機(jī)制。Hibernate 3 實(shí)現(xiàn)了JSR-220 (the persistence API)的EntityManager,支持通過Hibernate Annotations包定義映射元數(shù)據(jù)。這個包作為單獨(dú)的部分下載,支持EJB3 (JSR-220)和Hibernate3的元數(shù)據(jù)。

          這是一個被注解為EJB entity bean 的POJO類的例子

          @Entity(access = AccessType.FIELD)
          public class Customer implements Serializable {
          
              @Id;
              Long id;
          
              String firstName;
              String lastName;
              Date birthday;
          
              @Transient
              Integer age;
          
              @Dependent
              private Address homeAddress;
          
              @OneToMany(cascade=CascadeType.ALL,
                         targetEntity="Order")
              @JoinColumn(name="CUSTOMER_ID")
              Set orders;
          
              // Getter/setter and business methods
          }

          注意:對 JDK 5.0 注解 (和 JSR-220)支持的工作仍然在進(jìn)行中,并未完成。

          第?7?章?集合類(Collections)映射

          7.1.?持久化集合類(Persistent collections)

          (譯者注:在閱讀本章的時候,以后整個手冊的閱讀過程中,我們都會面臨一個名詞方面的問題,那就是“集合”。"Collections"和"Set"在中文里對應(yīng)都被翻譯為“集合”,但是他們的含義很不一樣。Collections是一個超集,Set是其中的一種。大部分情況下,本譯稿中泛指的未加英文注明的“集合”,都應(yīng)當(dāng)理解為“Collections”。在有些二者同時出現(xiàn),可能造成混淆的地方,我們用“集合類”來特指“Collecions”,“集合(Set)”來指"Set",一般都會在后面的括號中給出英文。希望大家在閱讀時聯(lián)系上下文理解,不要造成誤解。 與此同時,“元素”一詞對應(yīng)的英文“element”,也有兩個不同的含義。其一為集合的元素,是內(nèi)存中的一個變量;另一含義則是XML文檔中的一個標(biāo)簽所代表的元素。也請注意區(qū)別。 本章中,特別是后半部分是需要反復(fù)閱讀才能理解清楚的。如果遇到任何疑問,請記住,英文版本的reference是惟一標(biāo)準(zhǔn)的參考資料。)

          Hibernate要求持久化集合值字段必須聲明為接口,比如:

          public class Product {
              private String serialNumber;
              private Set parts = new HashSet();
              
              public Set getParts() { return parts; }
              void setParts(Set parts) { this.parts = parts; }
              public String getSerialNumber() { return serialNumber; }
              void setSerialNumber(String sn) { serialNumber = sn; }
          }

          實(shí)際的接口可能是java.util.Set, java.util.Collection, java.util.List, java.util.Map, java.util.SortedSet, java.util.SortedMap 或者...任何你喜歡的類型!("任何你喜歡的類型" 代表你需要編寫 org.hibernate.usertype.UserCollectionType的實(shí)現(xiàn).)

          注意我們是如何用一個HashSet實(shí)例來初始化實(shí)例變量的.這是用于初始化新創(chuàng)建(尚未持久化)的類實(shí)例中集合值屬性的最佳方法。當(dāng)你持久化這個實(shí)例時——比如通過調(diào)用persist()——Hibernate 會自動把HashSet替換為Hibernate自己的Set實(shí)現(xiàn)。觀察下面的錯誤:

          Cat cat = new DomesticCat();
          Cat kitten = new DomesticCat();
          ....
          Set kittens = new HashSet();
          kittens.add(kitten);
          cat.setKittens(kittens);
          session.persist(cat);
          kittens = cat.getKittens(); //Okay, kittens collection is a Set
          (HashSet) cat.getKittens(); //Error!

          根據(jù)不同的接口類型,被Hibernate注射的持久化集合類的表現(xiàn)類似HashMap, HashSet, TreeMap, TreeSet or ArrayList

          集合類實(shí)例具有值類型的通常行為。當(dāng)被持久化對象引用后,他們會自動被持久化,當(dāng)不再被引用后,自動被刪除。假若實(shí)例被從一個持久化對象傳遞到另一個,它的元素可能從一個表轉(zhuǎn)移到另一個表。兩個實(shí)體不能共享同一個集合類實(shí)例的引用。因?yàn)榈讓雨P(guān)系數(shù)據(jù)庫模型的原因,集合值屬性無法支持空值語義;Hibernate對空的集合引用和空集合不加區(qū)別。

          你不需要過多的為此擔(dān)心。就如同你平時使用普通的Java集合類一樣來使用持久化集合類。只是要確認(rèn)你理解了雙向關(guān)聯(lián)的語義(后文討論)。

          7.2.?集合映射( Collection mappings )

          用于映射集合類的Hibernate映射元素取決于接口的類型。比如, <set> 元素用來映射Set類型的屬性。

          <class name="Product">
              <id name="serialNumber" column="productSerialNumber"/>
              <set name="parts">
                  <key column="productSerialNumber" not-null="true"/>
                  <one-to-many class="Part"/>
              </set>
          </class>

          除了<set>,還有<list>, <map>, <bag>, <array><primitive-array> 映射元素。<map>具有代表性:

          <map
              name="propertyName"                                         (1)
              table="table_name"                                          (2)
              schema="schema_name"                                        (3)
              lazy="true|false"                                           (4)
              inverse="true|false"                                        (5)
              cascade="all|none|save-update|delete|all-delete-orphan"     (6)
              sort="unsorted|natural|comparatorClass"                     (7)
              order-by="column_name asc|desc"                             (8)
              where="arbitrary sql where condition"                       (9)
              fetch="join|select|subselect"                               (10)
              batch-size="N"                                              (11)
              access="field|property|ClassName"                           (12)
              optimistic-lock="true|false"                                (13)
              node="element-name|."
              embed-xml="true|false"
          >
          
              <key .... />
              <map-key .... />
              <element .... />
          </map>
          (1)

          name 集合屬性的名稱

          (2)

          table (可選——默認(rèn)為屬性的名稱)這個集合表的名稱(不能在一對多的關(guān)聯(lián)關(guān)系中使用)

          (3)

          schema (可選) 表的schema的名稱, 他將覆蓋在根元素中定義的schema

          (4)

          lazy (可選--默認(rèn)為true) 可以用來關(guān)閉延遲加載,指定一直使用預(yù)先抓取(對數(shù)組不適用)

          (5)

          inverse (可選——默認(rèn)為false) 標(biāo)記這個集合作為雙向關(guān)聯(lián)關(guān)系中的方向一端。

          (6)

          cascade (可選——默認(rèn)為none) 讓操作級聯(lián)到子實(shí)體

          (7)

          sort(可選)指定集合的排序順序, 其可以為自然的(natural)或者給定一個用來比較的類。

          (8)

          order-by (可選, 僅用于jdk1.4) 指定表的字段(一個或幾個)再加上asc或者desc(可選), 定義Map,Set和Bag的迭代順序

          (9)

          where (可選) 指定任意的SQL where條件, 該條件將在重新載入或者刪除這個集合時使用(當(dāng)集合中的數(shù)據(jù)僅僅是所有可用數(shù)據(jù)的一個子集時這個條件非常有用)

          (10)

          fetch (可選, 默認(rèn)為select) 用于在外連接抓取、通過后續(xù)select抓取和通過后續(xù)subselect抓取之間選擇。

          (11)

          batch-size (可選, 默認(rèn)為1) 指定通過延遲加載取得集合實(shí)例的批處理塊大小("batch size")。

          (12)

          access(可選-默認(rèn)為屬性property):Hibernate取得屬性值時使用的策略

          (12)

          樂觀鎖 (可選 - 默認(rèn)為 true): 對集合的狀態(tài)的改變會是否導(dǎo)致其所屬的實(shí)體的版本增長。 (對一對多關(guān)聯(lián)來說,關(guān)閉這個屬性常常是有理的)

          7.2.1.?集合外鍵(Collection foreign keys)

          集合實(shí)例在數(shù)據(jù)庫中依靠持有集合的實(shí)體的外鍵加以辨別。此外鍵作為集合關(guān)鍵字段(collection key column)(或多個字段)加以引用。集合關(guān)鍵字段通過<key> 元素映射。

          在外鍵字段上可能具有非空約束。對于大多數(shù)集合來說,這是隱含的。對單向一對多關(guān)聯(lián)來說,外鍵字段默認(rèn)是可以為空的,因此你可能需要指明 not-null="true"

          <key column="productSerialNumber" not-null="true"/>

          外鍵約束可以使用ON DELETE CASCADE

          <key column="productSerialNumber" on-delete="cascade"/>

          <key> 元素的完整定義,請參閱前面的章節(jié)。

          7.2.2.?集合元素(Collection elements)

          集合幾乎可以包含任何其他的Hibernate類型,包括所有的基本類型、自定義類型、組件,當(dāng)然還有對其他實(shí)體的引用。存在一個重要的區(qū)別:位于集合中的對象可能是根據(jù)“值”語義來操作(其聲明周期完全依賴于集合持有者),或者它可能是指向另一個實(shí)體的引用,具有其自己的生命周期。在后者的情況下,被作為集合持有的狀態(tài)考慮的,只有兩個對象之間的“連接”。

          被包容的類型被稱為集合元素類型(collection element type)。集合元素通過<element><composite-element>映射,或在其是實(shí)體引用的時候,通過<one-to-many><many-to-many>映射。前兩種用于使用值語義映射元素,后兩種用于映射實(shí)體關(guān)聯(lián)。

          7.2.3.?索引集合類(Indexed collections)

          所有的集合映射,除了set和bag語義的以外,都需要指定一個集合表的索引字段(index column)——用于對應(yīng)到數(shù)組索引,或者List的索引,或者Map的關(guān)鍵字。通過<map-key>,Map 的索引可以是任何基礎(chǔ)類型;若通過<map-key-many-to-many>,它也可以是一個實(shí)體引用;若通過<composite-map-key>,它還可以是一個組合類型。數(shù)組或列表的索引必須是integer類型,并且使用 <list-index>元素定義映射。被映射的字段包含有順序排列的整數(shù)(默認(rèn)從0開始)。

          <map-key 
                  column="column_name"                (1)
                  formula="any SQL expression"        (2)
                  type="type_name"                    (3)
                  node="@attribute-name"
                  length="N"/>
          (1)

          column(可選):保存集合索引值的字段名。

          (2)

          formula (可選): 用于計算map關(guān)鍵字的SQL公式

          (3)

          type (可選,默認(rèn)為整型integer):集合索引的類型。

          <map-key-many-to-many
                  column="column_name"                (1)
                  formula="any SQL expression"        (2)(3)
                  class="ClassName"
          />
          (1)

          column(可選):集合索引值中外鍵字段的名稱

          (2)

          formula (可選): 用于計算map關(guān)鍵字的外鍵的SQL公式

          (3)

          class (必需):集合的索引使用的實(shí)體類。

          假若你的表沒有一個索引字段,當(dāng)你仍然希望使用List作為屬性類型,你應(yīng)該把此屬性映射為Hibernate <bag>。從數(shù)據(jù)庫中獲取的時候,bag不維護(hù)其順序,但也可選擇性的進(jìn)行排序。

          從集合類可以產(chǎn)生很大一部分映射,覆蓋了很多常見的關(guān)系模型。我們建議你試驗(yàn)schema生成工具,來體會一下不同的映射聲明是如何被翻譯為數(shù)據(jù)庫表的。

          7.2.4.?值集合于多對多關(guān)聯(lián)(Collections of values and many-to-many associations)

          任何值集合或者多對多關(guān)聯(lián)需要專用的具有一個或多個外鍵字段的collection table、一個或多個collection element column,以及還可能有一個或多個索引字段。

          對于一個值集合, 我們使用<element>標(biāo)簽。

          <element
                  column="column_name"                     (1)
                  formula="any SQL expression"             (2)
                  type="typename"                          (3)
                  length="N"
                  precision="N"
                  scale="N"
                  not-null="true|false"
                  unique="true|false"
                  node="element-name"
          />
          (1)

          column(可選):保存集合元素值的字段名。

          (2)

          formula (可選): 用于計算元素的SQL公式

          (3)

          type (必需):集合元素的類型

          多對多關(guān)聯(lián)(many-to-many association) 使用 <many-to-many>元素定義.

          <many-to-many
                  column="column_name"                               (1)
                  formula="any SQL expression"                       (2)
                  class="ClassName"                                  (3)
                  fetch="select|join"                                (4)
                  unique="true|false"                                (5)
                  not-found="ignore|exception"                       (6)
                  entity-name="EntityName"                           (7)
                  node="element-name"
                  embed-xml="true|false"
              />
          (1)

          column(可選): 這個元素的外鍵關(guān)鍵字段名

          (2)

          formula (可選): 用于計算元素外鍵值的SQL公式.

          (3)

          class (必需): 關(guān)聯(lián)類的名稱

          (3)

          outer-join (可選 - 默認(rèn)為auto): 在Hibernate系統(tǒng)參數(shù)中hibernate.use_outer_join被打開的情況下,該參數(shù)用來允許使用outer join來載入此集合的數(shù)據(jù)。

          (4)

          為此關(guān)聯(lián)打開外連接抓取或者后續(xù)select抓取。這是特殊情況;對于一個實(shí)體及其指向其他實(shí)體的多對多關(guān)聯(lián)進(jìn)全預(yù)先抓取(使用一條單獨(dú)的SELECT),你不僅需要對集合自身打開join,也需要對<many-to-many>這個內(nèi)嵌元素打開此屬性。

          (5)

          對外鍵字段允許DDL生成的時候生成一個惟一約束。這使關(guān)聯(lián)變成了一個高效的一對多關(guān)聯(lián)。(此句存疑:原文為This makes the association multiplicity effectively one to many.)

          (6)

          not-found (可選 - 默認(rèn)為 exception): 指明引用的外鍵中缺少某些行該如何處理: ignore 會把缺失的行作為一個空引用處理。

          (7)

          entity-name (可選): 被關(guān)聯(lián)的類的實(shí)體名,作為class的替代。

          例子:首先, 一組字符串:

          <set name="names" table="NAMES">
              <key column="GROUPID"/>
              <element column="NAME" type="string"/>
          </set>

          包含一組整數(shù)的bag(還設(shè)置了order-by參數(shù)指定了迭代的順序):

          <bag name="sizes" 
                  table="item_sizes" 
                  order-by="size asc">
              <key column="item_id"/>
              <element column="size" type="integer"/>
          </bag>

          一個實(shí)體數(shù)組,在這個案例中是一個多對多的關(guān)聯(lián)(注意這里的實(shí)體是自動管理生命周期的對象(lifecycle objects),cascade="all"):

          <array name="addresses" 
                  table="PersonAddress" 
                  cascade="persist">
              <key column="personId"/>
              <list-index column="sortOrder"/>
              <many-to-many column="addressId" class="Address"/>
          </array>

          一個map,通過字符串的索引來指明日期:

          <map name="holidays" 
                  table="holidays" 
                  schema="dbo" 
                  order-by="hol_name asc">
              <key column="id"/>
              <map-key column="hol_name" type="string"/>
              <element column="hol_date" type="date"/>
          </map>

          一個組件的列表:(下一章討論)

          <list name="carComponents" 
                  table="CarComponents">
              <key column="carId"/>
              <list-index column="sortOrder"/>
              <composite-element class="CarComponent">
                  <property name="price"/>
                  <property name="type"/>
                  <property name="serialNumber" column="serialNum"/>
              </composite-element>
          </list>

          7.2.5.?一對多關(guān)聯(lián)(One-to-many Associations)

          一對多關(guān)聯(lián) 通過外鍵 連接兩個類對應(yīng)的表,而沒有中間集合表。 這個關(guān)系模型失去了一些Java集合的語義:

          • 一個被包含的實(shí)體的實(shí)例只能被包含在一個集合的實(shí)例中

          • 一個被包含的實(shí)體的實(shí)例只能對應(yīng)于集合索引的一個值中

          一個從ProductPart的關(guān)聯(lián)需要關(guān)鍵字字段,可能還有一個索引字段指向Part所對應(yīng)的表。 <one-to-many>標(biāo)記指明了一個一對多的關(guān)聯(lián)。

          <one-to-many 
                  class="ClassName"                                  (1)
                  not-found="ignore|exception"                       (2)
                  entity-name="EntityName"                           (3)
                  node="element-name"
                  embed-xml="true|false"
              />
          (1)

          class(必須):被關(guān)聯(lián)類的名稱。

          (2)

          not-found (可選 - 默認(rèn)為exception): 指明若緩存的標(biāo)示值關(guān)聯(lián)的行缺失,該如何處理: ignore 會把缺失的行作為一個空關(guān)聯(lián)處理。

          (3)

          entity-name (可選): 被關(guān)聯(lián)的類的實(shí)體名,作為class的替代。

          例子

          <set name="bars">
              <key column="foo_id"/>
              <one-to-many class="org.hibernate.Bar"/>
          </set>

          注意:<one-to-many>元素不需要定義任何字段。 也不需要指定表名。

          重要提示 :如果一對多關(guān)聯(lián)中的外鍵字段定義成NOT NULL,你必須把<key>映射聲明為not-null="true",或者使用雙向關(guān)聯(lián),并且標(biāo)明inverse="true"。參閱本章后面關(guān)于雙向關(guān)聯(lián)的討論。

          下面的例子展示一個Part實(shí)體的map,把name作為關(guān)鍵字。( partNamePart的持久化屬性)。注意其中的基于公式的索引的用法。

          <map name="parts"
                  cascade="all">
              <key column="productId" not-null="true"/>
              <map-key formula="partName"/>
              <one-to-many class="Part"/>
          </map>

          7.3.?高級集合映射(Advanced collection mappings)

          7.3.1.?有序集合(Sorted collections)

          Hibernate支持實(shí)現(xiàn)java.util.SortedMapjava.util.SortedSet的集合。你必須在映射文件中指定一個比較器:

          <set name="aliases" 
                      table="person_aliases" 
                      sort="natural">
              <key column="person"/>
              <element column="name" type="string"/>
          </set>
          
          <map name="holidays" sort="my.custom.HolidayComparator">
              <key column="year_id"/>
              <map-key column="hol_name" type="string"/>
              <element column="hol_date" type="date"/>
          </map>

          sort屬性中允許的值包括unsorted,natural和某個實(shí)現(xiàn)了java.util.Comparator的類的名稱。

          分類集合的行為事實(shí)上象java.util.TreeSet或者java.util.TreeMap

          如果你希望數(shù)據(jù)庫自己對集合元素排序,可以利用set,bag或者map映射中的order-by屬性。這個解決方案只能在jdk1.4或者更高的jdk版本中才可以實(shí)現(xiàn)(通過LinkedHashSet或者 LinkedHashMap實(shí)現(xiàn))。 它是在SQL查詢中完成排序,而不是在內(nèi)存中。

          <set name="aliases" table="person_aliases" order-by="lower(name) asc">
              <key column="person"/>
              <element column="name" type="string"/>
          </set>
          
          <map name="holidays" order-by="hol_date, hol_name">
              <key column="year_id"/>
              <map-key column="hol_name" type="string"/>
              <element column="hol_date" type="date"/>
          </map>

          注意: 這個order-by屬性的值是一個SQL排序子句而不是HQL的!

          關(guān)聯(lián)還可以在運(yùn)行時使用集合filter()根據(jù)任意的條件來排序。

          sortedUsers = s.createFilter( group.getUsers(), "order by this.name" ).list();

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

          雙向關(guān)聯(lián) 允許通過關(guān)聯(lián)的任一端訪問另外一端。在Hibernate中, 支持兩種類型的雙向關(guān)聯(lián):

          一對多(one-to-many)

          Set或者bag值在一端, 單獨(dú)值(非集合)在另外一端

          多對多(many-to-many)

          兩端都是set或bag值

          要建立一個雙向的多對多關(guān)聯(lián),只需要映射兩個many-to-many關(guān)聯(lián)到同一個數(shù)據(jù)庫表中,并再定義其中的一端為inverse(使用哪一端要根據(jù)你的選擇,但它不能是一個索引集合)。

          這里有一個many-to-many的雙向關(guān)聯(lián)的例子;每一個category都可以有很多items,每一個items可以屬于很多categories:

          <class name="Category">
              <id name="id" column="CATEGORY_ID"/>
              ...
              <bag name="items" table="CATEGORY_ITEM">
                  <key column="CATEGORY_ID"/>
                  <many-to-many class="Item" column="ITEM_ID"/>
              </bag>
          </class>
          
          <class name="Item">
              <id name="id" column="CATEGORY_ID"/>
              ...
          
              <!-- inverse end -->
              <bag name="categories" table="CATEGORY_ITEM" inverse="true">
                  <key column="ITEM_ID"/>
                  <many-to-many class="Category" column="CATEGORY_ID"/>
              </bag>
          </class>

          如果只對關(guān)聯(lián)的反向端進(jìn)行了改變,這個改變不會被持久化。 這表示Hibernate為每個雙向關(guān)聯(lián)在內(nèi)存中存在兩次表現(xiàn),一個從A連接到B,另一個從B連接到A。如果你回想一下Java對象模型,我們是如何在Java中創(chuàng)建多對多關(guān)系的,這可以讓你更容易理解:

          category.getItems().add(item);          // The category now "knows" about the relationship
          item.getCategories().add(category);     // The item now "knows" about the relationship
          
          session.persist(item);                   // The relationship won''t be saved!
          session.persist(category);               // The relationship will be saved

          非反向端用于把內(nèi)存中的表示保存到數(shù)據(jù)庫中。

          要建立一個一對多的雙向關(guān)聯(lián),你可以通過把一個一對多關(guān)聯(lián),作為一個多對一關(guān)聯(lián)映射到到同一張表的字段上,并且在"多"的那一端定義inverse="true"

          <class name="Parent">
              <id name="id" column="parent_id"/>
              ....
              <set name="children" inverse="true">
                  <key column="parent_id"/>
                  <one-to-many class="Child"/>
              </set>
          </class>
          
          <class name="eg.Child">
              <id name="id" column="id"/>
              ....
              <many-to-one name="parent" 
                  class="Parent" 
                  column="parent_id"
                  not-null="true"/>
          </class>

          在“一”這一端定義inverse="true"不會影響級聯(lián)操作,二者是正交的概念!

          7.3.3.?三重關(guān)聯(lián)(Ternary associations)

          有三種可能的途徑來映射一個三重關(guān)聯(lián)。第一種是使用一個Map,把一個關(guān)聯(lián)作為其索引:

          <map name="contracts">
              <key column="employer_id" not-null="true"/>
              <map-key-many-to-many column="employee_id" class="Employee"/>
              <one-to-many class="Contract"/>
          </map>
          <map name="connections">
              <key column="incoming_node_id"/>
              <map-key-many-to-many column="outgoing_node_id" class="Node"/>
              <many-to-many column="connection_id" class="Connection"/>
          </map>

          第二種方法是簡單的把關(guān)聯(lián)重新建模為一個實(shí)體類。這使我們最經(jīng)常使用的方法。

          最后一種選擇是使用復(fù)合元素,我們會在后面討論

          7.3.4.?使用<idbag>

          如果你完全信奉我們對于“聯(lián)合主鍵(composite keys)是個壞東西”,和“實(shí)體應(yīng)該使用(無機(jī)的)自己生成的代用標(biāo)識符(surrogate keys)”的觀點(diǎn),也許你會感到有一些奇怪,我們目前為止展示的多對多關(guān)聯(lián)和值集合都是映射成為帶有聯(lián)合主鍵的表的!現(xiàn)在,這一點(diǎn)非常值得爭辯;看上去一個單純的關(guān)聯(lián)表并不能從代用標(biāo)識符中獲得什么好處(雖然使用組合值的集合可能會獲得一點(diǎn)好處)。不過,Hibernate提供了一個(一點(diǎn)點(diǎn)試驗(yàn)性質(zhì)的)功能,讓你把多對多關(guān)聯(lián)和值集合應(yīng)得到一個使用代用標(biāo)識符的表去。

          <idbag> 屬性讓你使用bag語義來映射一個List (或Collection)。

          <idbag name="lovers" table="LOVERS">
              <collection-id column="ID" type="long">
                  <generator class="sequence"/>
              </collection-id>
              <key column="PERSON1"/>
              <many-to-many column="PERSON2" class="eg.Person" outer-join="true"/>
          </idbag>

          你可以理解,<idbag>人工的id生成器,就好像是實(shí)體類一樣!集合的每一行都有一個不同的人造關(guān)鍵字。但是,Hibernate沒有提供任何機(jī)制來讓你取得某個特定行的人造關(guān)鍵字。

          注意<idbag>的更新性能要比普通的<bag>高得多!Hibernate可以有效的定位到不同的行,分別進(jìn)行更新或刪除工作,就如同處理一個list, map或者set一樣。

          在目前的實(shí)現(xiàn)中,還不支持使用identity標(biāo)識符生成器策略來生成<idbag>集合的標(biāo)識符。

          7.4.?集合例子(Collection example)

          在前面的幾個章節(jié)的確非常令人迷惑。 因此讓我們來看一個例子。這個類:

          package eg;
          import java.util.Set;
          
          public class Parent {
              private long id;
              private Set children;
          
              public long getId() { return id; }
              private void setId(long id) { this.id=id; }
          
              private Set getChildren() { return children; }
              private void setChildren(Set children) { this.children=children; }
          
              ....
              ....
          }

          這個類有一個Child的實(shí)例集合。如果每一個子實(shí)例至多有一個父實(shí)例, 那么最自然的映射是一個one-to-many的關(guān)聯(lián)關(guān)系:

          <hibernate-mapping>
          
              <class name="Parent">
                  <id name="id">
                      <generator class="sequence"/>
                  </id>
                  <set name="children">
                      <key column="parent_id"/>
                      <one-to-many class="Child"/>
                  </set>
              </class>
          
              <class name="Child">
                  <id name="id">
                      <generator class="sequence"/>
                  </id>
                  <property name="name"/>
              </class>
          
          </hibernate-mapping>

          在以下的表定義中反應(yīng)了這個映射關(guān)系:

          create table parent ( id bigint not null primary key )
          create table child ( id bigint not null primary key, name varchar(255), parent_id bigint )
          alter table child add constraint childfk0 (parent_id) references parent

          如果父親是必須的, 那么就可以使用雙向one-to-many的關(guān)聯(lián)了:

          <hibernate-mapping>
          
              <class name="Parent">
                  <id name="id">
                      <generator class="sequence"/>
                  </id>
                  <set name="children" inverse="true">
                      <key column="parent_id"/>
                      <one-to-many class="Child"/>
                  </set>
              </class>
          
              <class name="Child">
                  <id name="id">
                      <generator class="sequence"/>
                  </id>
                  <property name="name"/>
                  <many-to-one name="parent" class="Parent" column="parent_id" not-null="true"/>
              </class>
          
          </hibernate-mapping>

          請注意NOT NULL的約束:

          create table parent ( id bigint not null primary key )
          create table child ( id bigint not null
                               primary key,
                               name varchar(255),
                               parent_id bigint not null )
          alter table child add constraint childfk0 (parent_id) references parent

          另外,如果你絕對堅持這個關(guān)聯(lián)應(yīng)該是單向的,你可以對<key>映射聲明NOT NULL約束:

          <hibernate-mapping>
          
              <class name="Parent">
                  <id name="id">
                      <generator class="sequence"/>
                  </id>
                  <set name="children">
                      <key column="parent_id" not-null="true"/>
                      <one-to-many class="Child"/>
                  </set>
              </class>
          
              <class name="Child">
                  <id name="id">
                      <generator class="sequence"/>
                  </id>
                  <property name="name"/>
              </class>
          
          </hibernate-mapping>

          另外一方面,如果一個子實(shí)例可能有多個父實(shí)例, 那么就應(yīng)該使用many-to-many關(guān)聯(lián):

          <hibernate-mapping>
          
              <class name="Parent">
                  <id name="id">
                      <generator class="sequence"/>
                  </id>
                  <set name="children" table="childset">
                      <key column="parent_id"/>
                      <many-to-many class="Child" column="child_id"/>
                  </set>
              </class>
          
              <class name="Child">
                  <id name="id">
                      <generator class="sequence"/>
                  </id>
                  <property name="name"/>
              </class>
          
          </hibernate-mapping>

          表定義:

          create table parent ( id bigint not null primary key )
          create table child ( id bigint not null primary key, name varchar(255) )
          create table childset ( parent_id bigint not null,
                                  child_id bigint not null,
                                  primary key ( parent_id, child_id ) )
          alter table childset add constraint childsetfk0 (parent_id) references parent
          alter table childset add constraint childsetfk1 (child_id) references child

          更多的例子,以及一個完整的父/子關(guān)系映射的排練,請參閱第?22?章 示例:父子關(guān)系(Parent Child Relationships).

          甚至可能出現(xiàn)更加復(fù)雜的關(guān)聯(lián)映射,我們會在下一章中列出所有可能性。

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

          8.1.?介紹

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

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

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

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

          8.2.1.?多對一(many to one)

          單向many-to-one關(guān)聯(liá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.?一對一(one to one)

          基于外鍵關(guān)聯(lián)的單向一對一關(guān)聯(lián) 單向多對一關(guān)聯(lián)幾乎是一樣的。唯一的不同就是單向一對一關(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)的單向一對一關(guān)聯(lián) 通常使用一個特定的id生成器。(請注意,在這個例子中我們掉換了關(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.?一對多(one to many)

          基于外鍵關(guān)聯(lián)的單向一對多關(guān)聯(liá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)為對于這種關(guān)聯(lián)關(guān)系最好使用連接表。

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

          8.3.1.?一對多(one to many)

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

          <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.?多對一(many to one)

          基于連接表的單向多對一關(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.?一對一(one to one)

          基于連接表的單向一對一關(guān)聯(liá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.?多對多(many to many)

          最后,還有 單向多對多關(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.?一對多(one to many) / 多對一(many to one)

          雙向多對一關(guān)聯(liá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.?一對一(one to one)

          基于外鍵關(guān)聯(lián)的雙向一對一關(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>
             <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)的一對一關(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.?一對多(one to many) /多對一( many to one)

          基于連接表的雙向一對多關(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.?一對一(one to one)

          基于連接表的雙向一對一關(guān)聯(liá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.?多對多(many to many)

          最后,還有 雙向多對多關(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 這個概念在Hibernate中幾處不同的地方為了不同的目的被重復(fù)使用.

          9.1.?依賴對象(Dependent objects)

          Component是一個被包含的對象,它作為值類型被持久化,而非一個被引用的實(shí)體。“component(組件)”這一術(shù)語指的是面向?qū)ο蟮暮铣筛拍睿ǘ⒉皇窍到y(tǒng)構(gòu)架層次上的組件的概念)舉個例子, 你可以對人(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)的一個組成部分。需要注意的是:需要對姓名 的持久化屬性定義getter和setter方法,但是不需要實(shí)現(xiàn)任何的接口或申明標(biāo)識符字段。

          以下是這個例子的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等字段。

          就像所有的值類型一樣, Component不支持共享引用。 換句話說,兩個人可能重名,但是兩個person對象應(yīng)該包含兩個獨(dú)立的name對象,只不過是具有“同樣”的值。 Component的值為空從語義學(xué)上來講是專有的(ad hoc)。 每當(dāng) 重新加載一個包含組件的對象,如果component的所有字段為空,那么將Hibernate將假定整個component為 空。對于絕大多數(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è)計細(xì)致(fine-grained)的對象模型。

          <component> 元素還允許有 <parent>子元素 ,用來表明component類中的一個屬性返回包含它的實(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)的依賴對象

          Hibernate支持component的集合(例如: 一個元素是“姓名”這種類型的數(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>

          注意,如果你決定定義一個元素是聯(lián)合元素的Set,正確地實(shí)現(xiàn)equals()hashCode()是非常重要的。

          組合元素可以包含component但是不能包含集合。如果你的組合元素自身包含component, 必須使用<nested-composite-element>標(biāo)簽。這是一個相當(dāng)特殊的案例 - 組合元素的集合自身可以包含component。 這個時候你就應(yīng)該考慮一下使用one-to-many關(guān)聯(lián)是否會更恰當(dāng)。 嘗試對這個組合元素重新建模為一個實(shí)體-但是需要注意的是,雖然Java模型和重新建模前 是一樣的,關(guān)系模型和持久性語義上仍然存在輕微的區(qū)別。

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

          組合元素有個特別的案例,是組合元素可以包含一個<many-to-one> 元素。類似這樣的映射允許你映射一個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.) 接下來的的例子是從OrderItem的一個多對多的關(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)然,在另一方面,無法存在指向purchase的關(guān)聯(lián),因此不能實(shí)現(xiàn)雙向關(guān)聯(lián)查詢。記住組建是值類型,并且不允許共享關(guān)聯(lián)。單個Purchase 可以放在包含Order的集合中,但它不能同時被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>元素允許你映射一個Component類作為Map的key, 但是你必須確定你正確的在這個類中重寫了hashCode()equals()方法。

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

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

          • 它必須實(shí)現(xiàn)java.io.Serializable接口

          • 它必須重新實(shí)現(xiàn)equals()hashCode()方法, 始終和組合關(guān)鍵字在數(shù)據(jù)庫中的概念保持一致

          注意:在Hibernate3中,第二種要求并非是Hibernate強(qiáng)制必須的。但最好這樣做。

          你不能使用一個IdentifierGenerator產(chǎn)生組合關(guān)鍵字。作為替代應(yīng)用程序必須分配它自己的標(biāo)識符。

          使用<composite-id> 標(biāo)簽(并且內(nèi)嵌<key-property>元素)代替通常的<id>標(biāo)簽。 比如,OrderLine類具有一個依賴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>

          (注意在各個地方<column>標(biāo)簽都是column屬性的替代寫法。)

          指向OrderLine多對多關(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本身擁有一個集合,它也具有組合外鍵。

          <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.?動態(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í)屬性的能力.利用一個DOM解析器,是有可能在運(yùn)行時刻操作映射文件的。 更好的是,你可以通過Configuration對象來訪問(或者修改)Hibernate的運(yùn)行時元模型。

          第?10?章?繼承映射(Inheritance Mappings)

          10.1.? 三種策略

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

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

          • 每個子類一張表(table per subclass)

          • 每個具體類一張表(table per concrete class)

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

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

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

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

          假設(shè)我們有接口Payment和它的幾個實(shí)現(xiàn)類: CreditCardPayment, CashPayment, 和ChequePayment。則“每個類分層結(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>

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

          10.1.2.?每個子類一張表(Table per subclass)

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

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

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

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

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

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

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

          <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的 關(guān)聯(lián)是使用<many-to-one>進(jìn)行映射的。

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

          10.1.5.?每個具體類一張表(Table per concrete class)

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

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

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

          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>

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

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

          對于這種映射策略而言,通常用<any>來實(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)和其他繼承映射混合使用

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

          <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執(zhí)行查詢 ——如from Payment—— Hibernate 自動返回CreditCardPayment(和它的子類,因?yàn)?它們也實(shí)現(xiàn)了接口Payment)、 CashPaymentChequepayment的實(shí)例, 但不返回NonelectronicTransaction的實(shí)例。

          10.2.?限制

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

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

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

          繼承策略(Inheritance strategy) 多態(tài)多對一 多態(tài)一對一 多態(tài)一對多 多態(tài)多對多 多態(tài) load()/get() 多態(tài)查詢 多態(tài)連接(join) 外連接(Outer join)抓取
          每個類分層結(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 支持
          每個子類一張表 <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 支持
          每個具體類一張表(隱式多態(tài)) <any> 不支持 不支持 <many-to-any> s.createCriteria(Payment.class).add( Restrictions.idEq(id) ).uniqueResult() from Payment p 不支持 不支持

          ?

          posted on 2006-06-28 14:37 程序-人生 閱讀(8215) 評論(0)  編輯  收藏

          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 高要市| 江口县| 金坛市| 介休市| 安宁市| 德钦县| 平利县| 思茅市| 仁布县| 黄冈市| 南城县| 乌拉特前旗| 清远市| 沅陵县| 土默特右旗| 睢宁县| 礼泉县| 临颍县| 张家口市| 华宁县| 太仆寺旗| 西华县| 横山县| 牙克石市| 全南县| 锦屏县| 西丰县| 招远市| 太康县| 陆良县| 宁蒗| 宁武县| 太仆寺旗| 勃利县| 孟津县| 安徽省| 郎溪县| 海安县| 新乐市| 南木林县| 大竹县|