對(duì)象和關(guān)系數(shù)據(jù)庫(kù)之間的映射通常是用一個(gè)XML文檔(XML document)來(lái)定義的。這個(gè)映射文檔被設(shè)計(jì)為易讀的, 并且可以手工修改。映射語(yǔ)言是以Java為中心,這意味著映射文檔是按照持久化類(lèi)的定義來(lái)創(chuàng)建的, 而非表的定義。
請(qǐng)注意,雖然很多Hibernate用戶選擇手寫(xiě)XML映射文檔,但也有一些工具可以用來(lái)生成映射文檔, 包括XDoclet,Middlegen和AndroMDA。
讓我們從一個(gè)映射的例子開(kāi)始:
<?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)在開(kāi)始討論映射文檔的內(nèi)容。我們只描述Hibernate在運(yùn)行時(shí)用到的文檔元素和屬性。 映射文檔還包括一些額外的可選屬性和元素,它們?cè)谑褂胹chema導(dǎo)出工具的時(shí)候會(huì)影響導(dǎo)出的數(shù)據(jù)庫(kù)schema結(jié)果。 (比如, not-null 屬性。)
所有的XML映射都需要定義如上所示的doctype。DTD可以從上述URL中獲取, 從hibernate-x.x.x/src/net/sf/hibernate目錄中、 或hibernate.jar文件中找到。Hibernate總是會(huì)首先在它的classptah中搜索DTD文件。 如果你發(fā)現(xiàn)它是通過(guò)連接Internet查找DTD文件,就對(duì)照你的classpath目錄檢查XML文件里的DTD聲明。
這個(gè)元素包括一些可選的屬性。schema和catalog屬性, 指明了這個(gè)映射所連接(refer)的表所在的schema和/或catalog名稱(chēng)。 假若指定了這個(gè)屬性,表名會(huì)加上所指定的schema和catalog的名字?jǐn)U展為全限定名。假若沒(méi)有指定,表名就不會(huì)使用全限定名。 default-cascade指定了未明確注明cascade屬性的Java屬性和 集合類(lèi)Hibernate會(huì)采取什么樣的默認(rèn)級(jí)聯(lián)風(fēng)格。auto-import屬性默認(rèn)讓我們?cè)诓樵冋Z(yǔ)言中可以使用 非全限定名的類(lèi)名。
<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ù)庫(kù)schema的名稱(chēng)。 |
(2) |
catalog (可選): 數(shù)據(jù)庫(kù)catalog的名稱(chēng)。 |
(3) |
default-cascade (可選 - 默認(rèn)為 none): 默認(rèn)的級(jí)聯(lián)風(fēng)格。 |
(4) |
default-access (可選 - 默認(rèn)為 property): Hibernate用來(lái)訪問(wèn)屬性的策略。可以通過(guò)實(shí)現(xiàn)PropertyAccessor接口 自定義。 |
(5) |
default-lazy (可選 - 默認(rèn)為 true): 指定了未明確注明lazy屬性的Java屬性和集合類(lèi), Hibernate會(huì)采取什么樣的默認(rèn)加載風(fēng)格。 |
(6) |
auto-import (可選 - 默認(rèn)為 true): 指定我們是否可以在查詢語(yǔ)言中使用非全限定的類(lèi)名(僅限于本映射文件中的類(lèi))。 |
(7) |
package (可選): 指定一個(gè)包前綴,如果在映射文檔中沒(méi)有指定全限定的類(lèi)名, 就使用這個(gè)作為包名。 |
假若你有兩個(gè)持久化類(lèi),它們的非全限定名是一樣的(就是兩個(gè)類(lèi)的名字一樣,所在的包不一樣--譯者注), 你應(yīng)該設(shè)置auto-import="false"。假若說(shuō)你把一個(gè)“import過(guò)”的名字同時(shí)對(duì)應(yīng)兩個(gè)類(lèi), Hibernate會(huì)拋出一個(gè)異常。
注意hibernate-mapping 元素允許你嵌套多個(gè)如上所示的 <class>映射。但是最好的做法(也許一些工具需要的)是一個(gè) 持久化類(lèi)(或一個(gè)類(lèi)的繼承層次)對(duì)應(yīng)一個(gè)映射文件,并以持久化的超類(lèi)名稱(chēng)命名,例如: Cat.hbm.xml, Dog.hbm.xml,或者如果使用繼承,Animal.hbm.xml。
你可以使用class元素來(lái)定義一個(gè)持久化類(lèi):
<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 (可選): 持久化類(lèi)(或者接口)的Java全限定名。 如果這個(gè)屬性不存在,Hibernate將假定這是一個(gè)非POJO的實(shí)體映射。 |
(2) |
table (可選 - 默認(rèn)是類(lèi)的非全限定名): 對(duì)應(yīng)的數(shù)據(jù)庫(kù)表名。 |
(3) |
discriminator-value (可選 - 默認(rèn)和類(lèi)名一樣): 一個(gè)用于區(qū)分不同的子類(lèi)的值,在多態(tài)行為時(shí)使用。它可以接受的值包括 null 和 not null。 |
(4) |
mutable (可選,默認(rèn)值為true): 表明該類(lèi)的實(shí)例是可變的或者可變的。 |
(5) |
schema (可選): 覆蓋在根<hibernate-mapping>元素中指定的schema名字。 |
(6) |
catalog (可選): 覆蓋在根<hibernate-mapping>元素中指定的catalog名字。 |
(7) |
proxy (可選): 指定一個(gè)接口,在延遲裝載時(shí)作為代理使用。 你可以在這里使用該類(lèi)自己的名字。 |
(8) |
dynamic-update (可選, 默認(rèn)為 false): 指定用于UPDATE 的SQL將會(huì)在運(yùn)行時(shí)動(dòng)態(tài)生成,并且只更新那些改變過(guò)的字段。 |
(9) |
dynamic-insert (可選, 默認(rèn)為 false): 指定用于INSERT的 SQL 將會(huì)在運(yùn)行時(shí)動(dòng)態(tài)生成,并且只包含那些非空值字段。 |
(10) |
select-before-update (可選, 默認(rèn)為 false): 指定Hibernate除非確定對(duì)象真正被修改了(如果該值為true-譯注),否則不會(huì)執(zhí)行SQL UPDATE操作。在特定場(chǎng)合(實(shí)際上,它只在一個(gè)瞬時(shí)對(duì)象(transient object)關(guān)聯(lián)到一個(gè) 新的session中時(shí)執(zhí)行的update()中生效),這說(shuō)明Hibernate會(huì)在UPDATE 之前執(zhí)行一次額外的SQL SELECT操作,來(lái)決定是否應(yīng)該執(zhí)行 UPDATE。 |
(11) |
polymorphism(多態(tài)) (可選, 默認(rèn)值為 implicit (隱式) ): 界定是隱式還是顯式的使用多態(tài)查詢(這只在Hibernate的具體表繼承策略中用到-譯注)。 |
(12) |
where (可選) 指定一個(gè)附加的SQLWHERE 條件, 在抓取這個(gè)類(lèi)的對(duì)象時(shí)會(huì)一直增加這個(gè)條件。 |
(13) |
persister (可選): 指定一個(gè)定制的ClassPersister。 |
(14) |
batch-size (可選,默認(rèn)是1) 指定一個(gè)用于 根據(jù)標(biāo)識(shí)符(identifier)抓取實(shí)例時(shí)使用的"batch size"(批次抓取數(shù)量)。 |
(15) |
optimistic-lock(樂(lè)觀鎖定) (可選,默認(rèn)是version): 決定樂(lè)觀鎖定的策略。 |
(16) |
lazy (optional): 通過(guò)設(shè)置lazy="false", 所有的延遲加載(Lazy fetching)功能將未被激活(disabled)。 |
(17) |
entity-name (可選): Hibernate3允許一個(gè)類(lèi)進(jìn)行多次映射( 默認(rèn)情況是映射到不同的表),并且允許使用Maps或XML代替Java層次的實(shí)體映射 (也就是實(shí)現(xiàn)動(dòng)態(tài)領(lǐng)域模型,不用寫(xiě)持久化類(lèi)-譯注)。 更多信息請(qǐng)看第?5.4?節(jié) “動(dòng)態(tài)模型(Dynamic models)” and 第?19?章 XML映射。 |
(18) |
check (可選): 這是一個(gè)SQL表達(dá)式, 用于為自動(dòng)生成的schema添加多行(multi-row)約束檢查。 |
(19) |
rowid (可選): Hibernate可以使用數(shù)據(jù)庫(kù)支持的所謂的ROWIDs,例如: Oracle數(shù)據(jù)庫(kù),如果你設(shè)置這個(gè)可選的rowid, Hibernate可以使用額外的字段rowid實(shí)現(xiàn)快速更新。ROWID是這個(gè)功能實(shí)現(xiàn)的重點(diǎn), 它代表了一個(gè)存儲(chǔ)元組(tuple)的物理位置。 |
(20) |
subselect (可選): 它將一個(gè)不可變(immutable)并且只讀的實(shí)體映射到一個(gè)數(shù)據(jù)庫(kù)的 子查詢中。它用于實(shí)現(xiàn)一個(gè)視圖代替一張基本表,但是最好不要這樣做。更多的介紹請(qǐng)看下面內(nèi)容。 |
(21) |
abstract (可選): 用于在<union-subclass>的繼承結(jié)構(gòu) (hierarchies)中標(biāo)識(shí)抽象超類(lèi)。 |
(22) |
entity-name (可選, 默認(rèn)為類(lèi)名): 顯式指定實(shí)體名 |
若指明的持久化類(lèi)實(shí)際上是一個(gè)接口,這也是完全可以接受的。 之后你可以用<subclass>來(lái)指定該接口的實(shí)際實(shí)現(xiàn)類(lèi)。 你可以持久化任何static(靜態(tài)的)內(nèi)部類(lèi)。 你應(yīng)該使用標(biāo)準(zhǔn)的類(lèi)名格式來(lái)指定類(lèi)名,比如:Foo$Bar。
不可變類(lèi),mutable="false"不可以被應(yīng)用程序更新或者刪除。 這可以讓Hibernate做一些小小的性能優(yōu)化。
可選的proxy屬性允許延遲加載類(lèi)的持久化實(shí)例。 Hibernate開(kāi)始會(huì)返回實(shí)現(xiàn)了這個(gè)命名接口的CGLIB代理。當(dāng)代理的某個(gè)方法被實(shí)際調(diào)用的時(shí)候, 真實(shí)的持久化對(duì)象才會(huì)被裝載。參見(jiàn)下面的“用于延遲裝載的代理”。
Implicit (隱式)的多態(tài)是指,如果查詢時(shí)給出的是任何超類(lèi)、該類(lèi)實(shí)現(xiàn)的接口或者該類(lèi)的 名字,都會(huì)返回這個(gè)類(lèi)的實(shí)例;如果查詢中給出的是子類(lèi)的名字,則會(huì)返回子類(lèi)的實(shí)例。 Explicit (顯式)的多態(tài)是指,只有在查詢時(shí)給出明確的該類(lèi)名字時(shí)才會(huì)返回這個(gè)類(lèi)的實(shí)例; 同時(shí)只有在這個(gè)<class>的定義中作為<subclass> 或者<joined-subclass>出現(xiàn)的子類(lèi),才會(huì)可能返回。 在大多數(shù)情況下,默認(rèn)的polymorphism="implicit"都是合適的。 顯式的多態(tài)在有兩個(gè)不同的類(lèi)映射到同一個(gè)表的時(shí)候很有用。(允許一個(gè)“輕型”的類(lèi),只包含部分表字段)。
persister屬性可以讓你定制這個(gè)類(lèi)使用的持久化策略。 你可以指定你自己實(shí)現(xiàn) org.hibernate.persister.EntityPersister的子類(lèi),你甚至可以完全從頭開(kāi)始編寫(xiě)一個(gè) org.hibernate.persister.ClassPersister接口的實(shí)現(xiàn), 比如是用儲(chǔ)存過(guò)程調(diào)用、序列化到文件或者LDAP數(shù)據(jù)庫(kù)來(lái)實(shí)現(xiàn)。 參閱org.hibernate.test.CustomPersister,這是一個(gè)簡(jiǎn)單的例子 (“持久化”到一個(gè)Hashtable)。
請(qǐng)注意dynamic-update和dynamic-insert的設(shè)置并不會(huì)繼承到子類(lèi), 所以在<subclass>或者<joined-subclass>元素中可能 需要再次設(shè)置。這些設(shè)置是否能夠提高效率要視情形而定。請(qǐng)用你的智慧決定是否使用。
使用select-before-update通常會(huì)降低性能。如果你重新連接一個(gè)脫管(detache)對(duì)象實(shí)例 到一個(gè)Session中時(shí),它可以防止數(shù)據(jù)庫(kù)不必要的觸發(fā)update。 這就很有用了。
如果你打開(kāi)了dynamic-update,你可以選擇幾種樂(lè)觀鎖定的策略:
-
version(版本檢查) 檢查version/timestamp字段
-
all(全部) 檢查全部字段
-
dirty(臟檢查)只檢察修改過(guò)的字段
-
none(不檢查)不使用樂(lè)觀鎖定
我們非常強(qiáng)烈建議你在Hibernate中使用version/timestamp字段來(lái)進(jìn)行樂(lè)觀鎖定。 對(duì)性能來(lái)說(shuō),這是最好的選擇,并且這也是唯一能夠處理在session外進(jìn)行操作的策略(例如: 在使用Session.merge()的時(shí)候)。
對(duì)Hibernate映射來(lái)說(shuō)視圖和表是沒(méi)有區(qū)別的,這是因?yàn)樗鼈冊(cè)跀?shù)據(jù)層都是透明的( 注意:一些數(shù)據(jù)庫(kù)不支持視圖屬性,特別是更新的時(shí)候)。有時(shí)你想使用視圖,但卻不能在數(shù)據(jù)庫(kù) 中創(chuàng)建它(例如:在遺留的schema中)。這樣的話,你可以映射一個(gè)不可變的(immutable)并且是 只讀的實(shí)體到一個(gè)給定的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>
定義這個(gè)實(shí)體用到的表為同步(synchronize),確保自動(dòng)刷新(auto-flush)正確執(zhí)行, 并且依賴原實(shí)體的查詢不會(huì)返回過(guò)期數(shù)據(jù)。<subselect>在屬性元素 和一個(gè)嵌套映射元素中都可見(jiàn)。
被映射的類(lèi)必須定義對(duì)應(yīng)數(shù)據(jù)庫(kù)表主鍵字段。大多數(shù)類(lèi)有一個(gè)JavaBeans風(fēng)格的屬性, 為每一個(gè)實(shí)例包含唯一的標(biāo)識(shí)。<id> 元素定義了該屬性到數(shù)據(jù)庫(kù)表主鍵字段的映射。
<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)識(shí)屬性的名字。 |
(2) |
type (可選): 標(biāo)識(shí)Hibernate類(lèi)型的名字。 |
(3) |
column (可選 - 默認(rèn)為屬性名): 主鍵字段的名字。 |
(4) |
unsaved-value (可選 - 默認(rèn)為一個(gè)字段判斷(sensible)的值): 一個(gè)特定的標(biāo)識(shí)屬性值,用來(lái)標(biāo)志該實(shí)例是剛剛創(chuàng)建的,尚未保存。 這可以把這種實(shí)例和從以前的session中裝載過(guò)(可能又做過(guò)修改--譯者注) 但未再次持久化的實(shí)例區(qū)分開(kāi)來(lái)。 |
(5) |
access (可選 - 默認(rèn)為property): Hibernate用來(lái)訪問(wèn)屬性值的策略。 |
如果 name屬性不存在,會(huì)認(rèn)為這個(gè)類(lèi)沒(méi)有標(biāo)識(shí)屬性。
unsaved-value 屬性很重要!如果你的類(lèi)的標(biāo)識(shí)屬性不是默認(rèn)為 正常的Java默認(rèn)值(null或零),你應(yīng)該指定正確的默認(rèn)值。
還有一個(gè)另外的<composite-id>定義可以訪問(wèn)舊式的多主鍵數(shù)據(jù)。 我們強(qiáng)烈不建議使用這種方式。
可選的<generator>子元素是一個(gè)Java類(lèi)的名字, 用來(lái)為該持久化類(lèi)的實(shí)例生成唯一的標(biāo)識(shí)。如果這個(gè)生成器實(shí)例需要某些配置值或者初始化參數(shù), 用<param>元素來(lái)傳遞。
<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接口。 這是一個(gè)非常簡(jiǎn)單的接口;某些應(yīng)用程序可以選擇提供他們自己特定的實(shí)現(xiàn)。當(dāng)然, Hibernate提供了很多內(nèi)置的實(shí)現(xiàn)。下面是一些內(nèi)置生成器的快捷名字:
- increment
-
用于為long, short或者int類(lèi)型生成 唯一標(biāo)識(shí)。只有在沒(méi)有其他進(jìn)程往同一張表中插入數(shù)據(jù)時(shí)才能使用。 在集群下不要使用。
- identity
-
對(duì)DB2,MySQL, MS SQL Server, Sybase和HypersonicSQL的內(nèi)置標(biāo)識(shí)字段提供支持。 返回的標(biāo)識(shí)符是long, short 或者int類(lèi)型的。
- sequence
-
在DB2,PostgreSQL, Oracle, SAP DB, McKoi中使用序列(sequence), 而在Interbase中使用生成器(generator)。返回的標(biāo)識(shí)符是long, short或者 int類(lèi)型的。
- hilo
-
使用一個(gè)高/低位算法高效的生成long, short 或者 int類(lèi)型的標(biāo)識(shí)符。給定一個(gè)表和字段(默認(rèn)分別是是 hibernate_unique_key 和next_hi)作為高位值的來(lái)源。 高/低位算法生成的標(biāo)識(shí)符只在一個(gè)特定的數(shù)據(jù)庫(kù)中是唯一的。
- seqhilo
-
使用一個(gè)高/低位算法來(lái)高效的生成long, short 或者 int類(lèi)型的標(biāo)識(shí)符,給定一個(gè)數(shù)據(jù)庫(kù)序列(sequence)的名字。
- uuid
-
用一個(gè)128-bit的UUID算法生成字符串類(lèi)型的標(biāo)識(shí)符, 這在一個(gè)網(wǎng)絡(luò)中是唯一的(使用了IP地址)。UUID被編碼為一個(gè)32位16進(jìn)制數(shù)字的字符串。
- guid
-
在MS SQL Server 和 MySQL 中使用數(shù)據(jù)庫(kù)生成的GUID字符串。
- native
-
根據(jù)底層數(shù)據(jù)庫(kù)的能力選擇identity, sequence 或者hilo中的一個(gè)。
- assigned
-
讓?xiě)?yīng)用程序在save()之前為對(duì)象分配一個(gè)標(biāo)示符。這是 <generator>元素沒(méi)有指定時(shí)的默認(rèn)生成策略。
- select
-
通過(guò)數(shù)據(jù)庫(kù)觸發(fā)器選擇一些唯一主鍵的行并返回主鍵值來(lái)分配一個(gè)主鍵。
- foreign
-
使用另外一個(gè)相關(guān)聯(lián)的對(duì)象的標(biāo)識(shí)符。通常和<one-to-one>聯(lián)合起來(lái)使用。
hilo 和 seqhilo生成器給出了兩種hi/lo算法的實(shí)現(xiàn), 這是一種很令人滿意的標(biāo)識(shí)符生成算法。第一種實(shí)現(xiàn)需要一個(gè)“特殊”的數(shù)據(jù)庫(kù)表來(lái)保存下一個(gè)可用的“hi”值。 第二種實(shí)現(xiàn)使用一個(gè)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時(shí)無(wú)法使用hilo。 當(dāng)Hibernate使用JTA獲取應(yīng)用服務(wù)器的數(shù)據(jù)源連接時(shí),你必須正確地配置 hibernate.transaction.manager_lookup_class。
UUID包含:IP地址,JVM的啟動(dòng)時(shí)間(精確到1/4秒),系統(tǒng)時(shí)間和一個(gè)計(jì)數(shù)器值(在JVM中唯一)。 在Java代碼中不可能獲得MAC地址或者內(nèi)存地址,所以這已經(jīng)是我們?cè)诓皇褂肑NI的前提下的能做的最好實(shí)現(xiàn)了。
對(duì)于內(nèi)部支持標(biāo)識(shí)字段的數(shù)據(jù)庫(kù)(DB2,MySQL,Sybase,MS SQL),你可以使用identity關(guān)鍵字生成。 對(duì)于內(nèi)部支持序列的數(shù)據(jù)庫(kù)(DB2,Oracle, PostgreSQL, Interbase, McKoi,SAP DB), 你可以使用sequence風(fēng)格的關(guān)鍵字生成。 這兩種方式對(duì)于插入一個(gè)新的對(duì)象都需要兩次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>
對(duì)于跨平臺(tái)開(kāi)發(fā),native策略會(huì)從identity, sequence 和hilo中進(jìn)行選擇,選擇哪一個(gè),這取決于底層數(shù)據(jù)庫(kù)的支持能力。
如果你需要應(yīng)用程序分配一個(gè)標(biāo)示符(而非Hibernate來(lái)生成),你可以使用assigned 生成器。這種特殊的生成器會(huì)使用已經(jīng)分配給對(duì)象的標(biāo)識(shí)符屬性的標(biāo)識(shí)符值。 這個(gè)生成器使用一個(gè)自然鍵(natural key,有商業(yè)意義的列-譯注)作為主鍵,而不是使用一個(gè)代理鍵( surrogate key,沒(méi)有商業(yè)意義的列-譯注)。
當(dāng)選擇assigned生成器時(shí),除非有一個(gè)version或timestamp屬性,或者你定義了 Interceptor.isUnsaved(),否則需要讓Hiberante使用 unsaved-value="undefined",強(qiáng)制Hibernatet查詢數(shù)據(jù)庫(kù)來(lái)確定一個(gè)實(shí)例是瞬時(shí)的(transient) 還是脫管的(detached)。
僅僅用于遺留的schema中 (Hibernate不能使用觸發(fā)器生成DDL)。
<id name="id" type="long" column="person_id"> <generator class="select"> <param name="key">socialSecurityNumber</param> </generator> </id>
在上面的例子中,類(lèi)定義了一個(gè)命名為socialSecurityNumber的唯一值屬性, 它是一個(gè)自然鍵(natural key),命名為person_id的代理鍵(surrogate key) 的值由觸發(fā)器生成。
<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)合主鍵,你可以映射類(lèi)的多個(gè)屬性為標(biāo)識(shí)符屬性。 <composite-id>元素接受<key-property> 屬性映射和<key-many-to-one>屬性映射作為子元素。
<composite-id> <key-property name="medicareNumber"/> <key-property name="dependent"/> </composite-id>
你的持久化類(lèi)必須重載equals()和 hashCode()方法,來(lái)實(shí)現(xiàn)組合的標(biāo)識(shí)符的相等判斷。 實(shí)現(xiàn)Serializable接口也是必須的。
不幸的是,這種組合關(guān)鍵字的方法意味著一個(gè)持久化類(lèi)是它自己的標(biāo)識(shí)。除了對(duì)象自己之外, 沒(méi)有什么方便的“把手”可用。你必須自己初始化持久化類(lèi)的實(shí)例,在使用組合關(guān)鍵字load() 持久化狀態(tài)之前,必須填充他的聯(lián)合屬性。我們會(huì)在第?9.4?節(jié) “組件作為聯(lián)合標(biāo)識(shí)符(Components as composite identifiers)”章中說(shuō)明一種 更加便捷的方法,把聯(lián)合標(biāo)識(shí)實(shí)現(xiàn)為一個(gè)獨(dú)立的類(lèi),下面描述的屬性只對(duì)這種備用方法有效:
-
name (可選):一個(gè)組件類(lèi)型,持有復(fù)合標(biāo)識(shí)(參見(jiàn)下一節(jié))。
-
class (可選 - 默認(rèn)為通過(guò)反射(reflection)得到的屬性類(lèi)型) : 作為聯(lián)合標(biāo)識(shí)的組件類(lèi)名(參見(jiàn)下一節(jié))。
-
unsaved-value (可選 - 默認(rèn)為 undefined): 如果設(shè)置為any,就表示瞬時(shí)(transient)實(shí)例應(yīng)該被重新初始化,或者如果 設(shè)置為none,則表示該實(shí)例是脫管對(duì)象。最好在所有的情況下都保持默認(rèn)的值。
在"一棵對(duì)象繼承樹(shù)對(duì)應(yīng)一個(gè)表"的策略中,<discriminator>元素是必需的, 它定義了表的鑒別器字段。鑒別器字段包含標(biāo)志值,用于告知持久化層應(yīng)該為某個(gè)特定的行創(chuàng)建哪一個(gè)子類(lèi)的實(shí)例。 如下這些受到限制的類(lèi)型可以使用: 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) 一個(gè)Hibernate字段類(lèi)型的名字 |
(3) |
force(強(qiáng)制) (可選 - 默認(rèn)為 false) "強(qiáng)制"Hibernate指定允許的鑒別器值,就算取得的所有實(shí)例都是根類(lèi)的。 |
(4) |
insert (可選 - 默認(rèn)為true) 如果你的鑒別器字段也是映射為復(fù)合標(biāo)識(shí)(composite identifier)的一部分,則需將 這個(gè)值設(shè)為false。(告訴Hibernate在做SQL INSERT 時(shí)不包含該列) |
(5) |
formula (可選) 一個(gè)SQL表達(dá)式,在類(lèi)型判斷(判斷是父類(lèi)還是具體子類(lèi)-譯注)時(shí)執(zhí)行。可用于基于內(nèi)容的鑒別器。 |
鑒別器字段的實(shí)際值是根據(jù)<class>和<subclass>元素中 的discriminator-value屬性得來(lái)的。
force屬性僅僅是在表包含一些未指定應(yīng)該映射到哪個(gè)持久化類(lèi)的時(shí)候才是有用的。 這種情況不會(huì)經(jīng)常遇到。
使用formula屬性你可以定義一個(gè)SQL表達(dá)式,用來(lái)判斷一個(gè)行數(shù)據(jù)的類(lèi)型。
<discriminator formula="case when CLASS_TYPE in ('a', 'b', 'c') then 0 else 1 end" type="integer"/>
<version>元素是可選的,表明表中包含附帶版本信息的數(shù)據(jù)。 這在你準(zhǔn)備使用 長(zhǎng)事務(wù)(long transactions)的時(shí)候特別有用。(見(jiàn)后)
<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)為屬性名): 指定持有版本號(hào)的字段名。 |
(2) |
name: 持久化類(lèi)的屬性名。 |
(3) |
type (可選 - 默認(rèn)是 integer): 版本號(hào)的類(lèi)型。 |
(4) |
access (可選 - 默認(rèn)是 property): Hibernate用于訪問(wèn)屬性值的策略。 |
(5) |
unsaved-value (可選 - 默認(rèn)是undefined): 用于標(biāo)明某個(gè)實(shí)例時(shí)剛剛被實(shí)例化的(尚未保存)版本屬性值,依靠這個(gè)值就可以把這種情況 和已經(jīng)在先前的session中保存或裝載的脫管(detached)實(shí)例區(qū)分開(kāi)來(lái)。 (undefined指明使用標(biāo)識(shí)屬性值進(jìn)行判斷。) |
版本號(hào)必須是以下類(lèi)型:long, integer, short, timestamp或者calendar。
一個(gè)脫管(detached)實(shí)例的version或timestamp不能為空(null),因?yàn)镠ibernate不管 unsaved-value指定為何種策略,它將分離任何屬性為空的version或timestamp 實(shí)例為瞬時(shí)(transient)實(shí)例。 避免Hibernate中的傳遞重附(transitive reattachment)問(wèn)題的一個(gè)簡(jiǎn)單方法是 定義一個(gè)不能為空的version或timestamp屬性,特別是在人們使用程序分配的標(biāo)識(shí)符(assigned identifiers) 或復(fù)合主鍵時(shí)非常有用!
可選的<timestamp>元素指明了表中包含時(shí)間戳數(shù)據(jù)。 這用來(lái)作為版本的替代。時(shí)間戳本質(zhì)上是一種對(duì)樂(lè)觀鎖定的一種不是特別安全的實(shí)現(xiàn)。當(dāng)然, 有時(shí)候應(yīng)用程序可能在其他方面使用時(shí)間戳。
<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)為屬性名): 持有時(shí)間戳的字段名。 |
(2) |
name: 在持久化類(lèi)中的JavaBeans風(fēng)格的屬性名, 其Java類(lèi)型是 Date 或者 Timestamp的。 |
(3) |
access (可選 - 默認(rèn)是 property): Hibernate用于訪問(wèn)屬性值的策略。 |
(4) |
unsaved-value (可選 - 默認(rèn)是null): 用于標(biāo)明某個(gè)實(shí)例時(shí)剛剛被實(shí)例化的(尚未保存)版本屬性值,依靠這個(gè)值就可以把這種情況和 已經(jīng)在先前的session中保存或裝載的脫管(detached)實(shí)例區(qū)分開(kāi)來(lái)。(undefined 指明使用標(biāo)識(shí)屬性值進(jìn)行這種判斷。) |
注意,<timestamp> 和<version type="timestamp">是等價(jià)的。
<property>元素為類(lèi)定義了一個(gè)持久化的,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: 屬性的名字,以小寫(xiě)字母開(kāi)頭。 |
(2) |
column (可選 - 默認(rèn)為屬性名字): 對(duì)應(yīng)的數(shù)據(jù)庫(kù)字段名。 也可以通過(guò)嵌套的<column>元素指定。 |
(3) |
type (可選): 一個(gè)Hibernate類(lèi)型的名字。 |
(4) |
update, insert (可選 - 默認(rèn)為 true) : 表明用于UPDATE 和/或 INSERT 的SQL語(yǔ)句中是否包含這個(gè)被映射了的字段。這二者如果都設(shè)置為false 則表明這是一個(gè)“外源性(derived)”的屬性,它的值來(lái)源于映射到同一個(gè)(或多個(gè)) 字段的某些其他屬性,或者通過(guò)一個(gè)trigger(觸發(fā)器)或其他程序。 |
(5) |
formula (可選): 一個(gè)SQL表達(dá)式,定義了這個(gè)計(jì)算 (computed) 屬性的值。計(jì)算屬性沒(méi)有和它對(duì)應(yīng)的數(shù)據(jù)庫(kù)字段。 |
(6) |
access (可選 - 默認(rèn)值為 property): Hibernate用來(lái)訪問(wèn)屬性值的策略。 |
(7) |
lazy (可選 - 默認(rèn)為 false): 指定 指定實(shí)例變量第一次被訪問(wèn)時(shí),這個(gè)屬性是否延遲抓取(fetched lazily)( 需要運(yùn)行時(shí)字節(jié)碼增強(qiáng))。 |
(8) |
unique (可選): 使用DDL為該字段添加唯一的約束。 此外,這也可以用作property-ref的目標(biāo)屬性。 |
(9) |
not-null (可選): 使用DDL為該字段添加可否為空(nullability)的約束。 |
(10) |
optimistic-lock (可選 - 默認(rèn)為 true): 指定這個(gè)屬性在做更新時(shí)是否需要獲得樂(lè)觀鎖定(optimistic lock)。 換句話說(shuō),它決定這個(gè)屬性發(fā)生臟數(shù)據(jù)時(shí)版本(version)的值是否增長(zhǎng)。 |
typename 可以是如下幾種:
-
Hibernate基礎(chǔ)類(lèi)型之一(比如:integer, string, character,date, timestamp, float, binary, serializable, object, blob)。
-
一個(gè)Java類(lèi)的名字,這個(gè)類(lèi)屬于一種默認(rèn)基礎(chǔ)類(lèi)型 (比如: int, float,char, java.lang.String, java.util.Date, java.lang.Integer, java.sql.Clob)。
-
一個(gè)可以序列化的Java類(lèi)的名字。
-
一個(gè)自定義類(lèi)型的類(lèi)的名字。(比如: com.illflow.type.MyCustomType)。
如果你沒(méi)有指定類(lèi)型,Hibernarte會(huì)使用反射來(lái)得到這個(gè)名字的屬性,以此來(lái)猜測(cè)正確的Hibernate類(lèi)型。 Hibernate會(huì)按照規(guī)則2,3,4的順序?qū)傩宰x取器(getter方法)的返回類(lèi)進(jìn)行解釋。然而,這還不夠。 在某些情況下你仍然需要type屬性。(比如,為了區(qū)別Hibernate.DATE 和Hibernate.TIMESTAMP,或者為了指定一個(gè)自定義類(lèi)型。)
access屬性用來(lái)讓你控制Hibernate如何在運(yùn)行時(shí)訪問(wèn)屬性。在默認(rèn)情況下, Hibernate會(huì)使用屬性的get/set方法對(duì)(pair)。如果你指明access="field", Hibernate會(huì)忽略get/set方法對(duì),直接使用反射來(lái)訪問(wèn)成員變量。你也可以指定你自己的策略, 這就需要你自己實(shí)現(xiàn)org.hibernate.property.PropertyAccessor接口, 再在access中設(shè)置你自定義策略類(lèi)的名字。
衍生屬性(derive propertie)是一個(gè)特別強(qiáng)大的特征。這些屬性應(yīng)該定義為只讀,屬性值在裝載時(shí)計(jì)算生成。 你用一個(gè)SQL表達(dá)式生成計(jì)算的結(jié)果,它會(huì)在這個(gè)實(shí)例轉(zhuǎn)載時(shí)翻譯成一個(gè)SQL查詢的SELECT 子查詢語(yǔ)句。
<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í)體自己的表,而不用為這個(gè)特別的列定義別名( 上面例子中的customerId)。同時(shí)注意,如果你不喜歡使用屬性, 你可以使用嵌套的<formula>映射元素。
通過(guò)many-to-one元素,可以定義一種常見(jiàn)的與另一個(gè)持久化類(lèi)的關(guān)聯(lián)。 這種關(guān)系模型是多對(duì)一關(guān)聯(lián)(實(shí)際上是一個(gè)對(duì)象引用-譯注):這個(gè)表的一個(gè)外鍵引用目標(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 (可選): 外間字段名。它也可以通過(guò)嵌套的 <column>元素指定。 |
(3) |
class (可選 - 默認(rèn)是通過(guò)反射得到屬性類(lèi)型): 關(guān)聯(lián)的類(lèi)的名字。 |
(4) |
cascade(級(jí)聯(lián)) (可選): 指明哪些操作會(huì)從父對(duì)象級(jí)聯(lián)到關(guān)聯(lián)的對(duì)象。 |
(5) |
fetch (可選 - 默認(rèn)為 select): 在外連接抓取(outer-join fetching)和序列選擇抓取(sequential select fetching)兩者中選擇其一。 |
(6) |
update, insert (可選 - defaults to true) 指定對(duì)應(yīng)的字段是否包含在用于UPDATE 和/或 INSERT 的SQL語(yǔ)句中。如果二者都是false,則這是一個(gè)純粹的 “外源性(derived)”關(guān)聯(lián),它的值是通過(guò)映射到同一個(gè)(或多個(gè))字段的某些其他屬性得到 或者通過(guò)trigger(觸發(fā)器)、或其他程序。 |
(7) |
property-ref: (可選) 指定關(guān)聯(lián)類(lèi)的一個(gè)屬性,這個(gè)屬性將會(huì)和本外鍵相對(duì)應(yīng)。 如果沒(méi)有指定,會(huì)使用對(duì)方關(guān)聯(lián)類(lèi)的主鍵。 |
(8) |
access (可選 - 默認(rèn)是 property): Hibernate用來(lái)訪問(wèn)屬性的策略。 |
(9) |
unique (可選): 使用DDL為外鍵字段生成一個(gè)唯一約束。此外, 這也可以用作property-ref的目標(biāo)屬性。這使關(guān)聯(lián)同時(shí)具有 一對(duì)一的效果。 |
(10) |
not-null (可選): 使用DDL為外鍵字段生成一個(gè)非空約束。 |
(11) |
optimistic-lock (可選 - 默認(rèn)為 true): 指定這個(gè)屬性在做更新時(shí)是否需要獲得樂(lè)觀鎖定(optimistic lock)。 換句話說(shuō),它決定這個(gè)屬性發(fā)生臟數(shù)據(jù)時(shí)版本(version)的值是否增長(zhǎng)。 |
(12) |
lazy (可選 - 默認(rèn)為 proxy): 默認(rèn)情況下,單點(diǎn)關(guān)聯(lián)是經(jīng)過(guò)代理的。lazy="true"指定此屬性應(yīng)該在實(shí)例變量第一次被訪問(wèn)時(shí)應(yīng)該延遲抓取(fetche lazily)(需要運(yùn)行時(shí)字節(jié)碼的增強(qiáng))。 lazy="false"指定此關(guān)聯(lián)總是被預(yù)先抓取。 |
(13) |
not-found (可選 - 默認(rèn)為 exception): 指定外鍵引用的數(shù)據(jù)不存在時(shí)如何處理: ignore會(huì)將數(shù)據(jù)不存在作為關(guān)聯(lián)到一個(gè)空對(duì)象(null)處理。 |
(14) |
entity-name (optional): 被關(guān)聯(lián)的類(lèi)的實(shí)體名。 |
cascade屬性設(shè)置為除了none以外任何有意義的值, 它將把特定的操作傳播到關(guān)聯(lián)對(duì)象中。這個(gè)值就代表著Hibernate基本操作的名稱(chēng), persist, merge, delete, save-update, evict, replicate, lock, refresh, 以及特別的值delete-orphan和all,并且可以用逗號(hào)分隔符 來(lái)合并這些操作,例如,cascade="persist,merge,evict"或 cascade="all,delete-orphan"。更全面的解釋請(qǐng)參考第?11.11?節(jié) “傳播性持久化(transitive persistence)”.
一個(gè)典型的簡(jiǎn)單many-to-one定義例子:
<many-to-one name="product" class="Product" column="PRODUCT_ID"/>
property-ref屬性只應(yīng)該用來(lái)對(duì)付老舊的數(shù)據(jù)庫(kù)系統(tǒng), 可能有外鍵指向?qū)Ψ疥P(guān)聯(lián)表的是個(gè)非主鍵字段(但是應(yīng)該是一個(gè)惟一關(guān)鍵字)的情況下。 這是一種十分丑陋的關(guān)系模型。比如說(shuō),假設(shè)Product類(lèi)有一個(gè)惟一的序列號(hào), 它并不是主鍵。(unique屬性控制Hibernate通過(guò)SchemaExport工具生成DDL的過(guò)程。)
<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)然,我們決不鼓勵(lì)這種用法。
如果被引用的唯一主鍵由關(guān)聯(lián)實(shí)體的多個(gè)屬性組成,你應(yīng)該在名稱(chēng)為<properties>的元素 里面映射所有關(guān)聯(lián)的屬性。
持久化對(duì)象之間一對(duì)一的關(guān)聯(lián)關(guān)系是通過(guò)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ò)反射得到的屬性類(lèi)型):被關(guān)聯(lián)的類(lèi)的名字。 |
(3) |
cascade(級(jí)聯(lián)) (可選) 表明操作是否從父對(duì)象級(jí)聯(lián)到被關(guān)聯(lián)的對(duì)象。 |
(4) |
constrained(約束) (可選) 表明該類(lèi)對(duì)應(yīng)的表對(duì)應(yīng)的數(shù)據(jù)庫(kù)表,和被關(guān)聯(lián)的對(duì)象所對(duì)應(yīng)的數(shù)據(jù)庫(kù)表之間,通過(guò)一個(gè)外鍵引用對(duì)主鍵進(jìn)行約束。 這個(gè)選項(xiàng)影響save()和delete()在級(jí)聯(lián)執(zhí)行時(shí)的先后順序以及 決定該關(guān)聯(lián)能否被委托(也在schema export tool中被使用). |
(5) |
fetch (可選 - 默認(rèn)設(shè)置為選擇): 在外連接抓取或者序列選擇抓取選擇其一. |
(6) |
property-ref: (可選) 指定關(guān)聯(lián)類(lèi)的屬性名,這個(gè)屬性將會(huì)和本類(lèi)的主鍵相對(duì)應(yīng)。如果沒(méi)有指定,會(huì)使用對(duì)方關(guān)聯(lián)類(lèi)的主鍵。 |
(7) |
access (可選 - 默認(rèn)是 property): Hibernate用來(lái)訪問(wèn)屬性的策略。 |
(8) |
formula (可選):絕大多數(shù)一對(duì)一的關(guān)聯(lián)都指向其實(shí)體的主鍵。在一些少見(jiàn)的情況中, 你可能會(huì)指向其他的一個(gè)或多個(gè)字段,或者是一個(gè)表達(dá)式,這些情況下,你可以用一個(gè)SQL公式來(lái)表示。 (可以在org.hibernate.test.onetooneformula找到例子) |
(9) |
lazy (可選 - 默認(rèn)為 proxy): 默認(rèn)情況下,單點(diǎn)關(guān)聯(lián)是經(jīng)過(guò)代理的。lazy="true"指定此屬性應(yīng)該在實(shí)例變量第一次被訪問(wèn)時(shí)應(yīng)該延遲抓取(fetche lazily)(需要運(yùn)行時(shí)字節(jié)碼的增強(qiáng))。 lazy="false"指定此關(guān)聯(lián)總是被預(yù)先抓取。注意,如果constrained="false", 不可能使用代理,Hibernate會(huì)采取預(yù)先抓取! |
(10) |
entity-name (可選): 被關(guān)聯(lián)的類(lèi)的實(shí)體名。 |
有兩種不同的一對(duì)一關(guān)聯(lián):
-
主鍵關(guān)聯(lián)
-
惟一外鍵關(guān)聯(lián)
主鍵關(guān)聯(lián)不需要額外的表字段;如果兩行是通過(guò)這種一對(duì)一關(guān)系相關(guān)聯(lián)的,那么這兩行就共享同樣的主關(guān)鍵字值。所以如果你希望兩個(gè)對(duì)象通過(guò)主鍵一對(duì)一關(guān)聯(lián),你必須確認(rèn)它們被賦予同樣的標(biāo)識(shí)值!
比如說(shuō),對(duì)下面的Employee和Person進(jìn)行主鍵一對(duì)一關(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)的字段是相等的。我們使用一個(gè)被成為foreign的特殊的hibernate標(biāo)識(shí)符生成策略:
<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>
一個(gè)剛剛保存的Person實(shí)例被賦予和該Person的employee屬性所指向的Employee實(shí)例同樣的關(guān)鍵字值。
另一種方式是一個(gè)外鍵和一個(gè)惟一關(guān)鍵字對(duì)應(yīng),上面的Employee和Person的例子,如果使用這種關(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"/>
<component>元素把子對(duì)象的一些元素與父類(lèi)對(duì)應(yīng)的表的一些字段映射起來(lái)。 然后組件可以定義它們自己的屬性、組件或者集合。參見(jiàn)后面的“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)為通過(guò)反射得到的屬性類(lèi)型):組件(子)類(lèi)的名字。 |
(3) |
insert: 被映射的字段是否出現(xiàn)在SQL的INSERT語(yǔ)句中? |
(4) |
update: 被映射的字段是否出現(xiàn)在SQL的UPDATE語(yǔ)句中? |
(5) |
access (可選 - 默認(rèn)是 property): Hibernate用來(lái)訪問(wèn)屬性的策略。 |
(6) |
lazy (可選 - 默認(rèn)是 false): 表明此組件應(yīng)在實(shí)例變量第一次被訪問(wèn)的時(shí)候延遲加載(需要編譯時(shí)字節(jié)碼裝置器) |
(7) |
optimistic-lock (可選 - 默認(rèn)是 true):表明更新此組件是否需要獲取樂(lè)觀鎖。換句話說(shuō),當(dāng)這個(gè)屬性變臟時(shí),是否增加版本號(hào)(Version) |
(8) |
unique (可選 - 默認(rèn)是 false):表明組件映射的所有字段上都有唯一性約束 |
其<property>子標(biāo)簽為子類(lèi)的一些屬性與表字段之間建立映射。
<component>元素允許加入一個(gè)<parent>子元素,在組件類(lèi)內(nèi)部就可以有一個(gè)指向其容器的實(shí)體的反向引用。
<dynamic-component>元素允許把一個(gè)Map映射為組件,其屬性名對(duì)應(yīng)map的鍵值。 參見(jiàn)第?9.5?節(jié) “動(dòng)態(tài)組件 (Dynamic components)”.
<properties> 元素允許定義一個(gè)命名的邏輯分組(grouping)包含一個(gè)類(lèi)中的多個(gè)屬性。 這個(gè)元素最重要的用處是允許多個(gè)屬性的組合作為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: 分組的邏輯名稱(chēng) - 不是 實(shí)際屬性的名稱(chēng). |
(2) |
insert: 被映射的字段是否出現(xiàn)在SQL的 INSERT語(yǔ)句中? |
(3) |
update: 被映射的字段是否出現(xiàn)在SQL的 UPDATE語(yǔ)句中? |
(4) |
optimistic-lock (可選 - 默認(rèn)是 true):表明更新此組件是否需要獲取樂(lè)觀鎖。換句話說(shuō),當(dāng)這個(gè)屬性變臟時(shí),是否增加版本號(hào)(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表的這個(gè)唯一鍵,而不是主鍵。
<many-to-one name="person" class="Person" property-ref="name"> <column name="firstName"/> <column name="initial"/> <column name="lastName"/> </many-to-one>
我們并不推薦這樣使用,除非在映射遺留數(shù)據(jù)的情況下。
最后,多態(tài)持久化需要為父類(lèi)的每個(gè)子類(lèi)都進(jìn)行定義。對(duì)于“每一棵類(lèi)繼承樹(shù)對(duì)應(yīng)一個(gè)表”的策略來(lái)說(shuō),就需要使用<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: 子類(lèi)的全限定名。 |
(2) |
discriminator-value(辨別標(biāo)志) (可選 - 默認(rèn)為類(lèi)名):一個(gè)用于區(qū)分每個(gè)獨(dú)立的子類(lèi)的值。 |
(3) |
proxy(代理) (可選): 指定一個(gè)類(lèi)或者接口,在延遲裝載時(shí)作為代理使用。 |
(4) |
lazy (可選, 默認(rèn)是true): 設(shè)置為 lazy="false" 禁止使用延遲抓取 |
每個(gè)子類(lèi)都應(yīng)該定義它自己的持久化屬性和子類(lèi)。 <version> 和<id> 屬性可以從根父類(lèi)繼承下來(lái)。在一棵繼承樹(shù)上的每個(gè)子類(lèi)都必須定義一個(gè)唯一的discriminator-value。如果沒(méi)有指定,就會(huì)使用Java類(lèi)的全限定名。
可以在單獨(dú)的映射文件中,直接在hibernate-mapping下定義subclass,union-subclass和joined-subclass映射。這樣你只要增加一個(gè)新的映射文件就可以繼承一棵類(lèi)繼承樹(shù)。你必須在子類(lèi)的映射中指定extends 屬性來(lái)指定已映射的超類(lèi)。注意:以前,這個(gè)特性使得映射文件的順序變得很重要。從Hibernate3開(kāi)始,當(dāng)使用extends關(guān)鍵字的時(shí)候,映射文件的次序便不重要了。而在單一映射文件中,依舊需要保持將超類(lèi)定義在子類(lèi)之前這樣的次序。
<hibernate-mapping> <subclass name="DomesticCat" extends="Cat" discriminator-value="D"> <property name="name" type="string"/> </subclass> </hibernate-mapping>
更多關(guān)于繼承映射的信息, 參考 第?10?章 繼承映射(Inheritance Mappings)章節(jié).
此外,每個(gè)子類(lèi)可能被映射到他自己的表中(每個(gè)子類(lèi)一個(gè)表的策略)。被繼承的狀態(tài)通過(guò)和超類(lè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: 子類(lèi)的全限定名。 |
(2) |
table: 子類(lèi)的表名. |
(3) |
proxy (可選): 指定一個(gè)類(lèi)或者接口,在延遲裝載時(shí)作為代理使用。 |
(4) |
lazy (可選, 默認(rèn)是 true): 設(shè)置為 lazy="false" 禁止使用延遲裝載。 |
這種映射策略不需要指定辨別標(biāo)志(discriminator)字段。但是,每一個(gè)子類(lèi)都必須使用<key>元素指定一個(gè)表字段來(lái)持有對(duì)象的標(biāo)識(shí)符。本章開(kāi)始的映射可以被用如下方式重寫(xiě):
<?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)。
第三種選擇是僅僅映射類(lèi)繼承樹(shù)中具體類(lèi)部分到表中(每個(gè)具體類(lèi)一張表的策略)。其中,每張表定義了類(lèi)的所有持久化狀態(tài),包括繼承的狀態(tài)。在 Hibernate 中,并不需要完全顯式地映射這樣的繼承樹(shù)。你可以簡(jiǎn)單地使用單獨(dú)的<class>定義映射每個(gè)類(lèi)。然而,如果你想使用多態(tài)關(guān)聯(lián)(例如,一個(gè)對(duì)類(lèi)繼承樹(shù)中超類(lèi)的關(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: 子類(lèi)的全限定名。 |
(2) |
table: 子類(lèi)的表名 |
(3) |
proxy (可選): 指定一個(gè)類(lèi)或者接口,在延遲裝載時(shí)作為代理使用。 |
(4) |
lazy (可選, 默認(rèn)是 true): 設(shè)置為 lazy="false" 禁止使用延遲裝載。 |
這種映射策略不需要指定辨別標(biāo)志(discriminator)字段。
更多關(guān)于繼承映射的信息,參考第?10?章 繼承映射(Inheritance Mappings)。
使用 <join> 元素,可以將一個(gè)類(lèi)的屬性映射到多張表中。
<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: 被連接表的名稱(chēng)。 |
(2) |
schema (可選):覆蓋由根<hibernate-mapping>元素指定的模式名稱(chēng)。 |
(3) |
catalog (可選): 覆蓋由根 <hibernate-mapping>元素指定的目錄名稱(chēng)。 |
(4) |
fetch (可選 - 默認(rèn)是 join): 如果設(shè)置為默認(rèn)值join, Hibernate 將使用一個(gè)內(nèi)連接來(lái)得到這個(gè)類(lèi)或其超類(lèi)定義的<join>,而使用一個(gè)外連接來(lái)得到其子類(lèi)定義的<join>。如果設(shè)置為select,則 Hibernate 將為子類(lèi)定義的 <join>使用順序選擇。這僅在一行數(shù)據(jù)表示一個(gè)子類(lèi)的對(duì)象的時(shí)候才會(huì)發(fā)生。對(duì)這個(gè)類(lèi)和其超類(lèi)定義的<join>,依然會(huì)使用內(nèi)連接得到。 |
(5) |
inverse (可選 - 默認(rèn)是 false): 如果打開(kāi),Hibernate 不會(huì)插入或者更新此連接定義的屬性。 |
(6) |
optional (可選 - 默認(rèn)是 false): 如果打開(kāi),Hibernate 只會(huì)在此連接定義的屬性非空時(shí)插入一行數(shù)據(jù),并且總是使用一個(gè)外連接來(lái)得到這些屬性。 |
例如,一個(gè)人(person)的地址(address)信息可以被映射到單獨(dú)的表中(并保留所有屬性的值類(lèi)型語(yǔ)義):
<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> ...
此特性常常對(duì)遺留數(shù)據(jù)模型有用,我們推薦表個(gè)數(shù)比類(lèi)個(gè)數(shù)少,以及細(xì)粒度的領(lǐng)域模型。然而,在單獨(dú)的繼承樹(shù)上切換繼承映射策略是有用的,后面會(huì)解釋這點(diǎn)。
我們目前已經(jīng)見(jiàn)到過(guò)<key>元素多次了。 這個(gè)元素在父映射元素定義了對(duì)新表的連接,并且在被連接表中定義了一個(gè)外鍵引用原表的主鍵的情況下經(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 (可選): 外鍵字段的名稱(chēng)。也可以通過(guò)嵌套的 <column>指定。 |
(2) |
on-delete (可選, 默認(rèn)是 noaction): 表明外鍵關(guān)聯(lián)是否打開(kāi)數(shù)據(jù)庫(kù)級(jí)別的級(jí)聯(lián)刪除。 |
(3) |
property-ref (可選): 表明外鍵引用的字段不是原表的主鍵(提供給遺留數(shù)據(jù))。 |
(4) |
not-null (可選): 表明外鍵的字段不可為空(這意味著無(wú)論何時(shí)外鍵都是主鍵的一部分)。 |
(5) |
update (可選): 表明外鍵決不應(yīng)該被更新(這意味著無(wú)論何時(shí)外鍵都是主鍵的一部分)。 |
(6) |
unique (可選): 表明外鍵應(yīng)有唯一性約束 (這意味著無(wú)論何時(shí)外鍵都是主鍵的一部分)。 |
對(duì)那些看重刪除性能的系統(tǒng),我們推薦所有的鍵都應(yīng)該定義為on-delete="cascade",這樣 Hibernate 將使用數(shù)據(jù)庫(kù)級(jí)的ON CASCADE DELETE約束,而不是多個(gè)DELETE語(yǔ)句。 注意,這個(gè)特性會(huì)繞過(guò) Hibernate 通常對(duì)版本數(shù)據(jù)(versioned data)采用的樂(lè)觀鎖策略。
not-null 和 update 屬性在映射單向一對(duì)多關(guān)聯(lián)的時(shí)候有用。如果你映射一個(gè)單向一對(duì)多關(guān)聯(lián)到非空的(non-nullable)外鍵,你必須 用<key not-null="true">定義此鍵字段。
任何接受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>
column 和 formula 屬性甚至可以在同一個(gè)屬性或關(guān)聯(lián)映射中被合并來(lái)表達(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>
假設(shè)你的應(yīng)用程序有兩個(gè)同樣名字的持久化類(lèi),但是你不想在Hibernate查詢中使用他們的全限定名。除了依賴auto-import="true"以外,類(lèi)也可以被顯式地“import(引用)”。你甚至可以引用沒(méi)有明確被映射的類(lèi)和接口。
<import class="java.lang.Object" rename="Universe"/>
<import class="ClassName" (1) rename="ShortName" (2) />
(1) |
class: 任何Java類(lèi)的全限定名。 |
(2) |
rename (可選 - 默認(rèn)為類(lèi)的全限定名): 在查詢語(yǔ)句中可以使用的名字。 |
這是屬性映射的又一種類(lèi)型。<any> 映射元素定義了一種從多個(gè)表到類(lèi)的多態(tài)關(guān)聯(lián)。這種類(lèi)型的映射常常需要多于一個(gè)字段。第一個(gè)字段持有被關(guān)聯(lián)實(shí)體的類(lèi)型,其他的字段持有標(biāo)識(shí)符。對(duì)這種類(lèi)型的關(guān)聯(lián)來(lái)說(shuō),不可能指定一個(gè)外鍵約束,所以這當(dāng)然不是映射(多態(tài))關(guān)聯(lián)的通常的方式。你只應(yīng)該在非常特殊的情況下使用它(比如,審計(jì)log,用戶會(huì)話數(shù)據(jù)等等)。
meta-type 屬性使得應(yīng)用程序能指定一個(gè)將數(shù)據(jù)庫(kù)字段的值映射到持久化類(lèi)的自定義類(lèi)型。這個(gè)持久化類(lèi)包含有用id-type指定的標(biāo)識(shí)符屬性。 你必須指定從meta-type的值到類(lèi)名的映射。
<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)識(shí)符類(lèi)型 |
(3) |
meta-type (可選 -默認(rèn)是 string): 允許辨別標(biāo)志(discriminator)映射的任何類(lèi)型 |
(4) |
cascade (可選 -默認(rèn)是none): 級(jí)聯(lián)的類(lèi)型 |
(5) |
access (可選 -默認(rèn)是 property): Hibernate 用來(lái)訪問(wèn)屬性值的策略。 |
(6) |
optimistic-lock (可選 -默認(rèn)是 true): 表明更新此組件是否需要獲取樂(lè)觀鎖。換句話說(shuō),當(dāng)這個(gè)屬性變臟時(shí),是否增加版本號(hào)(Version) |
為了理解很多與持久化服務(wù)相關(guān)的Java語(yǔ)言級(jí)對(duì)象的行為,我們需要把它們分為兩類(lèi):
實(shí)體entity 獨(dú)立于任何持有實(shí)體引用的對(duì)象。與通常的Java模型相比,不再被引用的對(duì)象會(huì)被當(dāng)作垃圾收集掉。實(shí)體必須被顯式的保存和刪除(除非保存和刪除是從父實(shí)體向子實(shí)體引發(fā)的級(jí)聯(lián))。這和ODMG模型中關(guān)于對(duì)象通過(guò)可觸及保持持久性有一些不同——比較起來(lái)更加接近應(yīng)用程序?qū)ο笸ǔT谝粋€(gè)大系統(tǒng)中的使用方法。實(shí)體支持循環(huán)引用和交叉引用,它們也可以加上版本信息。
一個(gè)實(shí)體的持久狀態(tài)包含指向其他實(shí)體和值類(lèi)型實(shí)例的引用。值可以是原始類(lèi)型,集合(不是集合中的對(duì)象),組件或者特定的不可變對(duì)象。與實(shí)體不同,值(特別是集合和組件)是通過(guò)可觸及性來(lái)進(jìn)行持久化和刪除的。因?yàn)橹祵?duì)象(和原始類(lèi)型數(shù)據(jù))是隨著包含他們的實(shí)體而被持久化和刪除的,他們不能被獨(dú)立的加上版本信息。值沒(méi)有獨(dú)立的標(biāo)識(shí),所以他們不能被兩個(gè)實(shí)體或者集合共享。
直到現(xiàn)在,我們都一直使用術(shù)語(yǔ)“持久類(lèi)”(persistent class)來(lái)代表實(shí)體。我們?nèi)匀粫?huì)這么做。 然而嚴(yán)格說(shuō)來(lái),不是所有的用戶自定義的,帶有持久化狀態(tài)的類(lèi)都是實(shí)體。組件就是用戶自定義類(lèi),卻是值語(yǔ)義的。java.lang.String類(lèi)型的java屬性也是值語(yǔ)義的。給了這個(gè)定義以后,我們可以說(shuō)所有JDK提供的類(lèi)型(類(lèi))都是值類(lèi)型的語(yǔ)義,而用于自定義類(lèi)型可能被映射為實(shí)體類(lèi)型或值類(lèi)型語(yǔ)義。采用哪種類(lèi)型的語(yǔ)義取決于開(kāi)發(fā)人員。在領(lǐng)域模型中,尋找實(shí)體類(lèi)的一個(gè)好線索是共享引用指向這個(gè)類(lèi)的單一實(shí)例,而組合或聚合通常被轉(zhuǎn)化為值類(lèi)型。
我們會(huì)在本文檔中重復(fù)碰到這兩個(gè)概念。
挑戰(zhàn)在于將java類(lèi)型系統(tǒng)(和開(kāi)發(fā)者定義的實(shí)體和值類(lèi)型)映射到 SQL/數(shù)據(jù)庫(kù)類(lèi)型系統(tǒng)。Hibernate提供了連接兩個(gè)系統(tǒng)之間的橋梁:對(duì)于實(shí)體類(lèi)型,我們使用<class>, <subclass> 等等。對(duì)于值類(lèi)型,我們使用 <property>, <component> 及其他,通常跟隨著type屬性。這個(gè)屬性的值是Hibernate 的映射類(lèi)型的名字。Hibernate提供了許多現(xiàn)成的映射(標(biāo)準(zhǔn)的JDK值類(lèi)型)。你也可以編寫(xiě)自己的映射類(lèi)型并實(shí)現(xiàn)自定義的變換策略,隨后我們會(huì)看到這點(diǎn)。
所有的Hibernate內(nèi)建類(lèi)型,除了collections以外,都支持空(null)語(yǔ)義。
The built-in basic mapping types may be roughly categorized into 內(nèi)建的 基本映射類(lèi)型可以大致分為
- integer, long, short, float, double, character, byte, boolean, yes_no, true_false
-
這些類(lèi)型都對(duì)應(yīng)Java的原始類(lèi)型或者其封裝類(lèi),來(lái)符合(特定廠商的)SQL 字段類(lèi)型。boolean, yes_no 和 true_false都是Java 中boolean 或者java.lang.Boolean的另外說(shuō)法。
- string
-
從java.lang.String 到 VARCHAR (或者 Oracle的 VARCHAR2)的映射。
- date, time, timestamp
-
從java.util.Date和其子類(lèi)到SQL類(lèi)型DATE, TIME 和TIMESTAMP (或等價(jià)類(lèi)型)的映射。
- calendar, calendar_date
-
從java.util.Calendar 到SQL 類(lèi)型TIMESTAMP和 DATE(或等價(jià)類(lèi)型)的映射。
- big_decimal, big_integer
-
從java.math.BigDecimal和java.math.BigInteger到NUMERIC (或者 Oracle 的NUMBER類(lèi)型)的映射。
- locale, timezone, currency
-
從java.util.Locale, java.util.TimeZone 和java.util.Currency 到VARCHAR (或者 Oracle 的VARCHAR2類(lèi)型)的映射. Locale和 Currency 的實(shí)例被映射為它們的ISO代碼。TimeZone的實(shí)例被影射為它的ID。
- class
-
從java.lang.Class 到 VARCHAR (或者 Oracle 的VARCHAR2類(lèi)型)的映射。Class被映射為它的全限定名。
- binary
-
把字節(jié)數(shù)組(byte arrays)映射為對(duì)應(yīng)的 SQL二進(jìn)制類(lèi)型。
- text
-
把長(zhǎng)Java字符串映射為SQL的CLOB或者TEXT類(lèi)型。
- serializable
-
把可序列化的Java類(lèi)型映射到對(duì)應(yīng)的SQL二進(jìn)制類(lèi)型。你也可以為一個(gè)并非默認(rèn)為基本類(lèi)型的可序列化Java類(lèi)或者接口指定Hibernate類(lèi)型serializable。
- clob, blob
-
JDBC 類(lèi) java.sql.Clob 和 java.sql.Blob的映射。某些程序可能不適合使用這個(gè)類(lèi)型,因?yàn)閎lob和clob對(duì)象可能在一個(gè)事務(wù)之外是無(wú)法重用的。(而且, 驅(qū)動(dòng)程序?qū)@種類(lèi)型的支持充滿著補(bǔ)丁和前后矛盾。)
實(shí)體及其集合的唯一標(biāo)識(shí)可以是除了binary、 blob 和 clob之外的任何基礎(chǔ)類(lèi)型。(聯(lián)合標(biāo)識(shí)也是允許的,后面會(huì)說(shuō)到。)
在org.hibernate.Hibernate中,定義了基礎(chǔ)類(lèi)型對(duì)應(yīng)的Type常量。比如,Hibernate.STRING代表string 類(lèi)型。
開(kāi)發(fā)者創(chuàng)建屬于他們自己的值類(lèi)型也是很容易的。比如說(shuō),你可能希望持久化java.lang.BigInteger類(lèi)型的屬性,持久化成為VARCHAR字段。Hibernate沒(méi)有內(nèi)置這樣一種類(lèi)型。自定義類(lèi)型能夠映射一個(gè)屬性(或集合元素)到不止一個(gè)數(shù)據(jù)庫(kù)表字段。比如說(shuō),你可能有這樣的Java屬性:getName()/setName(),這是java.lang.String類(lèi)型的,對(duì)應(yīng)的持久化到三個(gè)字段:FIRST_NAME, INITIAL, SURNAME。
要實(shí)現(xiàn)一個(gè)自定義類(lèi)型,可以實(shí)現(xiàn)org.hibernate.UserType或org.hibernate.CompositeUserType中的任一個(gè),并且使用類(lèi)型的Java全限定類(lèi)名來(lái)定義屬性。請(qǐng)查看org.hibernate.test.DoubleStringType這個(gè)例子,看看它是怎么做的。
<property name="twoStrings" type="org.hibernate.test.DoubleStringType"> <column name="first_string"/> <column name="second_string"/> </property>
注意使用<column>標(biāo)簽來(lái)把一個(gè)屬性映射到多個(gè)字段的做法。
CompositeUserType, EnhancedUserType, UserCollectionType, 和 UserVersionType 接口為更特殊的使用方式提供支持。
你甚至可以在一個(gè)映射文件中提供參數(shù)給一個(gè)UserType。 為了這樣做,你的UserType必須實(shí)現(xiàn)org.hibernate.usertype.ParameterizedType接口。為了給自定義類(lèi)型提供參數(shù),你可以在映射文件中使用<type>元素。
<property name="priority"> <type name="com.mycompany.usertypes.DefaultValueIntegerType"> <param name="default">0</param> </type> </property>
現(xiàn)在,UserType 可以從傳入的Properties對(duì)象中得到default 參數(shù)的值。
如果你非常頻繁地使用某一UserType,可以為他定義一個(gè)簡(jiǎn)稱(chēng)。這可以通過(guò)使用 <typedef>元素來(lái)實(shí)現(xiàn)。Typedefs為一自定義類(lèi)型賦予一個(gè)名稱(chēng),并且如果此類(lèi)型是參數(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ù)具體案例通過(guò)屬性映射中的類(lèi)型參數(shù)覆蓋在typedef中提供的參數(shù)。
盡管 Hibernate 內(nèi)建的豐富的類(lèi)型和對(duì)組件的支持意味著你可能很少 需要使用自定義類(lèi)型。不過(guò),為那些在你的應(yīng)用中經(jīng)常出現(xiàn)的(非實(shí)體)類(lèi)使用自定義類(lèi)型也是一個(gè)好方法。例如,一個(gè)MonetaryAmount類(lèi)使用CompositeUserType來(lái)映射是不錯(cuò)的選擇,雖然他可以很容易地被映射成組件。這樣做的動(dòng)機(jī)之一是抽象。使用自定義類(lèi)型,以后假若你改變表示金額的方法時(shí),它可以保證映射文件不需要修改。
你可通過(guò)在映射文檔中使用反向引號(hào)(`)把表名或者字段名包圍起來(lái),以強(qiáng)制Hibernate在生成的SQL中把標(biāo)識(shí)符用引號(hào)包圍起來(lái)。Hibernate會(huì)使用相應(yīng)的SQLDialect(方言)來(lái)使用正確的引號(hào)風(fēng)格(通常是雙引號(hào),但是在SQL Server中是括號(hào),MySQL中是反向引號(hào))。
<class name="LineItem" table="`Line Item`"> <id name="id" column="`Item Id`"/><generator class="assigned"/></id> <property name="itemNumber" column="`Item #`"/> ... </class>
XML 并不適用于所有人, 因此有其他定義Hibernate O/R 映射元數(shù)據(jù)(metadata)的方法。
很多Hibernate使用者更喜歡使用XDoclet@hibernate.tags將映射信息直接嵌入到源代碼中。我們不會(huì)在本文檔中涉及這個(gè)方法,因?yàn)閲?yán)格說(shuō)來(lái),這屬于XDoclet的一部分。然而,我們包含了如下使用XDoclet映射的Cat類(lèi)的例子。
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的例子
JDK 5.0 在語(yǔ)言級(jí)別引入了 XDoclet 風(fēng)格的標(biāo)注,并且是類(lèi)型安全的,在編譯期進(jìn)行檢查。這一機(jī)制比XDoclet的注解更為強(qiáng)大,有更好的工具和IDE支持。例如, IntelliJ IDEA,支持JDK 5.0注解的自動(dòng)完成和語(yǔ)法高亮 。EJB規(guī)范的新修訂版(JSR-220)使用 JDK 5.0的注解作為entity beans的主要元數(shù)據(jù)(metadata)機(jī)制。Hibernate 3 實(shí)現(xiàn)了JSR-220 (the persistence API)的EntityManager,支持通過(guò)Hibernate Annotations包定義映射元數(shù)據(jù)。這個(gè)包作為單獨(dú)的部分下載,支持EJB3 (JSR-220)和Hibernate3的元數(shù)據(jù)。
這是一個(gè)被注解為EJB entity bean 的POJO類(lèi)的例子
@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 }
注意:對(duì) JDK 5.0 注解 (和 JSR-220)支持的工作仍然在進(jìn)行中,并未完成。
(譯者注:在閱讀本章的時(shí)候,以后整個(gè)手冊(cè)的閱讀過(guò)程中,我們都會(huì)面臨一個(gè)名詞方面的問(wèn)題,那就是“集合”。"Collections"和"Set"在中文里對(duì)應(yīng)都被翻譯為“集合”,但是他們的含義很不一樣。Collections是一個(gè)超集,Set是其中的一種。大部分情況下,本譯稿中泛指的未加英文注明的“集合”,都應(yīng)當(dāng)理解為“Collections”。在有些二者同時(shí)出現(xiàn),可能造成混淆的地方,我們用“集合類(lèi)”來(lái)特指“Collecions”,“集合(Set)”來(lái)指"Set",一般都會(huì)在后面的括號(hào)中給出英文。希望大家在閱讀時(shí)聯(lián)系上下文理解,不要造成誤解。 與此同時(shí),“元素”一詞對(duì)應(yīng)的英文“element”,也有兩個(gè)不同的含義。其一為集合的元素,是內(nèi)存中的一個(gè)變量;另一含義則是XML文檔中的一個(gè)標(biāo)簽所代表的元素。也請(qǐng)注意區(qū)別。 本章中,特別是后半部分是需要反復(fù)閱讀才能理解清楚的。如果遇到任何疑問(wèn),請(qǐng)記住,英文版本的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 或者...任何你喜歡的類(lèi)型!("任何你喜歡的類(lèi)型" 代表你需要編寫(xiě) org.hibernate.usertype.UserCollectionType的實(shí)現(xiàn).)
注意我們是如何用一個(gè)HashSet實(shí)例來(lái)初始化實(shí)例變量的.這是用于初始化新創(chuàng)建(尚未持久化)的類(lèi)實(shí)例中集合值屬性的最佳方法。當(dāng)你持久化這個(gè)實(shí)例時(shí)——比如通過(guò)調(diào)用persist()——Hibernate 會(huì)自動(dòng)把HashSet替換為Hibernate自己的Set實(shí)現(xiàn)。觀察下面的錯(cuò)誤:
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ù)不同的接口類(lèi)型,被Hibernate注射的持久化集合類(lèi)的表現(xiàn)類(lèi)似HashMap, HashSet, TreeMap, TreeSet or ArrayList。
集合類(lèi)實(shí)例具有值類(lèi)型的通常行為。當(dāng)被持久化對(duì)象引用后,他們會(huì)自動(dòng)被持久化,當(dāng)不再被引用后,自動(dòng)被刪除。假若實(shí)例被從一個(gè)持久化對(duì)象傳遞到另一個(gè),它的元素可能從一個(gè)表轉(zhuǎn)移到另一個(gè)表。兩個(gè)實(shí)體不能共享同一個(gè)集合類(lèi)實(shí)例的引用。因?yàn)榈讓雨P(guān)系數(shù)據(jù)庫(kù)模型的原因,集合值屬性無(wú)法支持空值語(yǔ)義;Hibernate對(duì)空的集合引用和空集合不加區(qū)別。
你不需要過(guò)多的為此擔(dān)心。就如同你平時(shí)使用普通的Java集合類(lèi)一樣來(lái)使用持久化集合類(lèi)。只是要確認(rèn)你理解了雙向關(guān)聯(lián)的語(yǔ)義(后文討論)。
用于映射集合類(lèi)的Hibernate映射元素取決于接口的類(lèi)型。比如, <set> 元素用來(lái)映射Set類(lèi)型的屬性。
<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 集合屬性的名稱(chēng) |
(2) |
table (可選——默認(rèn)為屬性的名稱(chēng))這個(gè)集合表的名稱(chēng)(不能在一對(duì)多的關(guān)聯(lián)關(guān)系中使用) |
(3) |
schema (可選) 表的schema的名稱(chēng), 他將覆蓋在根元素中定義的schema |
(4) |
lazy (可選--默認(rèn)為true) 可以用來(lái)關(guān)閉延遲加載,指定一直使用預(yù)先抓取(對(duì)數(shù)組不適用) |
(5) |
inverse (可選——默認(rèn)為false) 標(biāo)記這個(gè)集合作為雙向關(guān)聯(lián)關(guān)系中的方向一端。 |
(6) |
cascade (可選——默認(rèn)為none) 讓操作級(jí)聯(lián)到子實(shí)體 |
(7) |
sort(可選)指定集合的排序順序, 其可以為自然的(natural)或者給定一個(gè)用來(lái)比較的類(lèi)。 |
(8) |
order-by (可選, 僅用于jdk1.4) 指定表的字段(一個(gè)或幾個(gè))再加上asc或者desc(可選), 定義Map,Set和Bag的迭代順序 |
(9) |
where (可選) 指定任意的SQL where條件, 該條件將在重新載入或者刪除這個(gè)集合時(shí)使用(當(dāng)集合中的數(shù)據(jù)僅僅是所有可用數(shù)據(jù)的一個(gè)子集時(shí)這個(gè)條件非常有用) |
(10) |
fetch (可選, 默認(rèn)為select) 用于在外連接抓取、通過(guò)后續(xù)select抓取和通過(guò)后續(xù)subselect抓取之間選擇。 |
(11) |
batch-size (可選, 默認(rèn)為1) 指定通過(guò)延遲加載取得集合實(shí)例的批處理塊大小("batch size")。 |
(12) |
access(可選-默認(rèn)為屬性property):Hibernate取得屬性值時(shí)使用的策略 |
(12) |
樂(lè)觀鎖 (可選 - 默認(rèn)為 true): 對(duì)集合的狀態(tài)的改變會(huì)是否導(dǎo)致其所屬的實(shí)體的版本增長(zhǎng)。 (對(duì)一對(duì)多關(guān)聯(lián)來(lái)說(shuō),關(guān)閉這個(gè)屬性常常是有理的) |
集合實(shí)例在數(shù)據(jù)庫(kù)中依靠持有集合的實(shí)體的外鍵加以辨別。此外鍵作為集合關(guān)鍵字段(collection key column)(或多個(gè)字段)加以引用。集合關(guān)鍵字段通過(guò)<key> 元素映射。
在外鍵字段上可能具有非空約束。對(duì)于大多數(shù)集合來(lái)說(shuō),這是隱含的。對(duì)單向一對(duì)多關(guān)聯(lián)來(lái)說(shuō),外鍵字段默認(rèn)是可以為空的,因此你可能需要指明 not-null="true"。
<key column="productSerialNumber" not-null="true"/>
外鍵約束可以使用ON DELETE CASCADE。
<key column="productSerialNumber" on-delete="cascade"/>
對(duì)<key> 元素的完整定義,請(qǐng)參閱前面的章節(jié)。
集合幾乎可以包含任何其他的Hibernate類(lèi)型,包括所有的基本類(lèi)型、自定義類(lèi)型、組件,當(dāng)然還有對(duì)其他實(shí)體的引用。存在一個(gè)重要的區(qū)別:位于集合中的對(duì)象可能是根據(jù)“值”語(yǔ)義來(lái)操作(其聲明周期完全依賴于集合持有者),或者它可能是指向另一個(gè)實(shí)體的引用,具有其自己的生命周期。在后者的情況下,被作為集合持有的狀態(tài)考慮的,只有兩個(gè)對(duì)象之間的“連接”。
被包容的類(lèi)型被稱(chēng)為集合元素類(lèi)型(collection element type)。集合元素通過(guò)<element>或<composite-element>映射,或在其是實(shí)體引用的時(shí)候,通過(guò)<one-to-many> 或<many-to-many>映射。前兩種用于使用值語(yǔ)義映射元素,后兩種用于映射實(shí)體關(guān)聯(lián)。
所有的集合映射,除了set和bag語(yǔ)義的以外,都需要指定一個(gè)集合表的索引字段(index column)——用于對(duì)應(yīng)到數(shù)組索引,或者List的索引,或者Map的關(guān)鍵字。通過(guò)<map-key>,Map 的索引可以是任何基礎(chǔ)類(lèi)型;若通過(guò)<map-key-many-to-many>,它也可以是一個(gè)實(shí)體引用;若通過(guò)<composite-map-key>,它還可以是一個(gè)組合類(lèi)型。數(shù)組或列表的索引必須是integer類(lèi)型,并且使用 <list-index>元素定義映射。被映射的字段包含有順序排列的整數(shù)(默認(rèn)從0開(kāi)始)。
<map-key column="column_name" (1) formula="any SQL expression" (2) type="type_name" (3) node="@attribute-name" length="N"/>
(1) |
column(可選):保存集合索引值的字段名。 |
(2) |
formula (可選): 用于計(jì)算map關(guān)鍵字的SQL公式 |
(3) |
type (可選,默認(rèn)為整型integer):集合索引的類(lèi)型。 |
<map-key-many-to-many column="column_name" (1) formula="any SQL expression" (2)(3) class="ClassName" />
(1) |
column(可選):集合索引值中外鍵字段的名稱(chēng) |
(2) |
formula (可選): 用于計(jì)算map關(guān)鍵字的外鍵的SQL公式 |
(3) |
class (必需):集合的索引使用的實(shí)體類(lèi)。 |
假若你的表沒(méi)有一個(gè)索引字段,當(dāng)你仍然希望使用List作為屬性類(lèi)型,你應(yīng)該把此屬性映射為Hibernate <bag>。從數(shù)據(jù)庫(kù)中獲取的時(shí)候,bag不維護(hù)其順序,但也可選擇性的進(jìn)行排序。
從集合類(lèi)可以產(chǎn)生很大一部分映射,覆蓋了很多常見(jiàn)的關(guān)系模型。我們建議你試驗(yàn)schema生成工具,來(lái)體會(huì)一下不同的映射聲明是如何被翻譯為數(shù)據(jù)庫(kù)表的。
任何值集合或者多對(duì)多關(guān)聯(lián)需要專(zhuān)用的具有一個(gè)或多個(gè)外鍵字段的collection table、一個(gè)或多個(gè)collection element column,以及還可能有一個(gè)或多個(gè)索引字段。
對(duì)于一個(gè)值集合, 我們使用<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 (可選): 用于計(jì)算元素的SQL公式 |
(3) |
type (必需):集合元素的類(lèi)型 |
多對(duì)多關(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(可選): 這個(gè)元素的外鍵關(guān)鍵字段名 |
(2) |
formula (可選): 用于計(jì)算元素外鍵值的SQL公式. |
(3) |
class (必需): 關(guān)聯(lián)類(lèi)的名稱(chēng) |
(3) |
outer-join (可選 - 默認(rèn)為auto): 在Hibernate系統(tǒng)參數(shù)中hibernate.use_outer_join被打開(kāi)的情況下,該參數(shù)用來(lái)允許使用outer join來(lái)載入此集合的數(shù)據(jù)。 |
(4) |
為此關(guān)聯(lián)打開(kāi)外連接抓取或者后續(xù)select抓取。這是特殊情況;對(duì)于一個(gè)實(shí)體及其指向其他實(shí)體的多對(duì)多關(guān)聯(lián)進(jìn)全預(yù)先抓取(使用一條單獨(dú)的SELECT),你不僅需要對(duì)集合自身打開(kāi)join,也需要對(duì)<many-to-many>這個(gè)內(nèi)嵌元素打開(kāi)此屬性。 |
(5) |
對(duì)外鍵字段允許DDL生成的時(shí)候生成一個(gè)惟一約束。這使關(guān)聯(lián)變成了一個(gè)高效的一對(duì)多關(guān)聯(lián)。(此句存疑:原文為T(mén)his makes the association multiplicity effectively one to many.) |
(6) |
not-found (可選 - 默認(rèn)為 exception): 指明引用的外鍵中缺少某些行該如何處理: ignore 會(huì)把缺失的行作為一個(gè)空引用處理。 |
(7) |
entity-name (可選): 被關(guān)聯(lián)的類(lèi)的實(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>
一個(gè)實(shí)體數(shù)組,在這個(gè)案例中是一個(gè)多對(duì)多的關(guān)聯(lián)(注意這里的實(shí)體是自動(dòng)管理生命周期的對(duì)象(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>
一個(gè)map,通過(guò)字符串的索引來(lái)指明日期:
<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>
一個(gè)組件的列表:(下一章討論)
<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>
一對(duì)多關(guān)聯(lián) 通過(guò)外鍵 連接兩個(gè)類(lèi)對(duì)應(yīng)的表,而沒(méi)有中間集合表。 這個(gè)關(guān)系模型失去了一些Java集合的語(yǔ)義:
-
一個(gè)被包含的實(shí)體的實(shí)例只能被包含在一個(gè)集合的實(shí)例中
-
一個(gè)被包含的實(shí)體的實(shí)例只能對(duì)應(yīng)于集合索引的一個(gè)值中
一個(gè)從Product到Part的關(guān)聯(lián)需要關(guān)鍵字字段,可能還有一個(gè)索引字段指向Part所對(duì)應(yīng)的表。 <one-to-many>標(biāo)記指明了一個(gè)一對(duì)多的關(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)類(lèi)的名稱(chēng)。 |
(2) |
not-found (可選 - 默認(rèn)為exception): 指明若緩存的標(biāo)示值關(guān)聯(lián)的行缺失,該如何處理: ignore 會(huì)把缺失的行作為一個(gè)空關(guān)聯(lián)處理。 |
(3) |
entity-name (可選): 被關(guān)聯(lián)的類(lèi)的實(shí)體名,作為class的替代。 |
例子
<set name="bars"> <key column="foo_id"/> <one-to-many class="org.hibernate.Bar"/> </set>
注意:<one-to-many>元素不需要定義任何字段。 也不需要指定表名。
重要提示 :如果一對(duì)多關(guān)聯(lián)中的外鍵字段定義成NOT NULL,你必須把<key>映射聲明為not-null="true",或者使用雙向關(guān)聯(lián),并且標(biāo)明inverse="true"。參閱本章后面關(guān)于雙向關(guān)聯(lián)的討論。
下面的例子展示一個(gè)Part實(shí)體的map,把name作為關(guān)鍵字。( partName 是Part的持久化屬性)。注意其中的基于公式的索引的用法。
<map name="parts" cascade="all"> <key column="productId" not-null="true"/> <map-key formula="partName"/> <one-to-many class="Part"/> </map>
Hibernate支持實(shí)現(xiàn)java.util.SortedMap和java.util.SortedSet的集合。你必須在映射文件中指定一個(gè)比較器:
<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和某個(gè)實(shí)現(xiàn)了java.util.Comparator的類(lèi)的名稱(chēng)。
分類(lèi)集合的行為事實(shí)上象java.util.TreeSet或者java.util.TreeMap。
如果你希望數(shù)據(jù)庫(kù)自己對(duì)集合元素排序,可以利用set,bag或者map映射中的order-by屬性。這個(gè)解決方案只能在jdk1.4或者更高的jdk版本中才可以實(shí)現(xiàn)(通過(guò)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>
注意: 這個(gè)order-by屬性的值是一個(gè)SQL排序子句而不是HQL的!
關(guān)聯(lián)還可以在運(yùn)行時(shí)使用集合filter()根據(jù)任意的條件來(lái)排序。
sortedUsers = s.createFilter( group.getUsers(), "order by this.name" ).list();
雙向關(guān)聯(lián) 允許通過(guò)關(guān)聯(lián)的任一端訪問(wèn)另外一端。在Hibernate中, 支持兩種類(lèi)型的雙向關(guān)聯(lián):
- 一對(duì)多(one-to-many)
-
Set或者bag值在一端, 單獨(dú)值(非集合)在另外一端
- 多對(duì)多(many-to-many)
-
兩端都是set或bag值
要建立一個(gè)雙向的多對(duì)多關(guān)聯(lián),只需要映射兩個(gè)many-to-many關(guān)聯(lián)到同一個(gè)數(shù)據(jù)庫(kù)表中,并再定義其中的一端為inverse(使用哪一端要根據(jù)你的選擇,但它不能是一個(gè)索引集合)。
這里有一個(gè)many-to-many的雙向關(guān)聯(lián)的例子;每一個(gè)category都可以有很多items,每一個(gè)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>
如果只對(duì)關(guān)聯(lián)的反向端進(jìn)行了改變,這個(gè)改變不會(huì)被持久化。 這表示Hibernate為每個(gè)雙向關(guān)聯(lián)在內(nèi)存中存在兩次表現(xiàn),一個(gè)從A連接到B,另一個(gè)從B連接到A。如果你回想一下Java對(duì)象模型,我們是如何在Java中創(chuàng)建多對(duì)多關(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ù)庫(kù)中。
要建立一個(gè)一對(duì)多的雙向關(guān)聯(lián),你可以通過(guò)把一個(gè)一對(duì)多關(guān)聯(lián),作為一個(gè)多對(duì)一關(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"不會(huì)影響級(jí)聯(lián)操作,二者是正交的概念!
有三種可能的途徑來(lái)映射一個(gè)三重關(guān)聯(lián)。第一種是使用一個(gè)Map,把一個(gè)關(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>
第二種方法是簡(jiǎn)單的把關(guān)聯(lián)重新建模為一個(gè)實(shí)體類(lèi)。這使我們最經(jīng)常使用的方法。
最后一種選擇是使用復(fù)合元素,我們會(huì)在后面討論
如果你完全信奉我們對(duì)于“聯(lián)合主鍵(composite keys)是個(gè)壞東西”,和“實(shí)體應(yīng)該使用(無(wú)機(jī)的)自己生成的代用標(biāo)識(shí)符(surrogate keys)”的觀點(diǎn),也許你會(huì)感到有一些奇怪,我們目前為止展示的多對(duì)多關(guān)聯(lián)和值集合都是映射成為帶有聯(lián)合主鍵的表的!現(xiàn)在,這一點(diǎn)非常值得爭(zhēng)辯;看上去一個(gè)單純的關(guān)聯(lián)表并不能從代用標(biāo)識(shí)符中獲得什么好處(雖然使用組合值的集合可能會(huì)獲得一點(diǎn)好處)。不過(guò),Hibernate提供了一個(gè)(一點(diǎn)點(diǎn)試驗(yàn)性質(zhì)的)功能,讓你把多對(duì)多關(guān)聯(lián)和值集合應(yīng)得到一個(gè)使用代用標(biāo)識(shí)符的表去。
<idbag> 屬性讓你使用bag語(yǔ)義來(lái)映射一個(gè)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í)體類(lèi)一樣!集合的每一行都有一個(gè)不同的人造關(guān)鍵字。但是,Hibernate沒(méi)有提供任何機(jī)制來(lái)讓你取得某個(gè)特定行的人造關(guān)鍵字。
注意<idbag>的更新性能要比普通的<bag>高得多!Hibernate可以有效的定位到不同的行,分別進(jìn)行更新或刪除工作,就如同處理一個(gè)list, map或者set一樣。
在目前的實(shí)現(xiàn)中,還不支持使用identity標(biāo)識(shí)符生成器策略來(lái)生成<idbag>集合的標(biāo)識(shí)符。
在前面的幾個(gè)章節(jié)的確非常令人迷惑。 因此讓我們來(lái)看一個(gè)例子。這個(gè)類(lèi):
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; } .... .... }
這個(gè)類(lèi)有一個(gè)Child的實(shí)例集合。如果每一個(gè)子實(shí)例至多有一個(gè)父實(shí)例, 那么最自然的映射是一個(gè)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)了這個(gè)映射關(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>
請(qǐng)注意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
另外,如果你絕對(duì)堅(jiān)持這個(gè)關(guān)聯(lián)應(yīng)該是單向的,你可以對(duì)<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>
另外一方面,如果一個(gè)子實(shí)例可能有多個(gè)父實(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
更多的例子,以及一個(gè)完整的父/子關(guān)系映射的排練,請(qǐng)參閱第?22?章 示例:父子關(guān)系(Parent Child Relationships).
甚至可能出現(xiàn)更加復(fù)雜的關(guān)聯(lián)映射,我們會(huì)在下一章中列出所有可能性。
關(guān)聯(lián)關(guān)系映射通常情況是最難配置正確的。在這個(gè)部分中,我們從單向關(guān)系映射開(kāi)始,然后考慮雙向關(guān)系映射,由淺至深講述一遍典型的案例。在所有的例子中,我們都使用 Person和Address。
我們根據(jù)映射關(guān)系是否涉及連接表以及多樣性來(lái)劃分關(guān)聯(lián)類(lèi)型。
在傳統(tǒng)的數(shù)據(jù)建模中,允許為Null值的外鍵被認(rèn)為是一種不好的實(shí)踐,因此我們所有的例子中都使用不允許為Null的外鍵。這并不是Hibernate的要求,即使你刪除掉不允許為Null的約束,Hibernate映射一樣可以工作的很好。
單向many-to-one關(guān)聯(lián) 是最常見(jiàn)的單向關(guān)聯(lián)關(guān)系。
<class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <many-to-one name="address" column="addressId" not-null="true"/> </class> <class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> </class>
create table Person ( personId bigint not null primary key, addressId bigint not null ) create table Address ( addressId bigint not null primary key )
基于外鍵關(guān)聯(lián)的單向一對(duì)一關(guān)聯(lián) 和單向多對(duì)一關(guān)聯(lián)幾乎是一樣的。唯一的不同就是單向一對(duì)一關(guān)聯(lián)中的外鍵字段具有唯一性約束。
<class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <many-to-one name="address" column="addressId" unique="true" not-null="true"/> </class> <class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> </class>
create table Person ( personId bigint not null primary key, addressId bigint not null unique ) create table Address ( addressId bigint not null primary key )
基于主鍵關(guān)聯(lián)的單向一對(duì)一關(guān)聯(lián) 通常使用一個(gè)特定的id生成器。(請(qǐng)注意,在這個(gè)例子中我們掉換了關(guān)聯(lián)的方向。)
<class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> </class> <class name="Address"> <id name="id" column="personId"> <generator class="foreign"> <param name="property">person</param> </generator> </id> <one-to-one name="person" constrained="true"/> </class>
create table Person ( personId bigint not null primary key ) create table Address ( personId bigint not null primary key )
基于外鍵關(guān)聯(lián)的單向一對(duì)多關(guān)聯(lián) 是一種很少見(jiàn)的情況,并不推薦使用。
<class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <set name="addresses"> <key column="personId" not-null="true"/> <one-to-many class="Address"/> </set> </class> <class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> </class>
create table Person ( personId bigint not null primary key ) create table Address ( addressId bigint not null primary key, personId bigint not null )
我們認(rèn)為對(duì)于這種關(guān)聯(lián)關(guān)系最好使用連接表。
基于連接表的單向一對(duì)多關(guān)聯(lián) 應(yīng)該優(yōu)先被采用。請(qǐng)注意,通過(guò)指定unique="true",我們可以把多樣性從多對(duì)多改變?yōu)橐粚?duì)多。
<class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <set name="addresses" table="PersonAddress"> <key column="personId"/> <many-to-many column="addressId" unique="true" class="Address"/> </set> </class> <class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> </class>
create table Person ( personId bigint not null primary key ) create table PersonAddress ( personId not null, addressId bigint not null primary key ) create table Address ( addressId bigint not null primary key )
基于連接表的單向多對(duì)一關(guān)聯(lián) 在關(guān)聯(lián)關(guān)系可選的情況下應(yīng)用也很普遍。
<class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <join table="PersonAddress" optional="true"> <key column="personId" unique="true"/> <many-to-one name="address" column="addressId" not-null="true"/> </join> </class> <class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> </class>
create table Person ( personId bigint not null primary key ) create table PersonAddress ( personId bigint not null primary key, addressId bigint not null ) create table Address ( addressId bigint not null primary key )
基于連接表的單向一對(duì)一關(guān)聯(lián) 非常少見(jiàn),但也是可行的。
<class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <join table="PersonAddress" optional="true"> <key column="personId" unique="true"/> <many-to-one name="address" column="addressId" not-null="true" unique="true"/> </join> </class> <class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> </class>
create table Person ( personId bigint not null primary key ) create table PersonAddress ( personId bigint not null primary key, addressId bigint not null unique ) create table Address ( addressId bigint not null primary key )
最后,還有 單向多對(duì)多關(guān)聯(lián).
<class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <set name="addresses" table="PersonAddress"> <key column="personId"/> <many-to-many column="addressId" class="Address"/> </set> </class> <class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> </class>
create table Person ( personId bigint not null primary key ) create table PersonAddress ( personId bigint not null, addressId bigint not null, primary key (personId, addressId) ) create table Address ( addressId bigint not null primary key )
雙向多對(duì)一關(guān)聯(lián) 是最常見(jiàn)的關(guān)聯(lián)關(guān)系。(這也是標(biāo)準(zhǔn)的父/子關(guān)聯(lián)關(guān)系。)
<class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <many-to-one name="address" column="addressId" not-null="true"/> </class> <class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> <set name="people" inverse="true"> <key column="addressId"/> <one-to-many class="Person"/> </set> </class>
create table Person ( personId bigint not null primary key, addressId bigint not null ) create table Address ( addressId bigint not null primary key )
基于外鍵關(guān)聯(lián)的雙向一對(duì)一關(guān)聯(lián) 也很常見(jiàn)。
<class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <many-to-one name="address" column="addressId" unique="true" not-null="true"/> </class> <class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> <one-to-one name="person" property-ref="address"/> </class>
create table Person ( personId bigint not null primary key, addressId bigint not null unique ) create table Address ( addressId bigint not null primary key )
基于主鍵關(guān)聯(lián)的一對(duì)一關(guān)聯(lián) 需要使用特定的id生成器。
<class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <one-to-one name="address"/> </class> <class name="Address"> <id name="id" column="personId"> <generator class="foreign"> <param name="property">person</param> </generator> </id> <one-to-one name="person" constrained="true"/> </class>
create table Person ( personId bigint not null primary key ) create table Address ( personId bigint not null primary key )
基于連接表的雙向一對(duì)多關(guān)聯(lián) 。注意inverse="true"可以出現(xiàn)在關(guān)聯(lián)的任意一端,即collection端或者join端。
<class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <set name="addresses" table="PersonAddress"> <key column="personId"/> <many-to-many column="addressId" unique="true" class="Address"/> </set> </class> <class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> <join table="PersonAddress" inverse="true" optional="true"> <key column="addressId"/> <many-to-one name="person" column="personId" not-null="true"/> </join> </class>
create table Person ( personId bigint not null primary key ) create table PersonAddress ( personId bigint not null, addressId bigint not null primary key ) create table Address ( addressId bigint not null primary key )
基于連接表的雙向一對(duì)一關(guān)聯(lián) 極為罕見(jiàn),但也是可行的。
<class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <join table="PersonAddress" optional="true"> <key column="personId" unique="true"/> <many-to-one name="address" column="addressId" not-null="true" unique="true"/> </join> </class> <class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> <join table="PersonAddress" optional="true" inverse="true"> <key column="addressId" unique="true"/> <many-to-one name="address" column="personId" not-null="true" unique="true"/> </join> </class>
create table Person ( personId bigint not null primary key ) create table PersonAddress ( personId bigint not null primary key, addressId bigint not null unique ) create table Address ( addressId bigint not null primary key )
最后,還有 雙向多對(duì)多關(guān)聯(lián).
<class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <set name="addresses"> <key column="personId"/> <many-to-many column="addressId" class="Address"/> </set> </class> <class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> <set name="people" inverse="true"> <key column="addressId"/> <many-to-many column="personId" class="Person"/> </set> </class>
create table Person ( personId bigint not null primary key ) create table PersonAddress ( personId bigint not null, addressId bigint not null, primary key (personId, addressId) ) create table Address ( addressId bigint not null primary key )
Component 這個(gè)概念在Hibernate中幾處不同的地方為了不同的目的被重復(fù)使用.
Component是一個(gè)被包含的對(duì)象,它作為值類(lèi)型被持久化,而非一個(gè)被引用的實(shí)體。“component(組件)”這一術(shù)語(yǔ)指的是面向?qū)ο蟮暮铣筛拍睿ǘ⒉皇窍到y(tǒng)構(gòu)架層次上的組件的概念)舉個(gè)例子, 你可以對(duì)人(Person)如以下這樣來(lái)建模:
public class Person { private java.util.Date birthday; private Name name; private String key; public String getKey() { return key; } private void setKey(String key) { this.key=key; } public java.util.Date getBirthday() { return birthday; } public void setBirthday(java.util.Date birthday) { this.birthday = birthday; } public Name getName() { return name; } public void setName(Name name) { this.name = name; } ...... ...... }
public class Name { char initial; String first; String last; public String getFirst() { return first; } void setFirst(String first) { this.first = first; } public String getLast() { return last; } void setLast(String last) { this.last = last; } public char getInitial() { return initial; } void setInitial(char initial) { this.initial = initial; } }
現(xiàn)在,姓名(Name)是作為人(Person)的一個(gè)組成部分。需要注意的是:需要對(duì)姓名 的持久化屬性定義getter和setter方法,但是不需要實(shí)現(xiàn)任何的接口或申明標(biāo)識(shí)符字段。
以下是這個(gè)例子的Hibernate映射文件:
<class name="eg.Person" table="person"> <id name="Key" column="pid" type="string"> <generator class="uuid.hex"/> </id> <property name="birthday" type="date"/> <component name="Name" class="eg.Name"> <!-- class attribute optional --> <property name="initial"/> <property name="first"/> <property name="last"/> </component> </class>
人員(Person)表中將包括pid, birthday, initial, first和 last等字段。
就像所有的值類(lèi)型一樣, Component不支持共享引用。 換句話說(shuō),兩個(gè)人可能重名,但是兩個(gè)person對(duì)象應(yīng)該包含兩個(gè)獨(dú)立的name對(duì)象,只不過(guò)是具有“同樣”的值。 Component的值為空從語(yǔ)義學(xué)上來(lái)講是專(zhuān)有的(ad hoc)。 每當(dāng) 重新加載一個(gè)包含組件的對(duì)象,如果component的所有字段為空,那么將Hibernate將假定整個(gè)component為 空。對(duì)于絕大多數(shù)目的,這樣假定是沒(méi)有問(wèn)題的。
Component的屬性可以是Hibernate類(lèi)型(包括Collections, many-to-one 關(guān)聯(lián), 以及其它Component 等等)。嵌套Component不應(yīng)該作為特殊的應(yīng)用被考慮(Nested components should not be considered an exotic usage)。 Hibernate趨向于支持設(shè)計(jì)細(xì)致(fine-grained)的對(duì)象模型。
<component> 元素還允許有 <parent>子元素 ,用來(lái)表明component類(lèi)中的一個(gè)屬性返回包含它的實(shí)體的引用。
<class name="eg.Person" table="person"> <id name="Key" column="pid" type="string"> <generator class="uuid.hex"/> </id> <property name="birthday" type="date"/> <component name="Name" class="eg.Name" unique="true">> <parent name="namedPerson"/> <!-- reference back to the Person --> <property name="initial"/> <property name="first"/> <property name="last"/> </component> </class>
Hibernate支持component的集合(例如: 一個(gè)元素是“姓名”這種類(lèi)型的數(shù)組)。 你可以使用<composite-element>標(biāo)簽替代<element>標(biāo)簽來(lái)定義你的component集合。
<set name="someNames" table="some_names" lazy="true"> <key column="id"/> <composite-element class="eg.Name"> <!-- class attribute required --> <property name="initial"/> <property name="first"/> <property name="last"/> </composite-element> </set>
注意,如果你決定定義一個(gè)元素是聯(lián)合元素的Set,正確地實(shí)現(xiàn)equals()和hashCode()是非常重要的。
組合元素可以包含component但是不能包含集合。如果你的組合元素自身包含component, 必須使用<nested-composite-element>標(biāo)簽。這是一個(gè)相當(dāng)特殊的案例 - 組合元素的集合自身可以包含component。 這個(gè)時(shí)候你就應(yīng)該考慮一下使用one-to-many關(guān)聯(lián)是否會(huì)更恰當(dāng)。 嘗試對(duì)這個(gè)組合元素重新建模為一個(gè)實(shí)體-但是需要注意的是,雖然Java模型和重新建模前 是一樣的,關(guān)系模型和持久性語(yǔ)義上仍然存在輕微的區(qū)別。
請(qǐng)注意如果你使用<set>標(biāo)簽,一個(gè)組合元素的映射不支持可能為空的屬性. 當(dāng)刪除對(duì)象時(shí), Hibernate必須使用每一個(gè)字段的來(lái)確定一條記錄(在組合元素表中,沒(méi)有單個(gè)的關(guān)鍵字段), 如果有為null的字段,這樣做就不可能了。你必須作出一個(gè)選擇,要么在組合元素中使用不能為空的屬性, 要么選擇使用<list>, <map>,<bag> 或者 <idbag>而不是 <set>。
組合元素有個(gè)特別的案例,是組合元素可以包含一個(gè)<many-to-one> 元素。類(lèi)似這樣的映射允許你映射一個(gè)many-to-mang關(guān)聯(lián)表作為組合元素額外的字段。(A mapping like this allows you to map extra columns of a many-to-many association table to the composite element class.) 接下來(lái)的的例子是從Order到Item的一個(gè)多對(duì)多的關(guān)聯(lián)關(guān)系,而 purchaseDate, price 和 quantity 是Item的關(guān)聯(lián)屬性。
<class name="eg.Order" .... > .... <set name="purchasedItems" table="purchase_items" lazy="true"> <key column="order_id"> <composite-element class="eg.Purchase"> <property name="purchaseDate"/> <property name="price"/> <property name="quantity"/> <many-to-one name="item" class="eg.Item"/> <!-- class attribute is optional --> </composite-element> </set> </class>
當(dāng)然,在另一方面,無(wú)法存在指向purchase的關(guān)聯(lián),因此不能實(shí)現(xiàn)雙向關(guān)聯(lián)查詢。記住組建是值類(lèi)型,并且不允許共享關(guān)聯(lián)。單個(gè)Purchase 可以放在包含Order的集合中,但它不能同時(shí)被Item所關(guān)聯(lián)。
即使三重或多重管理都是可能的:
<class name="eg.Order" .... > .... <set name="purchasedItems" table="purchase_items" lazy="true"> <key column="order_id"> <composite-element class="eg.OrderLine"> <many-to-one name="purchaseDetails" class="eg.Purchase"/> <many-to-one name="item" class="eg.Item"/> </composite-element> </set> </class>
在查詢中,組合元素使用的語(yǔ)法是和關(guān)聯(lián)到其他實(shí)體的語(yǔ)法一樣的。
<composite-map-key>元素允許你映射一個(gè)Component類(lèi)作為Map的key, 但是你必須確定你正確的在這個(gè)類(lèi)中重寫(xiě)了hashCode() 和 equals()方法。
你可以使用一個(gè)component作為一個(gè)實(shí)體類(lèi)的標(biāo)識(shí)符。 你的component類(lèi)必須滿足以下要求:
-
它必須實(shí)現(xiàn)java.io.Serializable接口
-
它必須重新實(shí)現(xiàn)equals()和hashCode()方法, 始終和組合關(guān)鍵字在數(shù)據(jù)庫(kù)中的概念保持一致
注意:在Hibernate3中,第二種要求并非是Hibernate強(qiáng)制必須的。但最好這樣做。
你不能使用一個(gè)IdentifierGenerator產(chǎn)生組合關(guān)鍵字。作為替代應(yīng)用程序必須分配它自己的標(biāo)識(shí)符。
使用<composite-id> 標(biāo)簽(并且內(nèi)嵌<key-property>元素)代替通常的<id>標(biāo)簽。 比如,OrderLine類(lèi)具有一個(gè)依賴Order的(聯(lián)合)主鍵的主鍵。
<class name="OrderLine"> <composite-id name="id" class="OrderLineId"> <key-property name="lineId"/> <key-property name="orderId"/> <key-property name="customerId"/> </composite-id> <property name="name"/> <many-to-one name="order" class="Order" insert="false" update="false"> <column name="orderId"/> <column name="customerId"/> </many-to-one> .... </class>
現(xiàn)在,任何關(guān)聯(lián)到OrderLine 的外鍵都是復(fù)合的。在你的映射文件中,必須為其他類(lèi)也這樣聲明。指向OrderLine的關(guān)聯(lián)可能被這樣映射:
<many-to-one name="orderLine" class="OrderLine"> <!-- the "class" attribute is optional, as usual --> <column name="lineId"/> <column name="orderId"/> <column name="customerId"/> </many-to-one>
(注意在各個(gè)地方<column>標(biāo)簽都是column屬性的替代寫(xiě)法。)
指向OrderLine的多對(duì)多關(guān)聯(lián)也使用聯(lián)合外鍵:
<set name="undeliveredOrderLines"> <key column name="warehouseId"/> <many-to-many class="OrderLine"> <column name="lineId"/> <column name="orderId"/> <column name="customerId"/> </many-to-many> </set>
在Order中, OrderLine的集合則是這樣:
<set name="orderLines" inverse="true"> <key> <column name="orderId"/> <column name="customerId"/> </key> <one-to-many class="OrderLine"/> </set>
(與通常一樣,<one-to-many>元素不聲明任何列.)
假若OrderLine本身?yè)碛幸粋€(gè)集合,它也具有組合外鍵。
<class name="OrderLine"> .... .... <list name="deliveryAttempts"> <key> <!-- a collection inherits the composite key type --> <column name="lineId"/> <column name="orderId"/> <column name="customerId"/> </key> <list-index column="attemptId" base="1"/> <composite-element class="DeliveryAttempt"> ... </composite-element> </set> </class>
你甚至可以映射Map類(lèi)型的屬性:
<dynamic-component name="userAttributes"> <property name="foo" column="FOO"/> <property name="bar" column="BAR"/> <many-to-one name="baz" class="Baz" column="BAZ_ID"/> </dynamic-component>
從<dynamic-component>映射的語(yǔ)義上來(lái)講,它和<component>是相同的。 這種映射類(lèi)型的優(yōu)點(diǎn)在于通過(guò)修改映射文件,就可以具有在部署時(shí)檢測(cè)真實(shí)屬性的能力.利用一個(gè)DOM解析器,是有可能在運(yùn)行時(shí)刻操作映射文件的。 更好的是,你可以通過(guò)Configuration對(duì)象來(lái)訪問(wèn)(或者修改)Hibernate的運(yùn)行時(shí)元模型。
Hibernate支持三種基本的繼承映射策略:
-
每個(gè)類(lèi)分層結(jié)構(gòu)一張表(table per class hierarchy)
-
每個(gè)子類(lèi)一張表(table per subclass)
-
每個(gè)具體類(lèi)一張表(table per concrete class)
此外,Hibernate還支持第四種稍有不同的多態(tài)映射策略:
-
隱式多態(tài)(implicit polymorphism)
對(duì)于同一個(gè)繼承層次內(nèi)的不同分支,可以采用不同的映射策略,然后用隱式多 態(tài)來(lái)完成跨越整個(gè)層次的多態(tài)。但是在同一個(gè)<class>根元素 下,Hibernate不支持混合了元素<subclass>、 <joined-subclass>和<union-subclass> 的映射。在同一個(gè)<class>元素下,可以混合使用 “每個(gè)類(lèi)分層結(jié)構(gòu)一張表”(table per hierarchy) 和“每個(gè)子類(lèi)一張表”(table per subclass) 這兩種映射策略,這是通過(guò)結(jié)合元素<subclass>和 <join>來(lái)實(shí)現(xiàn)的(見(jiàn)后)。
假設(shè)我們有接口Payment和它的幾個(gè)實(shí)現(xiàn)類(lèi): CreditCardPayment, CashPayment, 和ChequePayment。則“每個(gè)類(lèi)分層結(jié)構(gòu)一張表”(Table per class hierarchy)的映射代碼如下所示:
<class name="Payment" table="PAYMENT"> <id name="id" type="long" column="PAYMENT_ID"> <generator class="native"/> </id> <discriminator column="PAYMENT_TYPE" type="string"/> <property name="amount" column="AMOUNT"/> ... <subclass name="CreditCardPayment" discriminator-value="CREDIT"> <property name="creditCardType" column="CCTYPE"/> ... </subclass> <subclass name="CashPayment" discriminator-value="CASH"> ... </subclass> <subclass name="ChequePayment" discriminator-value="CHEQUE"> ... </subclass> </class>
采用這種策略只需要一張表即可。它有一個(gè)很大的限制:要求那些由子類(lèi)定義的字段, 如CCTYPE,不能有非空(NOT NULL)約束。
對(duì)于上例中的幾個(gè)類(lèi)而言,采用“每個(gè)子類(lèi)一張表”的映射策略,代碼如下所示:
<class name="Payment" table="PAYMENT"> <id name="id" type="long" column="PAYMENT_ID"> <generator class="native"/> </id> <property name="amount" column="AMOUNT"/> ... <joined-subclass name="CreditCardPayment" table="CREDIT_PAYMENT"> <key column="PAYMENT_ID"/> ... </joined-subclass> <joined-subclass name="CashPayment" table="CASH_PAYMENT"> <key column="PAYMENT_ID"/> <property name="creditCardType" column="CCTYPE"/> ... </joined-subclass> <joined-subclass name="ChequePayment" table="CHEQUE_PAYMENT"> <key column="PAYMENT_ID"/> ... </joined-subclass> </class>
需要四張表。三個(gè)子類(lèi)表通過(guò)主鍵關(guān)聯(lián)到超類(lèi)表(因而關(guān)系模型實(shí)際上是一對(duì)一關(guān)聯(lián))。
注意,對(duì)“每個(gè)子類(lèi)一張表”的映射策略,Hibernate的實(shí)現(xiàn)不需要辨別字段,而其他 的對(duì)象/關(guān)系映射工具使用了一種不同于Hibernate的實(shí)現(xiàn)方法,該方法要求在超類(lèi) 表中有一個(gè)類(lèi)型辨別字段(type discriminator column)。Hibernate采用的方法更 難實(shí)現(xiàn),但從關(guān)系(數(shù)據(jù)庫(kù))這點(diǎn)上來(lái)看,按理說(shuō)它更正確。若你愿意使用帶有辨別字 段的“每個(gè)子類(lèi)一張表”的策略,你可以結(jié)合使用<subclass> 與<join>,如下所示:
<class name="Payment" table="PAYMENT"> <id name="id" type="long" column="PAYMENT_ID"> <generator class="native"/> </id> <discriminator column="PAYMENT_TYPE" type="string"/> <property name="amount" column="AMOUNT"/> ... <subclass name="CreditCardPayment" discriminator-value="CREDIT"> <join table="CREDIT_PAYMENT"> <property name="creditCardType" column="CCTYPE"/> ... </join> </subclass> <subclass name="CashPayment" discriminator-value="CASH"> <join table="CASH_PAYMENT"> ... </join> </subclass> <subclass name="ChequePayment" discriminator-value="CHEQUE"> <join table="CHEQUE_PAYMENT" fetch="select"> ... </join> </subclass> </class>
可選的聲明fetch="select",是用來(lái)告訴Hibernate,在查詢超類(lèi)時(shí), 不要使用外部連接(outer join)來(lái)抓取子類(lèi)ChequePayment的數(shù)據(jù)。
你甚至可以采取如下方法混和使用“每個(gè)類(lèi)分層結(jié)構(gòu)一張表”和“每個(gè)子類(lèi)一張表”這兩種策略:
<class name="Payment" table="PAYMENT"> <id name="id" type="long" column="PAYMENT_ID"> <generator class="native"/> </id> <discriminator column="PAYMENT_TYPE" type="string"/> <property name="amount" column="AMOUNT"/> ... <subclass name="CreditCardPayment" discriminator-value="CREDIT"> <join table="CREDIT_PAYMENT"> <property name="creditCardType" column="CCTYPE"/> ... </join> </subclass> <subclass name="CashPayment" discriminator-value="CASH"> ... </subclass> <subclass name="ChequePayment" discriminator-value="CHEQUE"> ... </subclass> </class>
對(duì)上述任何一種映射策略而言,指向根類(lèi)Payment的 關(guān)聯(lián)是使用<many-to-one>進(jìn)行映射的。
<many-to-one name="payment" column="PAYMENT_ID" class="Payment"/>
對(duì)于“每個(gè)具體類(lèi)一張表”的映射策略,可以采用兩種方法。第一種方法是使用 <union-subclass>。
<class name="Payment"> <id name="id" type="long" column="PAYMENT_ID"> <generator class="sequence"/> </id> <property name="amount" column="AMOUNT"/> ... <union-subclass name="CreditCardPayment" table="CREDIT_PAYMENT"> <property name="creditCardType" column="CCTYPE"/> ... </union-subclass> <union-subclass name="CashPayment" table="CASH_PAYMENT"> ... </union-subclass> <union-subclass name="ChequePayment" table="CHEQUE_PAYMENT"> ... </union-subclass> </class>
這里涉及三張表。每張表為對(duì)應(yīng)類(lèi)的所有屬性(包括從超類(lèi)繼承的屬性)定義相應(yīng)字段。
這種方式的局限在于,如果一個(gè)屬性在超類(lèi)中做了映射,其字段名必須與所有子類(lèi) 表中定義的相同。(我們可能會(huì)在Hibernate的后續(xù)發(fā)布版本中放寬此限制。) 不允許在聯(lián)合子類(lèi)(union subclass)的繼承層次中使用標(biāo)識(shí)生成器策略(identity generator strategy), 實(shí)際上, 主鍵的種子(primary key seed)不得不為同一繼承層次中的全部被聯(lián)合子類(lèi)所共用.
另一種可供選擇的方法是采用隱式多態(tài):
<class name="CreditCardPayment" table="CREDIT_PAYMENT"> <id name="id" type="long" column="CREDIT_PAYMENT_ID"> <generator class="native"/> </id> <property name="amount" column="CREDIT_AMOUNT"/> ... </class> <class name="CashPayment" table="CASH_PAYMENT"> <id name="id" type="long" column="CASH_PAYMENT_ID"> <generator class="native"/> </id> <property name="amount" column="CASH_AMOUNT"/> ... </class> <class name="ChequePayment" table="CHEQUE_PAYMENT"> <id name="id" type="long" column="CHEQUE_PAYMENT_ID"> <generator class="native"/> </id> <property name="amount" column="CHEQUE_AMOUNT"/> ... </class>
注意,我們沒(méi)有在任何地方明確的提及接口Payment。同時(shí)注意 Payment的屬性在每個(gè)子類(lèi)中都進(jìn)行了映射。如果你想避免重復(fù), 可以考慮使用XML實(shí)體(例如:位于DOCTYPE聲明內(nèi)的 [ <!ENTITY allproperties SYSTEM "allproperties.xml"> ] 和映射中的&allproperties;)。
這種方法的缺陷在于,在Hibernate執(zhí)行多態(tài)查詢時(shí)(polymorphic queries)無(wú)法生成帶 UNION的SQL語(yǔ)句。
對(duì)于這種映射策略而言,通常用<any>來(lái)實(shí)現(xiàn)到 Payment的多態(tài)關(guān)聯(lián)映射。
<any name="payment" meta-type="string" id-type="long"> <meta-value value="CREDIT" class="CreditCardPayment"/> <meta-value value="CASH" class="CashPayment"/> <meta-value value="CHEQUE" class="ChequePayment"/> <column name="PAYMENT_CLASS"/> <column name="PAYMENT_ID"/> </any>
對(duì)這一映射還有一點(diǎn)需要注意。因?yàn)槊總€(gè)子類(lèi)都在各自獨(dú)立的元素<class> 中映射(并且Payment只是一個(gè)接口),每個(gè)子類(lèi)可以很容易的成為另一 個(gè)繼承體系中的一部分!(你仍然可以對(duì)接口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>
我們還是沒(méi)有明確的提到Payment。 如果我們針對(duì)接口Payment執(zhí)行查詢 ——如from Payment—— Hibernate 自動(dòng)返回CreditCardPayment(和它的子類(lèi),因?yàn)?它們也實(shí)現(xiàn)了接口Payment)、 CashPayment和Chequepayment的實(shí)例, 但不返回NonelectronicTransaction的實(shí)例。
對(duì)“每個(gè)具體類(lèi)映射一張表”(table per concrete-class)的映射策略而言,隱式多態(tài)的 方式有一定的限制。而<union-subclass>映射的限制則沒(méi)有那 么嚴(yán)格。
下面表格中列出了在Hibernte中“每個(gè)具體類(lèi)一張表”的策略和隱式多態(tài)的限制。
表?10.1.?繼承映射特性(Features of inheritance mappings)
繼承策略(Inheritance strategy) | 多態(tài)多對(duì)一 | 多態(tài)一對(duì)一 | 多態(tài)一對(duì)多 | 多態(tài)多對(duì)多 | 多態(tài) load()/get() | 多態(tài)查詢 | 多態(tài)連接(join) | 外連接(Outer join)抓取 |
---|---|---|---|---|---|---|---|---|
每個(gè)類(lèi)分層結(jié)構(gòu)一張表 | <many-to-one> | <one-to-one> | <one-to-many> | <many-to-many> | s.get(Payment.class, id) | from Payment p | from Order o join o.payment p | 支持 |
每個(gè)子類(lèi)一張表 | <many-to-one> | <one-to-one> | <one-to-many> | <many-to-many> | s.get(Payment.class, id) | from Payment p | from Order o join o.payment p | 支持 |
每個(gè)具體類(lèi)一張表(union-subclass) | <many-to-one> | <one-to-one> | <one-to-many> (僅對(duì)于inverse="true"的情況) | <many-to-many> | s.get(Payment.class, id) | from Payment p | from Order o join o.payment p | 支持 |
每個(gè)具體類(lèi)一張表(隱式多態(tài)) | <any> | 不支持 | 不支持 | <many-to-any> | s.createCriteria(Payment.class).add( Restrictions.idEq(id) ).uniqueResult() | from Payment p | 不支持 | 不支持 |