<hibernate-mapping schema="schemaName" (1) default-cascade="none|save-update" (2) auto-import="true|false" (3) assembly="Eg" (4) namespace="Eg" (5) />
各選項說明:
(1) | schema (optional): 數據庫schema名稱。 |
(2) | default-cascade (可選 - 默認為 none): 默認的級聯風格。 |
(3) | auto-import (optional - defaults to true): 指定我們在使用查詢語句的時候是否可以使用非全限定名。 |
(4)(5) | assembly and namespace(可選): 指定映射文件中的類的應用程序集名稱和其所在的名稱空間名,用來生成類的非全限定名。 |
如果沒有設置assembly何namespace標簽,我們不得不使用類的非全限定名 (namespace.類名,assembly).
假若你有兩個持久化類,它們的非全限定名是一樣的,你應該設置auto-import="false"。 假若說你把一個“import過”的名字同時對應兩個類, NHibernate會拋出一個異常。
<class name="ClassName" (1) table="tableName" (2) discriminator-value="discriminator_value" (3) mutable="true|false" (4) schema="owner" (5) proxy="ProxyInterface" (6) dynamic-update="true|false" (7) dynamic-insert="true|false" (8) select-before-update="true|false" (9) polymorphism="implicit|explicit" (10) where="arbitrary sql where condition" (11) persister="PersisterClass" (12) batch-size="N" (13) optimistic-lock="none|version|dirty|all" (14) lazy="true|false" (15) abstract="true|false" (16) />
各選項說明:
(1) | name: 持久化類(或者接口)的.NET全限定名。 |
(2) | table: 對應的數據庫表名。 |
(3) | discriminator-value(辨別值) (可選 - 默認和類名一樣):一個用於區分不同的子類的值,在多態行為時使用。可選值包括null和not null。 |
(4) | mutable (可選, 默認值為 true): 表明該類的實例可變(不可變)。 |
(5) | schema (可選) 覆蓋在根元素中指定的schema名字。 |
(6) | proxy (可選)指定一個接口,在延遲裝載時作為代理使用。你可以在這里使用該類自己的名字。 |
(7) | dynamic-update (可選,默認為false): 指定用於UPDATE 的SQL將會在運行時動態生成,並且只更新那些改變過的字段。 |
(8) | dynamic-insert (可選, 默認為false): 指定用於 INSERT的 SQL 將會在運行時動態生成,並且只包含那些非空值字段。 |
(9) | select-before-update (可選,默認值為 false): 指定NHibernate除非確定對象的確被修改了,UPDATE操作。在特定場合(實際上,只會發生在一個臨時對象關聯到一個新的session中去,執行update()的時候),這說明NHibernate會在UPDATE之前執行一次額外的SQL SELECT操作,來決定是否應該進行UPDATE。 |
(10) | polymorphism (可選, 默認值為 implicit (隱式)): 界定是隱式還是顯式的使用查詢多態。 |
(11) | where (可選) 指定一個附加的SQL WHERE 條件,在抓取這個類的對象時會一直增加這個條件。 |
(12) | persister (可選): 指定一個定制的 IClassPersister。 |
(13) | batch-size (可選,默認是1) 指定一個用於根據標識符抓取實例時使用的"batch size"(批次抓取數量)。 |
(14) | optimistic-lock (樂觀鎖定) (可選,默認是version): 決定樂觀鎖定的策略。 |
(15) | lazy (可選): 假若設置lazy="false",就會禁用延遲加載。 |
(16) | abstract(可選) 用於在<union-subclass>的繼承結構 (hierarchies)中標識抽象超類。 |
若指明的持久化類實際上是一個接口,這也是完全可以接受的。 之后你可以用元素 <subclass>來指定該接口的實際實現類。 你可以持久化任何static(靜態的)內部類。 你應該使用標准的類名格式來指定類名,比如:Eg.Foo+Bar, Eg。由於HQL解析器的限制NHibernate 1.0 無法在查詢里使用內部類。
不可變類 mutable="false"不可以被應用程序更新或者刪除。 這可以讓NHibernate做一些小小的性能優化。
可選的proxy屬性允許延遲加載類的持久化實例。 NHibernate開始會返回實現了這個命名接口的代理類。當代理的某個方法被實際調用的時候, 真實的持久化對象才會被裝載。參見下面的“用於延遲裝載的代理”。
Implicit (隱式)的多態是指,如果查詢時給出的是任何超類、該類實現的接口或者該類的 名字,都會返回這個類的實例;如果查詢中給出的是子類的名字,則會返回子類的實例。 Explicit (顯式)的多態是指,只有在查詢時給出明確的該類名字時才會返回這個類的實例; 同時只有在這個<class>的定義中作為<subclass>或者<joined-subclass>出現的子類,才會可能返回。 在大多數情況下,默認的polymorphism="implicit"都是合適的。 顯式的多態在有兩個不同的類映射到同一個表的時候很有用。(允許一個“輕型”的類,只包含部分表字段)。
persister屬性可以讓你定制這個類使用的持久化策略。 你可以指定你自己實現 NHibernate.Persister.EntityPersister的子類,你甚至可以完全從頭開始編寫一個 NHibernate.Persister.IClassPersister接口的實現, 比如是用儲存過程調用、序列化到文件或者LDAP數據庫來實現。 參閱NHibernate.DomainModel.CustomPersister,這是一個簡單的例子 (“持久化”Hashtable)。
請注意dynamic-updatee和dynamic-insert的設置並不會繼承到子類, 所以在<subclass>或者<joined-subclass>元素中可能 需要再次設置。這些設置是否能夠提高效率要視情形而定。請用你的智慧決定是否使用。
使用select-before-update通常會降低性能。如果你重新連接一個脫管(detache)對象實例 到一個Session中時,它可以防止數據庫不必要的觸發update。 這就很有用了。
如果你打開了dynamic-update,你可以選擇幾種樂觀鎖定的策略:
- version(版本檢查) 檢查version/timestamp字段
- all(全部) 檢查全部字段
- dirty(臟檢查)只檢察修改過的字段
- none(不檢查)不使用樂觀鎖定
我們非常強烈建議你在NHibernate中使用version/timestamp字段來進行樂觀鎖定。 對性能來說,這是最好的選擇,並且這也是唯一能夠處理在session外進行操作的策略(例如: 在使用ISession.Update()的時候)。注意version或者是timestamp屬性不能為null,不管是否使用了unsaved-value策略,或者是實例被作為是瞬態。
從NHibernate 1.2.0開始,版本號從1開始(以前的版本從0開始),這樣允許把version的屬性的unsaved-value設置為0。
<id name="PropertyName" (1) type="typename" (2) column="column_name" (3) unsaved-value="any|none|null|id_value" (4) access="field|property|nosetter|ClassName"> (5)
<generator class="generatorClass"/>
</id>
說明:
(1) | name (可選): 標識屬性的名字。 |
(2) | type(可選): 標識NHibernate類型的名字。 |
(3) | column(可選 - 默認為屬性名): 主鍵字段的名字。 |
(4) | unsaved-value (可選 - 默認為一個切合實際(sensible)的值): 一個特定的標識屬性值,用來標志該實例是剛剛創建的,尚未保存。 這可以把這種實例和從以前的session中裝載過(可能又做過修改--譯者注) 但未再次持久化的實例區分開來。 |
(5) | access (可選 - 默認為property): NHibernate用來訪問屬性值的策略。 |
如果name屬性不存在,會認為這個類沒有標識屬性。
四、(主鍵生成策略)generator
可選的<generator>子元素是一個.NET類的名字, 用來為該持久化類的實例生成唯一的標識。如果這個生成器實例需要某些配置值或者初始化參數, 用元素來傳遞。
<id name="Id" type="Int64" column="uid" unsaved-value="0"> <generator class="NHibernate.Id.TableHiLoGenerator"> <param name="table">uid_table</param> <param name="column">next_hi_value_column</param> </generator> </id>
所有的生成器都實現NHibernate.Id.IIdentifierGenerator接口。 這是一個非常簡單的接口; 某些應用程序可以選擇提供他們自己特定的實現。當然, NHibernate提供了很多內置的實現。下面是一些內置生成器的快捷名字:
- 1、increment
-
用於為int類型生成 唯一標識。只有在沒有其他進程往同一張表中插入數據時才能使用。 在集群下不要使用。
- 2、identity
-
對DB2,MySQL, MS SQL Server, Sybase和HypersonicSQL的內置標識字段提供支持。數據庫返回的主鍵值 返回的標識符是int類型的。
- 3、sequence
-
在DB2,PostgreSQL, Oracle, SAP DB, McKoi中使用序列(sequence), 而在Interbase中使用生成器(generator)。返回的標識符是int類型的。
- 4、hilo
-
使用一個高/低位算法來高效的生成int類型的標識符。給定一個表和字段(默認分別是是hibernate_unique_key 和next_hi)作為高位值得來源。 高/低位算法生成的標識符只在一個特定的數據庫中是唯一的。在用戶自行提供的連接中,不要使用這種生成器。
- 5、seqhilo
-
使用一個高/低位算法來高效的生成int類型的標識符,給定一個數據庫序列(sequence)的名字。
- 6、uuid.hex
-
用一個System.Guid的ToString()方法法生成字符串類型的標識符, 字符串的長度由format參數定義。
- 7、uuid.string
-
用一個新的System.Guid實例的byte[]轉化為字符串作為標示符。
- 8、guid
-
使用新的System.Guid實例作為標示符。
- 9、guid.comb
-
使用Jimmy Nilsson的算法(請參閱http://www.informit.com/articles/article.asp?p=25862)生成一個新的System.Guid標示符。
- 10、native
-
根據底層數據庫的能力選擇identity, sequence 或者hilo中的一個。
- 11、assigned
-
讓應用程序在 Save()之前為對象分配一個標示符。
- 12、foreign
-
使用另外一個相關聯的對象的標識符。通常和<one-to-one>聯合起來使用。
<id name="Id" type="Int64" 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="Int64" column="cat_id"> <generator class="seqhilo"> <param name="sequence">hi_value</param> <param name="max_lo">100</param> </generator> </id>
很不幸,你在為NHibernate自行提供IDbConnection時無法使用hilo。 NHibernate必須能夠在一個事務里獲取"hi"值。
UUID算法(UUID Algorithm )
<id name="Id" type="String" column="cat_id"> <generator class="uuid.hex"> <param name="format">format_value</param> <param name="seperator">seperator_value</param> </generator> </id>
UUID算法是調用Guid.NewGuid().ToString(format)方法生成標示符。 format參數的使用請參閱MSDN,GUID的默認分隔符是-, 這個基本不會改動。format可以決定是都替換默認的默認分隔符。 The UUID is generated by calling Guid.NewGuid().ToString(format)。
<id name="Id" type="Int64" column="uid"> <generator class="sequence"> <param name="sequence">uid_sequence</param> </generator> </id> <id name="Id" type="Int64" column="uid" unsaved-value="0"> <generator class="identity"/> </id>
對於跨平台開發,native策略會從identity, sequence 和hilo中進行選擇,選擇哪一個,這取決於底層數據庫的支持能力。
五、composite-id 聯合ID
<composite-id name="PropertyName" class="ClassName" unsaved-value="any|none" access="field|property|nosetter|ClassName"> <key-property name="PropertyName" type="typename" column="column_name"/> <key-many-to-one name="PropertyName class="ClassName" column="column_name"/></composite-id>
如果表使用聯合主鍵,你可以把類的多個屬性組合成為標識符屬性。<composite-id> 元素接受<key-property>屬性映射和 <key-many-to-one>屬性映射作為子元素。
<composite-id> <key-property name="MedicareNumber"/> <key-property name="Dependent"/> </composite-id>
你的持久化類必須重載 Equals()和GetHashCode()方法,來實現組合的標識符判斷等價.也必須實現可序列化。
不幸的是,這種組合關鍵字的方法意味着一個持久化類是它自己的標識。除了對象自己之外, 沒有什么方便的“把手”可用。你必須自己初始化持久化類的實例,在使用組合關鍵字load()持久化狀態之前, 必須填充他的聯合屬性。我們會在 7.4. 組件作為聯合標識符(Components as composite identifiers)第 7.4 節 “組件作為聯合標識符(Components as composite identifiers)”章中說明一種更加方便的方法, 把聯合標識實現為一個獨立的類,下面描述的屬性只對這種備用方法有效:
-
name (可選):一個組件類型,持有聯合標識(參見下一節)。
-
access (可選 - 默認為property): NHibernate應該使用的訪問此屬性值的策略
-
class (可選 - 默認為通過反射(reflection)得到的屬性類型) : 作為聯合標識的組件類名(參見下一節)。
<discriminator column="discriminator_column" (1) type="discriminator_type" (2) force="true|false" (3) insert="true|false" (4) formula="arbitrary SQL expression" (5) />
說明:
(1) | column (可選 - 默認為 class) 鑒別器字段的名字 |
(2) | type (可選 - 默認為 String) 一個NHibernate字段類型的名字。 |
(3) | force(強制) (可選 - 默認為 false) "強制"NHibernate指定允許的鑒別器值,即使當取得的所有實例都是根類的。 |
(4) | insert (可選 - 默認為true) 如果你的鑒別器字段也是映射為復合標識(composite identifier)的一部分, 則需將 這個值設為false。 |
(5) | formula (可選) 一個SQL表達式,在類型判斷(判斷是父類還是具體子類-譯注)時執行。可用於基於內容的鑒別器。 |
鑒別器字段的實際值是根據discriminator-value 和<class> and <subclass>元素中 的discriminator-value屬性得來的。
force屬性僅僅在這種情況下有用的:表中包含沒有被映射到持久化類的附加辨別器值。 這種情況不會經常遇到。
使用formula屬性你可以定義一個SQL表達式,用來判斷一個行數據的類型。
<discriminator formula="case when CLASS_TYPE in ('a', 'b', 'c') then 0 else 1 end" type="Int32"/>
七、版本(version)(可選)
<version column="version_column" (1) name="PropertyName" (2) type="typename" (3) access="field|property|nosetter|ClassName" (4) unsaved-value="null|negative|undefined|value" (5) generated="never|always" (6) />
說明:
(1) | column (可選 - 默認為屬性名): 指定持有版本號的字段名。 |
(2) | name 持久化類的屬性名。 |
(3) | type (可選 - 默認是 Int32): 版本號的類型。 |
(4) | access (可選 - 默認是 property): NHibernate用於訪問屬性值的策略。 |
(5) | unsaved-value(可選 - 默認是undefined): 用於標明某個實例時剛剛被實例化的(尚未保存)版本屬性值, 依靠這個值就可以把這種情況 和已經在先前的session中保存或裝載的脫管(detached)實例區分開來。 (undefined指明應被使用的標識屬性值。) |
(6) | generated(可選 - 默認是 never): 表明此版本屬性值是否實際上是由數據庫生成的。 請參閱 5.5 “數據庫生成屬性(Generated Properties)”部分的討論。 |
版本號必須是以下類型:Int64, Int32, Int16, Ticks,或者 Timestamp, TimeSpan。
<timestamp column="timestamp_column" (1) name="PropertyName" (2) access="field|property|nosetter|ClassName" (3) unsaved-value="null|undefined|value" (4) generated="never|always" (5) />
說明:
(1) | column(可選 - 默認為屬性名): 持有時間戳的字段名。 |
(2) | name: 在持久化類中的.NE風格的屬性名, 其.NE類型是 DateTime的。 |
(3) | access (可選 - 默認是 property): NHibernate用於訪問屬性值的策略。 |
(4) | unsaved-value (可選 - 默認是null): 用於標明某個實例時剛剛被實例化的(尚未保存)版本屬性值, 依靠這個值就可以把這種情況和 已經在先前的session中保存或裝載的脫管(detached)實例區分開來。 (undefined 指明使用標識屬性值進行這種判斷。) |
(5) | generated : 指出時間戳值是否實際上是由數據庫生成的.請參閱5.5 “數據庫生成屬性(Generated Properties)”的討論。 |
注意<timestamp>等價於 <version type="timestamp">。
<property>元素為類定義了一個持久化類的屬性。
<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) optimistic-lock="true|false" (7) generated="never|insert|always" (8) />
說明:
(1) | name: 屬性的名字。 |
(2) | column (可選 - 默認為屬性名字): 對應的數據庫字段名。 |
(3) | type (可選): 一個NHibernate類型的名字。 |
(4) | update, insert (可選 - 默認為 true) : 表明用於UPDATE 和/或 INSERT 的SQL語句中是否包含這個被映射了的字段。 這二者如果都設置為false 則表明這是一個“外源性(derived)”的屬性, 它的值來源於映射到同一個(或多個) 字段的某些其他屬性,或者通過一個trigger(觸發器)或其他程序生成。 |
(5) | formula (可選): 一個SQL表達式,定義了這個計算 (computed) 屬性的值。計算屬性沒有和它對應的數據庫字段。 |
(6) | access (可選 - 默認值為 property): NHibernate用來訪問屬性值的策略。 |
(7) | optimistic-lock (可選 - 默認為 true): 指定這個屬性在做更新時是否需要獲得樂觀鎖定(optimistic lock)。 換句話說,它決定這個屬性發生臟數據時版本(version)的值是否增長。 |
(8) | generated (可選 - 默認為 never): 表明此屬性值是否實際上是由數據庫生成的。 請參閱5.5 “數據庫生成屬性(Generated Properties)”的討論。 |
typename可以是如下幾種
-
NHibernate基本類型名(比如:Int32, String, Char, DateTime, Timestamp, Single, Byte[], Object, ...)。
-
一個.Net類的名字,這個類屬於一種默認基礎類型 (比如: System.Int16, System.Single, System.Char, System.String, System.DateTime, System.Byte[], ...)。
-
一個枚舉類型的名字。(比如:. eg.Color)。
-
一個可以序列化的.NET類的名字。
-
一個自定義類型的類的名字。(比如: Illflow.Type.MyCustomType)。
注意你必須為所有類型(除了NHibernate基礎類型)指定完整的應用程序集權限定名 (或者是在<hibernate-mapping>里面配置了assembly和namespace屬性)。
NHibernate支持.NET 2.0的可空類型,這些類型和對應的非可空類型處理方式是一致的, 例如:Nullable<Int32>可以對應type="Int32"或者是type="System.Int32"。
如果你沒有指定類型,NHibernate會使用反射來得到這個名字的屬性, 以此來猜測正確的NHibernate類型。NHibernate會對屬性讀取器(getter方法)的返回類進行解釋, 按照規則2,3,4的順序。然而,這並不足夠。 在某些情況下你仍然需要type屬性。 (比如,為了區別NHibernateUtil.DateTime和NHibernateUtil.Timestamp,或者為了指定一個自定義類型。)
access屬性用來讓你控制NHibernate如何在運行時訪問屬性。 在默認情況下, NHibernate會按照access-strategy.naming-strategy來格式化屬性名 .naming-strategy不是必填項。
表 5.1. 訪問策略
訪問策略名 | 描述 |
---|---|
property | 默認實現:NHibernate訪問類的set/get屬性,這種方式沒有命名策略,因為name就代表屬性的名稱。 |
field | NHibernate將會直接訪問成員變量。NHibernate使用name作為成員變量的名稱。 當對象屬性的get和set里面有額外的操作,而你不想讓NHibernate設置或者讀取對象時執行額外的操作, 可以用這個策略。當你使用HQL時需要屬性名而非字段時,就需要命名策略(Naming Strateg)。 |
nosetter | NHibernate將會在設置值時直接訪問字段,獲得值時訪問屬性。 當API使用者不能直接改變值,因而只為屬性只提供了get訪問器時, 你可以用這個策略。NHibernate使用name屬性(attribute)作為屬性(Property ), 並且需要提供字段名,所以命名策略必須(Naming Strategy)使用。 |
ClassName | 如果NHibernate內置的訪問策略(Access Strategie)不能滿足你的要求。 你可以通過實現NHibernate.Property.IPropertyAccessor接口來自己的訪問策略(Access Strategie)。 這個值需要用程序集名(Assembly)來限定,這樣就能通過 Activator.CreateInstance(string AssemblyQualifiedName)來讀取。 |
命名策略(Naming Strategy)
命名策略 | 描述 |
---|---|
camelcase | name屬性被轉換CamelCase格式來查找字段。<property name="Foo" ... >使用foo字段。 |
camelcase-underscore | name屬性被轉換CamelCase格式並添加下划線前綴來查找字段。<property name="Foo" ... >使用_foo字段。 |
lowercase | name屬性被轉換小寫格式來查找字段。<property name="FooBar" ... > 使用 foobar字段. |
lowercase-underscore | name屬性被轉換小寫格式並添加下划線前綴來查找字段。<property name="FooBar" ... >使用_foobar字段. |
pascalcase-underscore | name屬性添加下划線前綴來查找字段。<property name="Foo" ... >使用_Foo字段。 |
pascalcase-m | name屬性添加字母m前綴來查找字段。<property name="Foo" ... >使用mFoo字段。 |
pascalcase-m-underscore | name屬性添加字母m和下划線前綴來查找字段。<property name="Foo" ... > 使用m_Foo字段。 |
<many-to-one name="PropertyName" (1) column="column_name" (2) class="ClassName" (3) cascade="all|none|save-update|delete" (4) fetch="join|select" (5) update="true|false" (6) insert="true|false" (6) property-ref="PropertyNameFromAssociatedClass" (7) access="field|property|nosetter|ClassName" (8) unique="true|false" (9) optimistic-lock="true|false" (10) not-found="ignore|exception" (11) />
說明:
(1) | name:屬性名。 |
(2) | column數據庫字段名 |
(3) | class(可選 - 默認是通過反射得到屬性類型): 關聯的類的名字。 |
(4) | cascade(級聯) (可選): 指明哪些操作會從父對象級聯到關聯的對象。 |
(5) | fetch (可選 - 默認為 select): 在外連接抓取(outer-join fetching)和序列選擇抓取(sequential select fetching)兩者中選擇其一。 |
(6) | (可選 - defaults to true) 指定對應的字段是否包含在用於UPDATE 和/或 INSERT 的SQL語句中。如果二者都是false,則這是一個純粹的 “外源性(derived)”關聯,它的值是通過映射到同一個(或多個)字段的某些其他屬性得到 或者通過trigger(觸發器)、或其他程序。 |
(7) | property-ref: (可選) 指定關聯類的一個屬性,這個屬性將會和本外鍵相對應。 如果沒有指定,會使用對方關聯類的主鍵。 |
(8) | access(可選 - 默認是 property): NHibernate用來訪問屬性的策略。 |
(9) | unique (可選): 使用DDL為外鍵字段生成一個唯一約束。此外, 這也可以用作property-ref的目標屬性。這使關聯同時具有 一對一的效果。 |
(10) | optimistic-lock (可選 - 默認為 true): 指定這個屬性在做更新時是否需要獲得樂觀鎖定(optimistic lock)。 換句話說,它決定這個屬性發生臟數據時版本(version)的值是否增長。 |
(11) | not-found (可選 - 默認為 exception): 指定外鍵引用的數據不存在時如何處理: ignore會將數據不存在作為關聯到一個空對象(null)處理。 |
cascade屬性允許下列值:: all, save-update, delete, none. 設置除了none以外的其它值會傳播特定的操作到關聯的(子)對象中。參見后面的“Lifecycle Objects(自動管理生命周期的對象)”。
fetch參數允許下列兩個不同值:
-
join外連接抓取
-
select使用隔離查詢抓取
一個典型的簡單many-to-one 定義例子:
<many-to-one name="product" class="Product" column="PRODUCT_ID"/>
property-ref屬性只應該用來對付老舊的數據庫系統, 可能有外鍵指向對方關聯表的是個非主鍵字段(但是應該是一個惟一關鍵字)的情況下。 這是一種十分丑陋的關系模型。比如說,假設Product類有一個惟一的序列號, 它並不是主鍵。(unique屬性控制NHibernate通過SchemaExport工具生成DDL的過程。)
<property name="serialNumber" unique="true" type="string" column="SERIAL_NUMBER"/>
那么關於OrderItem 的映射可能是:
<many-to-one name="product" property-ref="serialNumber" column="PRODUCT_SERIAL_NUMBER"/>
當然,我們決不鼓勵這種用法。
<one-to-one name="PropertyName" (1) class="ClassName" (2) cascade="all|none|save-update|delete" (3) constrained="true|false" (4) fetch="join|select" (5) property-ref="PropertyNameFromAssociatedClass" (6) access="field|property|nosetter|ClassName" (7) />
說明:
(1) | name: 屬性的名字。 |
(2) | class (可選 - 默認是通過反射得到的屬性類型):被關聯的類的名字。 |
(3) | cascade(級聯) (可選) 表明操作是否從父對象級聯到被關聯的對象。 |
(4) | constrained(約束) (可選) 表明該類對應的表對應的數據庫表,和被關聯的對象所對應的數據庫表之間,通過一個外鍵引用對主鍵進行約束。 這個選項影響Save()和Delete()在級聯執行時的先后順序以及 決定該關聯能否被委托(也在schema export tool中被使用). |
(5) | fetch (可選 - 默認設置為select): 在外連接抓取或者序列選擇抓取選擇其一. |
(6) | property-ref: (可選) 指定關聯類的屬性名,這個屬性將會和本類的主鍵相對應。如果沒有指定,會使用對方關聯類的主鍵。 |
(7) | access (可選 - 默認是 property): NHibernate用來訪問屬性的策略。 |
有兩種不同的一對一關聯:
-
主鍵關聯
-
惟一外鍵關聯
主鍵關聯不需要額外的表字段;如果兩行是通過這種一對一關系相關聯的,那么這兩行就共享同樣的主關鍵字值。所以如果你希望兩個對象通過主鍵一對一關聯,你必須確認它們被賦予同樣的標識值!
比如說,對下面的Employee和Person進行主鍵一對一關聯:
<one-to-one name="Person" class="Person"/> <one-to-one name="Employee" class="Employee" constrained="true"/>
現在我們必須確保PERSON和EMPLOYEE中相關的字段是相等的。我們使用一個被成為foreign的特殊的現在我們必須確保PERSON和EMPLOYEE中相關的字段是相等的。我們使用一個被成為foreign的特殊的hibernate標識符生成策略:標識符生成策略: foreign:
<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實例被賦予和該Person的employee屬性所指向的Employee實例同樣的關鍵字值。
另一種方式是一個外鍵和一個惟一關鍵字對應,上面的Employee和Person的例子,如果使用這種關聯方式,可以表達成:
<many-to-one name="Person" class="Person" column="PERSON_ID" unique="true"/>
如果在Person的映射加入下面幾句,這種關聯就是雙向的:
<one-to-one name="Employee" class="Employee" property-ref="Person"/>
<component>元素把子對象的一些元素與父類對應的表的一些字段映射起來。 然后組件可以定義它們自己的屬性、組件或者集合。
<component name="PropertyName" (1) class="ClassName" (2) insert="true|false" (3) upate="true|false" (4) access="field|property|nosetter|ClassName" (5) optimistic-lock="true|false" (6) > <property ...../> <many-to-one .... /> ........ </component>
說明:
(1) | name: 屬性名 |
(2) | class (可選 - 默認為通過反射得到的屬性類型):組件(子)類的名字。 |
(3) | insert: 被映射的字段是否出現在SQL的INSERT語句中? |
(4) | update: 被映射的字段是否出現在SQL的UPDATE語句中? |
(5) | access可選 - 默認是 property): NHibernate用來訪問屬性的策略。 |
(6) | optimistic-lock (可選 - 默認是 true):表明更新此組件是否需要獲取樂觀鎖。換句話說,當這個屬性變臟時,是否增加版本號(Version |
其<property>子標簽為子類的一些屬性與表字段之間建立映射。
<component>元素允許加入一個 <parent>子元素,在組件類內部就可以有一個指向其容器的實體的反向引用。
<dynamic-component>元素允許把一個 IDictionaryp映射為組件,其屬性名對應鍵值。 參見第 8.5 節 “動態組件 (Dynamic components)”.
<subclass name="ClassName" (1) discriminator-value="discriminator_value" (2) proxy="ProxyInterface" (3) lazy="true|false" (4) dynamic-update="true|false" dynamic-insert="true|false"> <property .... /> ..... </subclass>
說明:
(1) | name: 子類的全限定名。 |
(2) | discriminator-value (辨別標志) (可選 - 默認為類名):一個用於區分每個獨立的子類的值。 |
(3) | proxy(代理) (可選): 指定一個類或者接口,在延遲裝載時作為代理使用。 |
(4) | lazy(可選, 默認是true): 設置為 lazy="false" 禁止使用延遲抓取。 |
每個子類都應該定義它自己的持久化屬性和子類。 <version> 和<id>屬性可以從根父類繼承下來。 在一棵繼承樹上的每個子類都必須定義一個唯一的discriminator-value。如果沒有指定,就會使用.NET類的全限定名。
<joined-subclass name="ClassName" (1) proxy="ProxyInterface" (2) lazy="true|false" (3) dynamic-update="true|false" dynamic-insert="true|false"> <key .... > <property .... /> ..... </joined-subclass>
說明:
(1) | name: 子類的全限定名。 |
(2) | proxy (可選): 指定一個類或者接口,在延遲裝載時作為代理使用。 |
(3) | lazy(可選, 默認是 true): 設置為 lazy="false" 禁止使用延遲裝載。 等價於設置proxy為自身類。 |
這種映射策略不需要指定辨別標志(discriminator)字段。但是,每一個子類都必須使用 <key>元素指定一個表字段來持有對象的標識符。本章開始的映射可以被用如下方式重寫:
<?xml version="1.0"?> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Eg" namespace="Eg"> <class name="Cat" table="CATS"> <id name="Id" column="uid" type="Int64"> <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="Dog"> <!-- mapping for Dog could go here --> </class> </hibernate-mapping>
<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 (可選, 默認是 true): 設置為 lazy="false" 禁止使用延遲裝載。 |
這種映射策略不需要指定辨別標志(discriminator)字段。
<join table="tablename" (1) schema="owner" (2) fetch="join|select" (3) inverse="true|false" (4) optional="true|false"> (5) <key ... /> <property ... /> ... </join>
說明:
(1) | table: 被連接表的名稱。 |
(2) | schema (可選):覆蓋由根<hibernate-mapping>元素指定的模式名稱。 |
(3) | fetch (可選 - 默認是 join): 如果設置為默認值join, NHibernate 將使用一個內連接來得到這個類或其超類定義的join ,而使用一個外連接來得到其子類定義的join。如果設置為select, 則 NHibernate 將為子類定義的 join使用順序選擇。 這僅在一行數據表示一個子類的對象的時候才會發生。對這個類和其超類定義的join,依然會使用內連接得到。 |
(4) | inverse(可選 - 默認是 false): 如果打開,NHibernate 不會插入或者更新此連接定義的屬性。 |
(5) | optional (可選 - 默認是 false): 如果打開,NHibernate 只會在此連接定義的屬性非空時插入一行數據,並且總是使用一個外連接來得到這些屬性。 |
例如,一個人(person)的地址(address)信息可以被映射到單獨的表中(並保留所有屬性的值類型語義):
<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>
此特性常常對遺留數據模型有用,我們推薦表個數比類個數少,以及細粒度的領域模型。然而,在單獨的繼承樹上切換繼承映射策略是有用的,后面會解釋這點。
假設你的應用程序有兩個同樣名字的持久化類,但是你不想在Hibernate查詢中使用他們的全限定名。 除了依賴auto-import="true"以外,類也可以被顯式地“import(引用)”。你甚至可以引用沒有明確被映射的類和接口。
<import class="System.Object" rename="Universe"/> <import class="ClassName" (1) rename="ShortName" (2) />
說明:
(1) | class: 任何.NET類的全限定名。包括應用程序集名 |
(2) | rename(可選 - 默認為類的全限定名): 在查詢語句中可以使用的名字。 |
實體entity獨立於任何持有實體引用的對象。與通常的.Net模型相比, 不再被引用的對象會被當作垃圾收集掉。實體必須被顯式的保存和刪除(除非保存和刪除是從父實體向子實體引發的級聯)。 這和ODMG模型中關於對象通過可觸及保持持久性有一些不同——比較起來更加接近應用程序對象通常在一個大系統中的使用方法。 實體支持循環引用和交叉引用,它們也可以加上版本信息。
一個實體的持久狀態包含指向其他實體和值類型實例的引用。值可以是原始類型,集合(不是集合中的對象), 組件或者特定的不可變對象。與實體不同,值(特別是集合和組件)是通過可觸及性來進行持久化和刪除的。 因為值對象(和原始類型數據)是隨着包含他們的實體而被持久化和刪除的,他們不能被獨立的加上版本信息。 值沒有獨立的標識,所以他們不能被兩個實體或者集合共享。
所有的NHibernate類型(如果.NET可空)除了集合類都支持null語義(繼承System.ValueType)。
直到現在,我們都一直使用術語“持久類”(persistent class)來代表實體。我們仍然會這么做。 然而嚴格說來,不是所有的用戶自定義的,帶有持久化狀態的類都是實體。組件就是用戶自定義類,卻是值語義的。
表 5.3. System.ValueType映射類型
NHibernate類型 | .NET類型 | Database類型 | 備注 |
---|---|---|---|
AnsiChar | System.Char | DbType.AnsiStringFixedLength - 1 char | |
Boolean | System.Boolean | DbType.Boolean | 在沒有指定類型(type) 屬性時的默認值。 |
Byte | System.Byte | DbType.Byte | 在沒有指定類型(type) 屬性時的默認值。 |
Char | System.Char | DbType.StringFixedLength - 1 char | 在沒有指定類型(type) 屬性時的默認值。 |
DateTime | System.DateTime | DbType.DateTime - ignores the milliseconds | 在沒有指定類型(type) 屬性時的默認值。 |
Decimal | System.Decimal | DbType.Decimal | 在沒有指定類型(type) 屬性時的默認值。 |
Double | System.Double | DbType.Double | 在沒有指定類型(type) 屬性時的默認值。 |
Guid | System.Guid | DbType.Guid | 在沒有指定類型(type) 屬性時的默認值。 |
Int16 | System.Int16 | DbType.Int16 | 在沒有指定類型(type) 屬性時的默認值。 |
Int32 | System.Int32 | DbType.Int32 | 在沒有指定類型(type) 屬性時的默認值。 |
Int64 | System.Int64 | DbType.Int64 | 在沒有指定類型(type) 屬性時的默認值。 |
PersistentEnum | A System.Enum | 潛在類型對應的DbType | 不用在映射文件指定type="PersistentEnum".而是提供枚舉的程序集全名, 讓NHibernate用反射來猜測類型。枚舉使用的潛在類型決定適當的DbType.。 |
Single | System.Single | DbType.Single | 在沒有指定類型(type) 屬性時的默認值。 |
Ticks | System.DateTime | DbType.Int64 | type="Ticks"必須被指定。 |
TimeSpan | System.TimeSpan | DbType.Int64 | 在沒有指定類型(type) 屬性時的默認值。 |
Timestamp | System.DateTime | DbType.DateTime - 取決於數據庫支持 | type="Timestamp"必須被指定。 |
TrueFalse | System.Boolean | DbType.AnsiStringFixedLength - 一個字符,'Y' 或者'N' | type="TrueFalse"必須被指定。 |
YesNo | System.Boolean | DbType.AnsiStringFixedLength - 一個字符,'Y' 或者'N' | type="YesNo"必須被指定。 |
NHibernate Type | .NET Type | Database Type | Remarks |
---|---|---|---|
AnsiString | System.String | DbType.AnsiString | type="AnsiString"必須被指定。 |
CultureInfo | System.Globalization.CultureInfo | DbType.String - 表明文化(culture)的5個字符 | 在沒有指定類型(type) 屬性時的默認值。 |
Binary | System.Byte[] | DbType.Binary | 在沒有指定類型(type) 屬性時的默認值。 |
Type | System.Type | DbType.String 保存應用程序集權限定名。 | 在沒有指定類型(type) 屬性時的默認值。 |
String | System.String | DbType.String | 在沒有指定類型(type) 屬性時的默認值。 |
NHibernate Type | .NET Type | Database Type | Remarks |
---|---|---|---|
StringClob | System.String | DbType.String | type="StringClob"必須被指定。 整個字段在內存里可讀。 |
BinaryBlob | System.Byte[] | DbType.Binary | type="BinaryBlob"必須被指定。 整個字段在內存里可讀。 |
Serializable | Any System.Object 必須標注可序列化標簽 | DbType.Binary | type="Serializable" 應該被指定. 如果不能為屬性找到NHibernate類型,這是最后可依靠的類型。 |
NHibernate為了兼容Hibernate也支持也一些額外的Java類型名(主要是方便能夠使用Hibernate代碼生成的映射文件), tt class="literal">type="integer"被映射為Int32 NHibernateType,type="short" 被映射為Int16 NHibernateType 。你可以通過查看NHibernate.Type.TypeFactory類的靜態構造函數的代碼查看到所有的上述所有的類型轉換。
要實現一個自定義類型,可以實現NHibernate.UserTypes.IUserType或NHibernate.UserTypes.ICompositeUserType中的任一個, 並且使用類型的全限定類名來定義屬性。請查看 NHibernate.DomainModel.DoubleStringType這個例子,看看它是怎么做的。
<property name="TwoStrings" type="NHibernate.DomainModel.DoubleStringType, NHibernate.DomainModel"> <column name="first_string"/> <column name="second_string"/> </property>
注意使用<column>標簽來把一個屬性映射到多個字段的做法。
ICompositeUserType, IEnhancedUserType, INullableUserType, IUserCollectionType, 和 IUserVersionType接口為更特殊的使用方式提供支持。
你甚至可以在一個映射文件中提供參數給一個IUserType。 為了這樣做, 你的UserType必須實現NHibernate.UserTypes.IParameterizedType接口。為了給自定義類型提供參數,你可以在映射文件中使用<type>元素。
<property name="priority"> <type name="MyCompany.UserTypes.DefaultValueIntegerType"> <param name="default">0</param> </type> </property>
現在,IUserType 可以從傳入的IDictionary對象中得到default 參數的值。
盡管 NHibernate 內建的豐富的類型和對組件的支持意味着你可能很少 需要使用自定義類型。不過, 為那些在你的應用中經常出現的(非實體)類使用自定義類型也是一個好方法。例如, 一個MonetaryAmount類使用ICompositeUserType來映射是不錯的選擇,雖然他可以很容易地被映射成組件。這樣做的動機之一是抽象。使用自定義類型,以后假若你改變表示金額的方法時,它可以保證映射文件不需要修改。
meta-type屬性使得應用程序能指定一個將數據庫字段的值映射到持久化類的自定義類型。 這個持久化類包含有用id-type指定的標識符屬性。 你必須指定從meta-type的值到類名的映射。
<any name="being" id-type="Int64" 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>
NHibernate也支持meta-type="class"標簽,這個例子里meta-value不是必須的, 因為 meta-value就是持久化類名(persistentClass.FullName)。
<any name="being" id-type="Int64" meta-type="class"> <column name="table_name"/> <column name="id"/> </any>
但你使用meta-type="class"在查詢語句里設置參數時,你必須使用下面的代碼:
SetParameter("paramName", typeof(YourClass).FullName, NHibernateUtil.ClassMetaType)
映射文件部分:
<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: 標識符類型。 |
(3) | meta-type (可選 -默認是 string): 允許辨別標志(discriminator)映射的任何類型。 |
(4) | cascade (可選 -默認是none): 級聯的類型。 |
(5) | access (可選 -默認是 property): NHibernate 用來訪問屬性值的策略。 |
(6) | optimistic-lock (可選 -默認是 true): 表明更新此組件是否需要獲取樂觀鎖。換句話說,當這個屬性變臟時,是否增加版本號(Version) 。 |
二十二、SQL中引號包圍的標識符
<class name="LineItem" table="`Line Item`"> <id name="Id" column="`Item Id`"/><generator class="assigned"/></id> <property name="ItemNumber" column="`Item #`"/> ... </class>
<hibernate-mapping> <subclass name="Eg.Subclass.DomesticCat, Eg" extends="Eg.Cat, Eg" discriminator-value="D"> <property name="name" type="string"/> </subclass> </hibernate-mapping>
二十四、數據庫生成屬性(Generated Properties)
被標明為generated的屬性還必須是 non-insertable和 non-updateable的。只有(version)(可選),時間戳 (可選)和屬性可以被標明為generated。
- never (默認) 標明此屬性值不是從數據庫中生成。
- insert - 標明此屬性值在insert的時候生成,但是不會在隨后的update時重新生成。比如說創建日期就歸屬於這類。
- always - 標明此屬性值在insert和update時都會被生成。
二十五、數據庫輔助對象
第一種模式是在映射文件中顯式聲明CREATE和DROP命令:
<nhibernate-mapping> ... <database-object> <create>CREATE TRIGGER my_trigger ...</create> <drop>DROP TRIGGER my_trigger</drop> </database-object> </nhibernate-mapping>
第二種模式是提供一個類,這個類知道如何組織CREATE和DROP命令。這個特別類必須實現NHibernate.Mapping.IAuxiliaryDatabaseObject接口。
<hibernate-mapping> ... <database-object> <definition class="MyTriggerDefinition, MyAssembly"/> </database-object> </hibernate-mapping>
你也可以在配置文件里設置參數傳給數據庫對象。
<hibernate-mapping> ... <database-object> <definition class="MyTriggerDefinition, MyAssembly"> <param name="parameterName">parameterValue</param> </definition> </database-object> </hibernate-mapping>
NHibernate可以調用IAuxiliaryDatabaseObject.SetParameterValues方法接受dictionary參數。
還有,這些數據庫對象可以特別指定為僅在特定的方言中才使用。
<hibernate-mapping> ... <database-object> <definition class="MyTriggerDefinition"/> <dialect-scope name="NHibernate.Dialect.Oracle9Dialect"/> <dialect-scope name="NHibernate.Dialect.OracleDialect"/> </database-object> </hibernate-mapping>