映射聲明
對象/關系映射在XML文件中配置。mapping文件這樣設計是為了使它可讀性強並且可修改。mapping語言是以對象為中心,意味着mapping是圍繞着持久化類聲明來建立的,而不是圍繞數據表。
要注意的是,盡管很多NHibernate使用者選擇手動定義XML文件,但是仍然有很多工具可以用來生成mapping文件,包括NHibernate.Mapping.Attributes 庫和各種各樣基於模板的代碼生成工具(CodeSmith, MyGeneration)。
讓我們用一個mapping的例子作為開始:
<?xml version="1.0"?> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Eg" namespace="Eg"> <class name="Cat" table="CATS" discriminator-value="C"> <id name="Id" column="uid" type="Int64"> <generator class="hilo"/> </id> <discriminator column="subclass" type="Char"/> <property name="BirthDate" type="Date"/> <property name="Color" not-null="true"/> <property name="Sex" not-null="true" update="false"/> <property name="Weight"/> <many-to-one name="Mate" column="mate_id"/> <set name="Kittens"> <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>
我們現在討論mapping文件中的內容。我們將只會描述NHibernate在運行時使用的文檔中的標簽和特性。mapping文件也包含一些額外的可選的特性和標簽,這些特性和標簽會影響表結構導出工具(schema export tool)導出的數據庫的表結構(database schemas)。(例如not-null特性)。
XML名稱空間
所有的XMLmapping應該聲明XML名稱空間。規定的XML結構定義(schema definition)可以在NHibernate發行版中的src\nhibernate-mapping.xsd 中找到。
小建議:想要啟用mapping和配置文件的智能感知功能,需要將相應的.xsd文件作為你解決方案中所有項目的一部分(生成操作可以是‘none’)或者作為“solution files”,或者放在你的“lib”文件夾中,然后將它添加到你的XML文件的schemas屬性中。你可以在<VS installation directory>\Xml\Schemas中復制它,要注意你可能得將不同版本的xsd對應不同版本的NHibernate。
hibernate-mapping
這個標簽有很多可選的特性。schema特性定義了對應表的數據庫表空間。如果指定了相應的表空間,那么數據表名稱就會被限定在給定的表空間。如果這個置空,就不會限定表空間。default-cascade 特性定義了沒有詳細指定級聯方式的屬性和集合的級聯方式。默認情況下,auto-import 特性讓我們在查詢語句中使用不限定表空間的類名。assembly 和 namespace特性定義了持久化類位於哪個程序集中,在哪個名稱空間中聲明。
<hibernate-mapping (1) schema="schemaName" (2) default-cascade="none|save-update" (3) auto-import="true|false" (4) assembly="Eg" (5) namespace="Eg" (6) default-access="field|property|field.camecase(7)..." default-lazy="true|false" />
(1) schema (可選): 數據庫表空間的名稱。
(2) default-cascade (可選 - 默認 none): 默認的級聯方式。
(3) auto-import (可選 - 默認true): 定義是否可以在查詢語句中使用非限定表空間的類名(在mapping文件中定義的類)。
(4)(5) assembly and namespace(可選): 指定非限定表空間的類所在的程序集和名稱空間。
(6) default-access (可選- 默認 property): NHibernate獲得屬性值的方式
(7) default-lazy (可選- 默認 true):如果設置成false,懶加載就會被禁用
如果你不使用assembly和namespace特性,你就得使用完整的類名,包括程序集和名稱空間的名稱。
如果你有兩個持久化類使用了相同的(未限定表空間的)名字,你應該將auto-import設置成false。如果你試圖將兩個類設置成相同的名稱,那么NHibernate會拋出異常。
類
你可以使用class來定義一個持久化類:
<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: 持久化類(或者接口)的全限定名。
(2) table(optional - 默認是非限定的類名): 對應的數據庫表名。
(3) discriminator-value (optional - 默認是類名): 一個用於區分不同的子類的值,在多態行為時使用。可以設置為null和not null。
(4) mutable (optional, 默認是true): 指定該類的實例可變(不可變)。
(5) schema (optional): 覆蓋在根<hibernate-mapping> 標簽中指定的表空間名字
(6) proxy (optional): Specifies an interface to use for lazy initializing proxies. You may specify the name of the class itself. 指定一個接口,在延遲裝載時作為代理使用。你可以在這里使用該類自己的名字。
(7) dynamic-update (optional, defaults to false): 指定用於UPDATE 的SQL將會在運行時動態生成,並且只更新那些改變過的字段。
(8) dynamic-insert (optional, defaults to false): 指定用於INSERT的SQL將會在運行時動態生成,並且只包含那些非空字段。
(9) select-before-update (optional, defaults to false): 指定NHibernate是否需要執行UPDATE操作,除非它確定對象真的被修改了,不然就不會執行。在一些特定的情況下(實際上僅僅當瞬時態對象要執行update()而被關聯到一個新的session的時候),這意味着NHibernate在執行UPDATE之前會執行一個額外的SELECT的SQL語句操作來判斷是不是真的需要進行UPDATE。
(10) polymorphism (optional, defaults to implicit): 指定是隱式還是顯式的使用查詢多態。
(11) where (optional) 指定一個附加的SQL WHERE 條件,在抓取這個類的對象時會一直增加這個條件。
(12) persister (optional): 指定一個自定義的IClassPersister.
(13) batch-size (optional, defaults to 1) 指定通過主鍵獲得實體方式的批量插入量
(14) optimistic-lock (optional, defaults to version): 指定樂觀鎖策略。
(15) lazy (optional): 如果設置成lazy="false"懶加載會被完全關閉。
(16) abstract (optional): 用來在<union-subclass>繼承中標記抽象父類
若指明的持久化類實際上是一個接口,也可以被完美地接受。其后你可以用 <subclass> 來指定該接口的實際實現類名。你可以持久化任何static(靜態的)內部類。記得應該使用標准的類名格式,就是說比如:Eg.Foo+Bar 。這是因為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-update和dynamic-insert的設置並不會繼承到子類,所以在<subclass>或者<joined-subclass>元素中可能需要再次設置。這些設置是否能夠提高效率要視情形而定。請用你的智慧決定是否使用。
使用select-before-update 通常會降低性能。但是這種方式能夠有效地防止不必要的數據庫更新。
如果你使用了select-before-update ,你就可以選擇一種樂觀鎖的策略:
version 檢查version/timestamp 行
all 檢查所有行
dirty 檢查改變的行
none 不使用樂觀鎖
我們強烈建議你使用NHibernate的樂觀鎖功能的時候使用version/timestamp 。考慮到性能問題,這個是最佳並且唯一的方案來正確地處理在session(例如,當使用ISession.Update() )之外的修改操作。要記住,version或者timestamp屬性永遠不能為null,無論是哪種unsaved-value 方案,或者實體將要變成游離態。
從NHibernate 1.2.0開始,version的是從1開始,而不是像之前的版本那樣從0開始。這個目的是為了讓unsaved-value將version設置成0。
子查詢
一個其他的映射類的方式是映射一個查詢。為了達到這個目的,我們可以使用<subselect>標簽,這個標簽是獨立於<class>, <subclass>, <joined-subclass> 和 <union-subclass>的。subselect標簽中的內容是一個SQL查詢:
<subselect>
SELECT cat.ID, cat.NAME, cat.SEX, cat.MATE FROM cat
</subselect>
一般來說,當使用subselect來mapping一個查詢的時候,你會將類標記成不變的(mutable="false"),除非你使用了自定義的SQL來完成增刪改操作。
其次,強制同步受到查詢影響的數據表是有意義的,可以使用一個或者多個<synchronize> :
<subselect> SELECT cat.ID, cat.NAME, cat.SEX, cat.MATE FROM cat </subselect> <syncronize table="cat"/>
Id
被映射的類必須聲明對應數據庫表主鍵字段。大多數類有一個屬性,為每一個實例包含唯一的標識。 <id> 標簽定義了該屬性到數據庫表主鍵字段的映射。
<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屬性不存在,會認為這個類沒有標識屬性。
unsaved-value 屬性在NHibernate 1.0中基本不需要。
還有一個另外的<composite-id>聲明可以訪問舊式的多主鍵數據。我們強烈不鼓勵使用這種方式。
Id生成器
必須要有一個生成器,用來為該持久化類的實例生成唯一的標識。
這個生成器可以使用<generator>子標簽來聲明。如果這個生成器實例需要某些配置值或者初始化參數,用 <param>元素來傳遞。
<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>
如果沒有指定參數,就可以直接在<id> 標簽中使用一個generator 特性來聲明一個生成器,就像下面的這樣:
<id name="Id" type="Int64" column="uid" unsaved-value="0" generator="native" />
所有的生成器實現NHibernate.Id.IIdentifierGenerator接口。這是一個非常簡單的接口;某些應用程序可以選擇提供他們自己特定的實現。當然,NHibernate提供了很多內置的實現。下面是一些內置生成器的快捷名字:
increment
僅在沒有其他進程往同一張數據表中插入數據的時候生成獨一無二的整型標識符。不要在一個集群中使用這種生成方式。
identity
對DB2,MySQL, MS SQL Server, Sybase和HypersonicSQL的內置標識字段提供支持。返回的標識符是 Int64, Int32 或者 Int16類型的。
sequence
對DB2,MySQL, PostgreSQL, Oracle的內置標識字段提供支持。返回的標識符是Int64 Int32 或者 Int16類型的。
hilo
使用一個高/低位算法來高效的生成Int64, Int32 或者 Int16類型的標識符。給定一個表和字 段(默認分別是hibernate_unique_key 和next)作為高位值得來源。高/低位算法生成的標識符只在一個特定的數據庫中是唯一的。
seqhilo
使用一個高/低位算法來高效的生成Int64, Int32 或者 Int16類型的標識符,給定一個數據庫 序列(sequence)的名字。
uuid.hex
用一個System.Guid和它的ToString(string format)方法生成字符串類型的標識符。字符串的長度取決於 format的配置。
uuid.string
用一個新的System.Guid產生一個byte[] ,把它轉換成字符串。
guid
用一個新的System.Guid 作為標識符。
guid.comb
用Jimmy Nilsson在文章http://www.informit.com/articles/article.asp?p=25862中描述的算 法產生一個新的System.Guid。
native
根據底層數據庫的能力選擇 identity, sequence 或者 hilo中的一個。
assigned
讓應用程序在save()之前為對象分配一個標示符。
foreign
使用另外一個相關聯的對象的標識符。和<one-to-one>聯合一起使用。
Hi/Lo算法
hilo 和 seqhilo生成器給出了兩種hi/lo算法的實現,這是一種很令人滿意的標識符生成算法。第一種實現需要一個“特殊”的數據庫表來保存下一個可用的“hi”值。第二種實現使用一個Oracle風格的序列(在被支持的情況下)。
<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自行提供Connection時無法使用hilo 。Hibernate必須能夠在一個新的事務中得到一個"hi"值。
UUID十六進制算法
<id name="Id" type="String" column="cat_id"> <generator class="uuid.hex"> <param name="format">format_value</param> <param name="separator">separator_value</param> </generator> </id>
UUID是通過調用Guid.NewGuid().ToString(format)產生的。format值的設置請參考MSDN文檔。默認的seperator很少也不應該被改變。format決定是否配置好的seperator 能替換format使用的默認seperator。
UUID String Algorithm
UUID是通過調用 Guid.NewGuid().ToByteArray() 並且把 byte[]轉換成char[],char[] 做為一個16個字符組成的字符串返回。
GUID Algorithms
guid 標識符通過調用Guid.NewGuid()產生。 為了提升Guids在MS SQL中作為主鍵,外鍵和索引的一部分時的性能,可以使用guid.comb。在別的數據庫中使用guid.comb的好處是支持非標准的GUID。
標識符字段和序列
對於內部支持標識字段的數據庫(DB2,MySQL,Sybase,MS SQL),你可以使用identity關鍵字生成。對於內部支持序列的數據庫(DB2,Oracle, PostgreSQL),你可以使用sequence風格的關鍵字生成。這兩種方式對於插入一個新的對象都需要兩次SQL查詢。
<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中進行選擇,取決於底層數據庫的支持能力。
由程序分配的標識符
如果你需要應用程序分配一個標示符(而非NHibernate來生成它們),你可以使用assigned生成器。這種特殊的生成器會使用已經分配給對象的標識符屬性的標識符值。用這種特性來分配商業行為的關鍵字要特別小心(基本上總是一種可怕的設計決定)。
因為其繼承天性,使用這種生成器策略的實體不能通過ISession的SaveOrUpdate()方法保存。作為替代,你應該明確告知NHibernate是應該被save還是update,分別調用ISession的Save()或Update()方法。
強化版標識符生成器
Starting with NHibernate release 3.3.0, there are 2 new generators which represent a re-thinking of 2 different aspects of identifier generation. The first aspect is database portability; the second is optimization Optimization means that you do not have to query the database for every request for a new identifier value. These two new generators are intended to take the place of some of the named generators described above, starting in 3.3.x. However, they are included in the current releases and can be referenced by FQN.
The first of these new generators is NHibernate.Id.Enhanced.SequenceStyleGenerator (short name enhanced-sequence) which is intended, firstly, as a replacement for the sequence generator and, secondly, as a better portability generator than native. This is because native generally chooses between identity and sequence which have largely different semantics that can cause subtle issues in applications eyeing portability. NHibernate.Id.Enhanced.SequenceStyleGenerator, however, achieves portability in a different manner. It chooses between a table or a sequence in the database to store its incrementing values, depending on the capabilities of the dialect being used. The difference between this and native is that table-based and sequence-based storage have the same exact semantic. In fact, sequences are exactly what NHibernate tries to emulate with its table-based generators. This generator has a number of configuration parameters:
sequence_name (optional, defaults to hibernate_sequence): the name of the sequence or table to be used.
initial_value (optional, defaults to 1): the initial value to be retrieved from the sequence/table. In sequence creation terms, this is analogous to the clause typically named "STARTS WITH".
increment_size (optional - defaults to 1): the value by which subsequent calls to the sequence/table should differ. In sequence creation terms, this is analogous to the clause typically named "INCREMENT BY".
force_table_use (optional - defaults to false): should we force the use of a table as the backing structure even though the dialect might support sequence?
value_column (optional - defaults to next_val): only relevant for table structures, it is the name of the column on the table which is used to hold the value.
prefer_sequence_per_entity (optional - defaults to false): should we create separate sequence for each entity that share current generator based on its name?
sequence_per_entity_suffix (optional - defaults to _SEQ): suffix added to the name of a dedicated sequence.
optimizer (optional - defaults to none): See Section 5.1.5.8.1, “Identifier generator optimization”
The second of these new generators is NHibernate.Id.Enhanced.TableGenerator (short name enhanced-table), which is intended, firstly, as a replacement for the table generator, even though it actually functions much more like org.hibernate.id.MultipleHiLoPerTableGenerator (not available in NHibernate), and secondly, as a re-implementation of org.hibernate.id.MultipleHiLoPerTableGenerator (not available in NHibernate) that utilizes the notion of pluggable optimizers. Essentially this generator defines a table capable of holding a number of different increment values simultaneously by using multiple distinctly keyed rows. This generator has a number of configuration parameters:
optimizer (optional - defaults to ??): See Section 5.1.5.8.1, “Identifier generator optimization”.
標識符生成器的優化
For identifier generators that store values in the database, it is inefficient for them to hit the database on each and every call to generate a new identifier value. Instead, you can group a bunch of them in memory and only hit the database when you have exhausted your in-memory value group. This is the role of the pluggable optimizers. Currently only the two enhanced generators (Section 5.1.5.8, “Enhanced identifier generators” support this operation.
pooled-lo: similar to pooled, except that it's the starting value of the "current group" that is stored into the database structure. Here, increment_size refers to the values coming from the database.
聯合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()和HashCode()方法,來實現組合的標識符判斷等價.也必須實現Serializable接口。
不幸的是,這種組合關鍵字的方法意味着一個持久化類是它自己的標識。除了對象自己之外,沒有什么方便的“把手”可用。你必須自己初始化持久化類的實例,在使用組合關鍵字Load()持久化狀態之前,必須填充他的聯合屬性。我們會在Section 7.4, “Components as composite identifiers”. 中說明一種更加方便的方法,把聯合標識實現為一個獨立的類,下面描述的屬性只對這種備用方法有效:
class (可選 - 默認為通過反射(reflection)得到的屬性類型): 作為聯合標識的組件類名(參見下一節)。
識別器
在"一棵對象繼承樹對應一個表"的mapping策略中,<discriminator>元素是必需的,它聲明了表的識別器字段。識別器字段包含標志值,用於告知持久化層應該為某個特定的行創建哪一個子類的實例。只能使用如下受到限制的一些類型:String, Char, Int32, Byte, Int16, Boolean, YesNo, TrueFalse.
<discriminator column="discriminator_column" (1) type="discriminator_type" (2) force="true|false" (3) insert="true|false" (4) formula="arbitrary SQL expressi(5)on" />
(1) column (可選 - 默認為 class) 識別器字段的名字
(2) type (可選 - 默認為 String) 一個NHibernate字段類型的名字
(3) force (可選 - 默認為 false) "強制"NHibernate指定允許的識別器值,就算取得的所有實例都是根類的。
(4) insert (可選 - 默認為 true) 當識別器是被映射的組件的標識符的一部分時設置為false。
(5) formula (可選) an arbitrary SQL expression that is executed when a type has to be evaluated. Allows content-based discrimination. 當一個類型需要被推斷的時候執行的一個任意的SQL表達式。允許基於內容的識別。
標識器字段的實際值是根據<class> 和<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 (可選 - 默認為 "敏感的" 值): 一個版本屬性值,表示一個實體是剛剛實例化的(未保存),將它和在之前的session中保存或者加載的持久化實例區分開來。(undefined 說明了這個標識符屬性值應該被使用)
(6)generated (optional - defaults to never): Specifies that this version property value is actually generated by the database. See the discussion of Section 5.5, “Generated Properties”.說明了這個版本屬性值是由數據庫生成的。可以參見Section 5.5, “Generated Properties”的討論。
版本數字可以是Int64, Int32, Int16, Ticks, Timestamp, or TimeSpan (或者他們對應的可空值類型)類型。
時間戳(可選)
可選的<timestamp>標簽表明這個數據表包含時間戳數據。時間戳可以是版本的一個替代品。時間戳是一種不太安全樂觀鎖的實現。然而,有些時候應用程序會用其他方式使用時間戳。
<timestamp column="timestamp_column" (1) name="PropertyName" (2) access="field|property|nosetter|Clas(3)sName" unsaved-value="null|undefined|value"(4) generated="never|always" (5) />
(1)column (可選 - 默認為屬性的名稱): 儲存時間戳的字段。
(2)name: .NET的DateTime 類型的屬性字段名稱。
(3)access (可選 - 默認為 property): NHibernate獲得時間戳字段值的方法。
(4)unsaved-value (可選 - 默認為null): 一個時間戳屬性值,表示一個實體是剛剛實例化的(未保存),將它和在之前的session中保存或者加載的持久化實例區分開來。(undefined 說明了這個標識符屬性值應該被使用)
(5)generated (可選 - 默認為never): 說明了這個時間戳屬性值是由數據庫生成的。可以參見Section 5.5, “Generated Properties”的討論。
注意,<timestamp> 和<version type="timestamp">是相同的。
屬性
The <property> element declares a persistent property of the class.
<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) lazy="true|false" (9) />
(1)name: 類中的屬性名稱。
(2)column (可選 - 默認為屬性的名稱): 對應數據表的字段名稱
(3)type (可選): 指向NHibernate類型的名稱
(4)update, insert (可選 - 默認為 true) : 指定被映射的字段是否被包含在SQL 的UPDATE或者INSERT聲明中。將二者都設置成false會得到一個完全的“衍生”的屬性,它的值是從其他映射到這個相同的字段或者通過觸發器或者通過其他應用程序來初始化
(5)formula (可選 ): 一個定義了一個計算后的屬性的SQL語句。這個計算后的屬性並沒有對應的映射字段。
(6)access (可選 - 默認為property):NHibernate用來獲取屬性值的策略。
(7)optimistic-lock (可選 - 默認為true): 定義這個屬性的更新是否需要獲取樂觀鎖。用其他的話說就是,當這個屬性dirty之后要不要變成新的版本。
(8)generated (可選 - 默認為never): 指定這個屬性值實際上是由數據庫生成的。可以參考Section 5.5, “Generated Properties”的討論。
(9)lazy (可選 - 默認為false): 指定這個屬性是不是懶加載。一個懶加載的屬性在實體被初始化加載的時候是不會被加載的,除非關聯模式被相應的特定查詢重寫。當這個實體的懶加載屬性被訪問的時候,懶加載屬性的值才會被加載。
類型名稱可以是:
自定義類型名稱(例如Illflow.Type.MyCustomType)
要注意的是,如果你使用的是NHibernate 基礎類型之外的類型你必須指定完整的程序集限定名稱(除非你在<hibernate-mapping>標簽中設置了assembly 和 namespace 特性)。
NHibernate 支持.NET 2.0 的可空數據類型。這些類型在代碼內幾乎都和原本的對應可空類型一樣。例如,Nullable<Int32>可以使用type="Int32" 或者 type="System.Int32"來配置映射。
如果你沒有指定類型,NHibernate會使用反射來反推出對應的屬性類型。NHibernate會按照規則2,3,4的順序嘗試着推測出getter返回類型的名字。然而,這並不足以應付所有情況。在一些情況下,你仍然需要指定type特性(例如,要區分NHibernateUtil.DateTime 和NHibernateUtil.Timestamp或者其他的自定義類型的時候)。
access 特性能夠讓你控制NHibernate在運行時獲得屬性值的方式。access 應該是字符形式的access-strategy.naming-strategy。.naming-strategy並不是必須的。
表:access策略
| Access 策略名稱 | 介紹 |
| property | 默認的實現模式。NHibernate使用屬性的get/set 方法。使用這種方式的話,就不能使用任何的命名規則,因為name特性的值就是屬性的名稱。 |
| field | NHibernate會直接訪問字段。NHibernate使用name特性的值作為字段的名稱。當在屬性的getter和setter包含了額外的動作,並且你不希望NHibernate在擴充或者獲取這個對象的時候觸發。如果你希望你的API的使用者使用HQL的時候使用的是屬性的名稱而不是字段,就要使用這個名稱策略。 |
| nosetter | 在為屬性賦值或者獲得屬性的值的時候NHibernate會直接訪問字段。當你的API使用者不能直接改變屬性值,也就是說,一個屬性僅僅暴露get訪問器的時候,有用。一個名稱策略在NHibernate使用name特性作為屬性名稱並且還需要知道字段名稱的時候是需要的。 |
| ClassName | 如果NHibernate內置的值獲取方式不是你需要的,你可以通過實現NHibernate.Property.IPropertyAccessor接口來自己定義一個。access的值應該是全限定的名稱,並且可以通過Activator.CreateInstance(string assemblyQualifiedName)來加載。 |
表:名稱策略
| 名稱策略名稱 | 介紹 |
| camelcase | name特性轉化成駝峰命名方式來查找字段。例如<property name="FooBar" ... >使用字段fooBar。 |
| camelcase-underscore | name特性轉化成駝峰命名方式並切加上下划線來查找字段。例如<property name="FooBar" ... >使用字段_fooBar。 |
| camelcase-m-underscore | name特性轉化成駝峰命名方式並且以m為前綴,然后加上下划線來查找字段。例如<property name="FooBar" ... >使用字段m_fooBar。 |
| lowercase | name特性轉化成了小寫字符來查找字段。例如<property name="FooBar" ... >使用字段foobar。 |
| lowercase-underscore | name特性轉化成了小寫字符並且加上下划線。例如<property name="FooBar" ... >使用字段_foobar |
| pascalcase-underscore | name特性加上下划線來查找字段。例如<property name="FooBar" ... >使用字段_ooBar。 |
| pascalcase-m | name特性以m作為前綴來查找字段。例如<property name="FooBar" ... >使用字段mFooBar。 |
| pascalcase-m-underscore | name特性以m作為前綴,然后加上下划線來查找字段。例如<property name="FooBar" ... >使用字段m_FooBar。 |
多對一關系
一個常見的和其他持久化類的關聯可以使用many-to-one 標簽。他們的管理是一個多對一的關系。(它實際上就只是一個對象引用)
<many-to-one name="PropertyName" (1) column="column_name" (2) class="ClassName" (3) cascade="all|none|save-update|delete|delete-orphan|(4)all-delete-orphan" 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 (optional): 對應數據表的字段名稱。
(3)class (optional - defaults to the property type determined by reflection): 關聯類的名稱。
(4)cascade (optional): 指定父對象的哪種操作會級聯到關聯對象上去。
(5)fetch (optional - defaults to select): 選擇outer-join 加載還是順序選擇加載(sequential select fetching)。
(6)update, insert (optional - defaults to true) : 指定被映射的字段是否被包含在SQL 的UPDATE或者INSERT聲明中。將二者都設置成false會得到一個完全的“衍生”的屬性,它的值是從其他映射到這個相同的字段或者通過觸發器或者通過其他應用程序來初始化。
(7)property-ref: (optional) : 關聯類的連接到這個外鍵的屬性名稱。如果沒有指定,就會使用關聯類的主鍵。
(8)access (optional - defaults to property): NHibernate用來獲取屬性值的策略。
(9)unique (optional): 允許DDL生成的時候對外鍵字段附加unique限制。
(10)optimistic-lock (optional - defaults to true): 制定對這個屬性的更新操作需要或者不需要獲得樂觀鎖。換句話說,當這個屬性變臟的時候是否需要更新版本信息。
(11)not-found (optional - defaults to exception): 制定關聯外鍵數據缺失的時候該如何處理:選擇ignore會將確實的數據關聯成null來處理(譯者注:默認情況會報異常)。
cascade特性可以設置成下面的值:all, save-update, delete, none。設置成none之外的其他值都會級聯操作關聯對象。參見下面的“生命周期對象”。
fetch特性支持兩種不同的值:
一個典型的多對一的聲明可以簡單地這樣配置
<many-to-one name="product" class="Product" column="PRODUCT_ID"/>
property-ref 特性應該只能被用來映射冗余字段,其外鍵應該是關聯表的一個獨一無二的鍵而不是主鍵。這是一個丑陋的關系模型。例如,假設Product類有一個獨一無二的序列,而這個序列不是主鍵。(unique特性能夠控制NHibernate的DDL生成方式)
<property name="serialNumber" unique="true" type="string" column="SERIAL_NUMBER"/>
然后OrderItem的mapping可以是:
<many-to-one name="product" property-ref="serialNumber" column="PRODUCT_SERIAL_NUMBER"/>
當然,這還遠遠不夠。
一對一關系
一對一關系可以使用one-to-one 標簽聲明。
<one-to-one name="PropertyName" (1) class="ClassName" (2) cascade="all|none|save-update|delete|delete-orphan|(3)all-delete-orphan" constrained="true|false" (4) fetch="join|select" (5) property-ref="PropertyNameFromAssociatedClass" (6) access="field|property|nosetter|ClassName" (7) />
(1)name: 類中的屬性名稱。
(2)class (optional - defaults to the property type determined by reflection): 關聯類的名稱。
(3)cascade (optional): 指定父對象的哪種操作會級聯到關聯對象上去。
(4)constrained (optional) specifies that a foreign key constraint on the primary key of the mapped table references the table of the associated class. This option affects the order in which Save() and Delete() are cascaded (and is also used by the schema export tool).
(5)fetch (optional - defaults to select): 選擇outer-join 加載還是順序選擇加載(sequential select fetching)。
(6)property-ref: (optional) The name of a property of the associated class that is joined to the primary key of this class. If not specified, the primary key of the associated class is used.
(7)access (optional - defaults to property): NHibernate用來獲取屬性值的策略。
有兩種一對一關聯方式:
主鍵關聯不需要額外的字段。如果兩條記錄通過這種方式關聯,那么這兩個數據表字段就共享同一個主鍵值。如果你像要兩個對象通過主鍵關聯,那么你必須保證他們的主鍵值是相同的!
對於主鍵關聯方式,將下面的映射信息分別添加給Employee和Person。
<one-to-one name="Person" class="Person"/>
<one-to-one name="Employee" class="Employee" constrained="true"/>
現在我們必須保證PERSON和EMPLOYEE對應表的相關記錄的主鍵是相等的。我們使用一種叫做foreign的特殊NHibernate標識符生成方式:
<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實例就會被分配到一個和Employee實例相同的主鍵值。
你也可以使用一個獨一無二的外鍵來關聯Employee和Person,可以簡單地這樣配置:
<many-to-one name="Person" class="Person" column="PERSON_ID" unique="true"/>
可以通過添加下面的信息到Person的mapping文件中來讓這個關聯變成一種雙向的關聯:
<one-to-one name="Employee" class="Employee" property-ref="Person"/>
自然id
<natural-id mutable="true|false"/> <property ... /> <many-to-one ... /> ...... </natural-id>
盡管我們建議使用代理鍵(surrogate keys )作為主鍵,你仍然要嘗試為所有的實體添加自然鍵(譯者注:原文是:”you should still try to identify natural keys for all entities.“)。自然鍵是非空且唯一的,可以是屬性也可以是多個屬性的組合。如果它是不可變的就更好了。可以在natural-id>標簽中來配置這個自然鍵屬性。Nhibernate會自動產生這個必要的獨一無二的非空約束,然后你的mapping的自描述性就會更強。
我們強烈建議你實現Equals() 和GetHashCode() 方法來比較你實體中的自然鍵屬性。
對於包含自然主鍵的實體就不需要這個mapping配置了。
組件,dynamic組件
<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 (optional - defaults to the property type determined by reflection): 組件(子)類的名稱。
(3)insert: 映射的字段是否要出現在INSERT語句中。
(4)update: 映射的字段是否要出現在UPDATE語句中。
(5)access (optional - defaults to property): NHibernate用來獲取屬性值的策略。
(6)optimistic-lock (optional - defaults to true): Specifies 制定對這個屬性的更新操作需要或者不需要獲得樂觀鎖。換句話說,當這個屬性變臟的時候是否需要更新版本信息。
子<property>標簽將其子類映射到表中相應的字段里。
<property>標簽允許一個<parent>子標簽,這個標簽映射這個組件類的一個屬性,引用回其包含的實體。
<dynamic-component>標簽允許一個IDictionary 對象映射成一個組件,它的屬性名稱指向字典的key。
屬性
<properties>標簽允許類的屬性命名一個邏輯組合(logical grouping )。最大的用處就是它能夠允許一些屬性的組合能夠成為一個property-ref的配置對象。它也是一種定義由多字段組成的唯一約束(multi-column unique constraint)的簡便方式,例如:
<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: 組合的邏輯名稱,它不是一個真正意義上的屬性名稱。
(3)insert: 映射的字段是否要出現在INSERT語句中。
(4)update: 映射的字段是否要出現在UPDATE語句中。
(6)optimistic-lock (optional - defaults to true): Specifies 制定對這個屬性的更新操作需要或者不需要獲得樂觀鎖。換句話說,當這個屬性變臟的時候是否需要更新版本信息。
(5)unique (optional - defaults to false): 指定這個組件所有映射的字段是否包含唯一約束。
例如,如果我們有下面這樣的<properties> 映射配置:
<class name="Person"> <id name="personNumber" /> <properties name="name" unique="true" update="false"> <property name="firstName" /> <property name="lastName" /> <property name="initial" /> </properties> </class>
你可能有一些冗余的數據關聯到Person表的這個唯一鍵,而不是主鍵上
<many-to-one name="owner" class="Person" property-ref="name"> <column name="firstName" /> <column name="lastName" /> <column name="initial" /> </many-to-one>
不推薦這種脫離上下文的冗余數據映射方式。
子類
最終,多態對象的持久化許要聲明每個父對象類的子類。對於(推薦的)table-per-class-hierarchy 的映射方式,許要使用<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"> <property .... /> <properties .... /> ..... </subclass>
(1)name: 全限定的.NET類型名稱,包括它的程序集名稱。
(2)discriminator-value (optional - defaults to the class name): 用來區分子類的值。
(3)proxy (optional): 指定一個類或者接口來實現懶加載。
(4)lazy (optional, defaults to true): 如果設置成false,懶加載就會被禁用
每個子類都應當聲明它自己的屬性和子類。<version> 和 <id> 屬性會繼承自他們的根類(譯者注:就是初代祖宗)。繼承層次結構中的每個子類都必須定義一個唯一的discriminator-value。如果沒有指定的話,那么就要使用完全限定的.NET類名。
想要獲得更多關於繼承映射的信息,參見繼承映射這一章節。
join方式關聯的子類
Alternatively, a subclass that is persisted to its own table (table-per-subclass mapping strategy) is declared using a <joined-subclass> element.
<joined-subclass name="ClassName" (1) proxy="ProxyInterface" (2) lazy="true|false" (3) dynamic-update="true|false" dynamic-insert="true|false"> <key .... > <property .... /> <properties .... /> ..... </joined-subclass>
(1)name: The fully qualified class name of the subclass.
(2)proxy (optional): Specifies a class or interface to use for lazy initializing proxies.
(3)lazy (optional): Setting lazy="true" is a shortcut equalivalent to specifying the name of the class itself as the proxy interface.
No discriminator column is required for this mapping strategy. Each subclass must, however, declare a table column holding the object identifier using the <key> element. The mapping at the start of the chapter would be re-written as:
<?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>
For information about inheritance mappings, see Chapter 8, Inheritance Mapping.
union方式關聯的子類
A third option is to map only the concrete classes of an inheritance hierarchy to tables, (the table-per-concrete-class strategy) where each table defines all persistent state of the class, including inherited state. In NHibernate, it is not absolutely necessary to explicitly map such inheritance hierarchies. You can simply map each class with a separate <class> declaration. However, if you wish use polymorphic associations (e.g. an association to the superclass of your hierarchy), you need to use the <union-subclass> 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 .... /> <properties .... /> ..... </union-subclass>
(1)name: The fully qualified class name of the subclass.
(2)table: The name of the subclass table.
(3)proxy (optional): Specifies a class or interface to use for lazy initializing proxies.
(4)lazy (optional, defaults to true): Setting lazy="false" disables the use of lazy fetching.
No discriminator column or key column is required for this mapping strategy.
For information about inheritance mappings, see Chapter 8, Inheritance Mapping.
join
Using the <join> element, it is possible to map properties of one class to several tables, when there's a 1-to-1 relationship between the tables.
<join table="tablename" (1) schema="owner" (2) fetch="join|select" (3) inverse="true|false" (4) optional="true|false"> (5) <key ... /> <property ... /> ... </join>
(1)table: The name of the joined table.
(2)schema (optional): Override the schema name specified by the root <hibernate-mapping> element.
(3)fetch (optional - defaults to join): If set to join, the default, NHibernate will use an inner join to retrieve a <join> defined by a class or its superclasses and an outer join for a <join> defined by a subclass. If set to select then NHibernate will use a sequential select for a <join> defined on a subclass, which will be issued only if a row turns out to represent an instance of the subclass. Inner joins will still be used to retrieve a <join> defined by the class and its superclasses.
(4)inverse (optional - defaults to false): If enabled, NHibernate will not try to insert or update the properties defined by this join.
(5)optional (optional - defaults to false): If enabled, NHibernate will insert a row only if the properties defined by this join are non-null and will always use an outer join to retrieve the properties.
For example, the address information for a person can be mapped to a separate table (while preserving value type semantics for all properties):
<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> ...
This feature is often only useful for legacy data models, we recommend fewer tables than classes and a fine-grained domain model. However, it is useful for switching between inheritance mapping strategies in a single hierarchy, as explained later.
map, set, list, bag
集合在之后的章節介紹。
import
Suppose your application has two persistent classes with the same name, and you don't want to specify the fully qualified name in NHibernate queries. Classes may be "imported" explicitly, rather than relying upon auto-import="true". You may even import classes and interfaces that are not explicitly mapped.
<import class="System.Object" rename="Universe"/>
<import class="ClassName" (1) rename="ShortName" (2) />
(1)class: The fully qualified class name of any .NET class, including its assembly name.
(2)rename (optional - defaults to the unqualified class name): A name that may be used in the query language.
NHibernate的類型
實體和值
想要明白各種關於持久化服務的.NET 語言級別對象,我們需要把他們區分成以下兩種類型:
實體和其他與實體關聯的對象獨立。和實體相反,普通的.NET模型的關聯會被GC回收。實體必須被顯式地保存和刪除(除了父類的級聯保存和刪除)。這個和依賴可獲得性(reachability)來持久化的ODMG模型對象不同——並且相應的更接近在大系統中使用應用程序對象的方式。實體支持循環和共享的引用。它們也是有版本的。
實體的持久化狀態包括了對其他實體的引用和值類型實例,值和基類,集合,組件和特定的不變對象。和實體不同,值類型(尤其是集合和組件)依賴可獲得性(reachability)來進行持久化和刪除。因為值類型對象(和基類)的持久化和刪除都通過他們包含的實體,因此他們不需要獨立的版本信息。值類型沒有獨立的標識符,因此他們不能夠被兩個實體或者集合共享。
對於.NET的可空類型(例如,不繼承自System.ValueType)所有的NHibernate類型,除了集合,都支持null語法。
到這里位置,我們使用“持久化類”來指實體。我們將會繼續這么做。嚴格來說,並不是所有的用戶自定義的包含持久化狀態的類都是實體。組件是一個包含值類型語法的自定義類型。
基礎值類型
基礎值類型主要分成三類——System.ValueType 類型, System.Object 類型, 和 System.Object 類型。和.NET類型一樣,System.ValueType 的值類型不能保存null值,而System.Object 類型可以為null。
System.ValueType映射類型
| NHibernate類型 |
.NET類型 |
數據庫類型 |
備注 |
| AnsiChar |
System.Char |
DbType.AnsiStringFixedLength - 1 char |
|
| Boolean | System.Boolean |
DbType.Boolean |
Default when no type attribute specified. |
| Byte | System.Byte |
DbType.Byte |
Default when no type attribute specified. |
| Char | System.Char |
DbType.StringFixedLength - 1 char |
Default when no type attribute specified. |
| Date | System.DateTime |
DbType.Date |
type="Date" must be specified. |
| DateTime | System.DateTime |
DbType.DateTime - ignores the milliseconds |
Default when no type attribute specified. |
| DateTime2 | System.DateTime |
DbType.DateTime2 |
type="DateTime2" must be specified. |
| DbTimestamp | System.DateTime |
DbType.DateTime - as specific as database supports. |
type="DbTimestamp" must be specified. When used as a version field, uses the database's current time rather than the client's current time. |
| LocalDateTime | System.DateTime |
DbType.DateTime - ignores the milliseconds |
Ensures the DateTimeKind is set to DateTimeKind.Utc |
| UtcDateTime | System.DateTime |
DbType.DateTime - ignores the milliseconds |
Ensures the DateTimeKind is set to DateTimeKind.Utc |
| Decimal | System.Decimal |
DbType.Decimal |
Default when no type attribute specified. |
| Double | System.Double |
DbType.Double |
Default when no type attribute specified. |
| Guid | System.Guid |
DbType.Guid |
Default when no type attribute specified. |
| Int16 | System.Int16 |
DbType.Int16 |
Default when no type attribute specified. |
| Int32 | System.Int32 |
DbType.Int32 |
Default when no type attribute specified. |
| Int64 | System.Int64 |
DbType.Int64 |
Default when no type attribute specified. |
| PersistentEnum | A System.Enum |
The DbType for the underlying value. |
Do not specify type="PersistentEnum" in the mapping. Instead specify the Assembly Qualified Name of the Enum or let NHibernate use Reflection to "guess" the Type. The UnderlyingType of the Enum is used to determine the correct DbType. |
| Single | System.Single |
DbType.Single |
Default when no type attribute specified. |
| Ticks | System.DateTime |
DbType.Int64 |
type="Time" must be specified. |
| Time | System.DateTime |
DbType.Time |
type="Time" must be specified. |
| TimeAsTimeSpan | System.TimeSpan |
DbType.Time |
type="TimeAsTimeSpan" must be specified. |
| TimeSpan | System.TimeSpan |
DbType.Int64 |
Default when no type attribute specified. |
| Timestamp | System.DateTime |
DbType.DateTime - as specific as database supports. |
type="Timestamp" must be specified. |
| TrueFalse | System.Boolean |
DbType.AnsiStringFixedLength - 1 char either 'T' or 'F' |
type="TrueFalse" must be specified. |
| YesNo | System.Boolean |
DbType.AnsiStringFixedLength - 1 char either 'T' or 'F' |
type="YesNo" must be specified. |
System.Object映射類型
| NHibernate類型 |
.NET類型 |
數據庫類型 |
備注 |
| AnsiString |
System.String |
DbType.AnsiString |
type="AnsiString" must be specified. |
| CultureInfo | System.Globalization.CultureInfo |
DbType.String - 5 chars for culture |
Default when no type attribute specified. |
| Binary | System.Byte[] | DbType.Binary | Default when no type attribute specified. |
| Type | System.Type | DbType.String holding Assembly Qualified Name. | Default when no type attribute specified. |
| String | System.String | DbType.String | Default when no type attribute specified. |
Object映射類型
| NHibernate類型 |
.NET類型 |
數據庫類型 |
備注 |
| StringClob | ystem.String | DbType.String |
type="StringClob" must be specified. Entire field is read into memory. |
| BinaryBlob | System.Byte[] |
DbType.Binary |
type="BinaryBlob" must be specified. Entire field is read into memory. |
| Serializable | Any System.Object that is marked with SerializableAttribute. |
DbType.Binary |
type="Serializable" should be specified. This is the fallback type if no NHibernate Type can be found for the Property. |
Nhibernate支持一些額外的類型名稱來兼容Java的Hibernate()type="integer"或者type="int"都會映射到Int32 的NHibernate類型,type="short"會映射到Int16的NHibernate類型。想要查閱所有的轉化規則,你可以看源碼中NHibernate.Type.TypeFactory類的靜態構造函數。
自定義值類型
It is relatively easy for developers to create their own value types. For example, you might want to persist properties of type Int64 to VARCHAR columns. NHibernate does not provide a built-in type for this. But custom types are not limited to mapping a property (or collection element) to a single table column. So, for example, you might have a property Name { get; set; } of type String that is persisted to the columns FIRST_NAME, INITIAL, SURNAME.
To implement a custom type, implement either NHibernate.UserTypes.IUserType or NHibernate.UserTypes.ICompositeUserType and declare properties using the fully qualified name of the type. Check out NHibernate.DomainModel.DoubleStringType to see the kind of things that are possible.
<property name="TwoStrings" type="NHibernate.DomainModel.DoubleStringType, NHibernate.DomainModel"> <column name="first_string"/> <column name="second_string"/> </property>
Notice the use of <column> tags to map a property to multiple columns.
The ICompositeUserType, IEnhancedUserType, INullableUserType, IUserCollectionType, and IUserVersionType interfaces provide support for more specialized uses.
You may even supply parameters to an IUserType in the mapping file. To do this, your IUserType must implement the NHibernate.UserTypes.IParameterizedType interface. To supply parameters to your custom type, you can use the <type> element in your mapping files.
<property name="priority"> <type name="MyCompany.UserTypes.DefaultValueIntegerType"> <param name="default">0</param> </type> </property>
The IUserType can now retrieve the value for the parameter named default from the IDictionary object passed to it.
If you use a certain UserType very often, it may be useful to define a shorter name for it. You can do this using the <typedef> element. Typedefs assign a name to a custom type, and may also contain a list of default parameter values if the type is parameterized.
<typedef class="MyCompany.UserTypes.DefaultValueIntegerType" name="default_zero"> <param name="default">0</param> </typedef>
<property name="priority" type="default_zero"/>
It is also possible to override the parameters supplied in a typedef on a case-by-case basis by using type parameters on the property mapping.
Even though NHibernate's rich range of built-in types and support for components means you will very rarely need to use a custom type, it is nevertheless considered good form to use custom types for (non-entity) classes that occur frequently in your application. For example, a MonetaryAmount class is a good candidate for an ICompositeUserType, even though it could easily be mapped as a component. One motivation for this is abstraction. With a custom type, your mapping documents would be future-proofed against possible changes in your way of representing monetary values.
任意類型mapping
There is one further type of property mapping. The <any> mapping element defines a polymorphic association to classes from multiple tables. This type of mapping always requires more than one column. The first column holds the type of the associated entity. The remaining columns hold the identifier. It is impossible to specify a foreign key constraint for this kind of association, so this is most certainly not meant as the usual way of mapping (polymorphic) associations. You should use this only in very special cases (eg. audit logs, user session data, etc).
<any name="AnyEntity" id-type="Int64" meta-type="Eg.Custom.Class2TablenameType"> <column name="table_name"/> <column name="id"/> </any>
The meta-type attribute lets the application specify a custom type that maps database column values to persistent classes which have identifier properties of the type specified by id-type. If the meta-type returns instances of System.Type, nothing else is required. On the other hand, if it is a basic type like String or Char, you must specify the mapping from values to classes.
<any name="AnyEntity" 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>
<any name="PropertyName" (1) id-type="idtypename" (2) meta-type="metatypename" (3) cascade="none|all|save-update" (4) access="field|property|nosetter|ClassName" (5) optimistic-lock="true|false" (6) > <meta-value ... /> <meta-value ... /> ..... <column .... /> <column .... /> ..... </any>
(1)name: the property name.
(2)id-type: the identifier type.
(3)meta-type (optional - defaults to Type): a type that maps System.Type to a single database column or, alternatively, a type that is allowed for a discriminator mapping.
(4)cascade (optional - defaults to none): the cascade style.
(5)access (optional - defaults to property): The strategy NHibernate should use for accessing the property value.
(6)optimistic-lock (optional - defaults to true): Specifies that updates to this property do or do not require acquisition of the optimistic lock. In other words, define if a version increment should occur if this property is dirty.
SQL引用標識符
You may force NHibernate to quote an identifier in the generated SQL by enclosing the table or column name in backticks in the mapping document. NHibernate will use the correct quotation style for the SQL Dialect (usually double quotes, but brackets for SQL Server and backticks for MySQL).
<class name="LineItem" table="`Line Item`"> <id name="Id" column="`Item Id`"/><generator class="assigned"/></id> <property name="ItemNumber" column="`Item #`"/> ... </class>
模塊mapping文件
It is possible to define subclass and joined-subclass mappings in saparate mapping documents, directly beneath hibernate-mapping. This allows you to extend a class hierachy just by adding a new mapping file. You must specify an extends attribute in the subclass mapping, naming a previously mapped superclass. Use of this feature makes the ordering of the mapping documents important!
<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 are properties which have their values generated by the database. Typically, NHibernate applications needed to Refresh objects which contain any properties for which the database was generating values. Marking properties as generated, however, lets the application delegate this responsibility to NHibernate. Essentially, whenever NHibernate issues an SQL INSERT or UPDATE for an entity which has defined generated properties, it immediately issues a select afterwards to retrieve the generated values.
Properties marked as generated must additionally be non-insertable and non-updateable. Only Section 5.1.8, “version (optional)”, Section 5.1.9, “timestamp (optional)”, and Section 5.1.10, “property” can be marked as generated.
never (the default) - means that the given property value is not generated within the database.
insert - states that the given property value is generated on insert, but is not regenerated on subsequent updates. Things like created-date would fall into this category. Note that even though Section 5.1.8, “version (optional)” and Section 5.1.9, “timestamp (optional)” properties can be marked as generated, this option is not available there...
always - states that the property value is generated both on insert and on update.
輔助的數據庫對象
Allows CREATE and DROP of arbitrary database objects, in conjunction with NHibernate's schema evolution tools, to provide the ability to fully define a user schema within the NHibernate mapping files. Although designed specifically for creating and dropping things like triggers or stored procedures, really any SQL command that can be run via a IDbCommand.ExecuteNonQuery() method is valid here (ALTERs, INSERTS, etc). There are essentially two modes for defining auxiliary database objects.
The first mode is to explicitly list the CREATE and DROP commands out in the mapping file:
<nhibernate-mapping> ... <database-object> <create>CREATE TRIGGER my_trigger ...</create> <drop>DROP TRIGGER my_trigger</drop> </database-object> </nhibernate-mapping>
The second mode is to supply a custom class which knows how to construct the CREATE and DROP commands. This custom class must implement the NHibernate.Mapping.IAuxiliaryDatabaseObject interface.
<hibernate-mapping> ... <database-object> <definition class="MyTriggerDefinition, MyAssembly"/> </database-object> </hibernate-mapping>
You may also specify parameters to be passed to the database object:
<hibernate-mapping> ... <database-object> <definition class="MyTriggerDefinition, MyAssembly"> <param name="parameterName">parameterValue</param> </definition> </database-object> </hibernate-mapping>
NHibernate will call IAuxiliaryDatabaseObject.SetParameterValues passing it a dictionary of parameter names and values.
Additionally, these database objects can be optionally scoped such that they only apply when certain dialects are used.
<hibernate-mapping> ... <database-object> <definition class="MyTriggerDefinition"/> <dialect-scope name="NHibernate.Dialect.Oracle9iDialect"/> <dialect-scope name="NHibernate.Dialect.Oracle8iDialect"/> </database-object> </hibernate-mapping>
