[NHibernate]O/R Mapping基礎


系列文章

[Nhibernate]體系結構

[NHibernate]ISessionFactory配置

[NHibernate]持久化類(Persistent Classes)

引言

對象和關系數據庫之間的映射是用一個XML文檔(XML document)來定義的。這個映射文檔被設計為易讀的,並且拒絕惡意手工修改。映射語言以.NET為中心的,意味着映射是持久化類的定義來創建的,而非表的定義。

請注意,雖然很多Hibernate用戶選擇手工定義XML映射文檔,也有一些工具來生成映射文檔,包括XDoclet,Middlegn和AndroMDA(這里是Nhibernate文檔中移除沒有從Hibernate文檔中轉換過來的部分),NHibernate中並沒有像XDoclet,Middlegn和AndroMDA這樣的工具,在運用中,一般使用代碼生成器來生成XML配置文檔。

一個映射的例子

 1 <?xml version="1.0" ?>
 2 <hibernate-mapping xmlns="urn:nhibernate-mapping-2.0"
 3  namespace="Eg" assembly="Eg">
 4  <class name="Cat" table="CATS" discriminator-value="C">
 5   <id name="Id" column="uid" type="Int64">
 6    <generator class="hilo"/>
 7   </id>
 8   <discriminator column="subclass" type="Char"/>
 9   <property name="Birthdate" type="Date"/>
10   <property name="Color" not-null="true"/>
11   <property name="Sex" not-null="true" update="false"/>
12   <property name="Weight"/>
13   <many-to-one name="Mate" column="mate_id"/>
14   <set name="Kittens">
15    <key column="mother_id"/>
16    <one-to-many class="Cat"/>
17   </set>
18   <subclass name="DomesticCat" discriminator-value="D">
19    <property name="Name" type="String"/>
20   </subclass>
21  </class>
22  <class name="Dog">
23   <!-- mapping for Dog could go here -->
24  </class>
25 </hibernate-mapping>

我們只描述NHibernate在運行時用到的文檔元素和屬性。映射文檔包括一些額外的可選屬性和元素,他們在使用schema導出工具的時候會影響導出的數據庫schema結果。(比如,not-null屬性。)

Schema

所有的XML映射都需要使用nhibenate-mapping-2.0 schema。目前schema可以在NHibernate的資源路徑或者是NHibernate.dll的嵌入資源(EmbeddedResource)中找到。NHibernate總是會優先使用嵌入在資源中的schema文件。

在使用VisualStudio.Net時,你應該將hibernate-mapping拷貝到C:\ProgramFiles\Microsoft Visual Studio.NET2003\Common7\Packages\schemas\xml路徑中,以獲取智能感知功能。或者在解決方案中,新建一個解決方案文件夾,將hibernate-mapping拷入該解決方案文件夾中也可以獲得智能感知功能。

hibernate-mapping

這個元素包括四個可選的屬性。schema屬性,指明了這個映射所引用的表所在的schema名稱。假若指定了這個屬性,表名會加上所指定的schema的名字擴展schema的名字擴展為全限定名。假若沒有指定,表明就不會使用全限定名。default-cascade指定了未注明cascade屬性和集合類會采用什么樣的默認級聯風格。auto-import屬性默認讓我們在查詢語言中可以使用非全限定名的類名。default-access告訴我們怎么訪問屬性。

1 <hibernate-mapping
2  schema="schemaName" (1)
3  default-cascade="none|save-update" (2)
4  auto-import="true|false" (3)
5  default-access="property|field|nosetter|ClassName" (4)
6  assembly="assembly.name" (5)
7  namespace="namespace.name" (6)
8 > 

(1)schema(可選):數據庫schema名稱。

(2)default-cascade(可選-默認為none):默認的級聯風格。

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

(4)default-acess(可選-默認為property):NHibernate訪問屬性時的策略。

(5)assembly(可選):指定一個程序集,如果在映射文檔中沒有指定程序集,就使用這個程序集。

(6)namespace(可選):指定一個命名空間前綴,如果在一個映射文檔中沒有指定全限定名,就使用這個命名空間名。

假若你有兩個持久化類,他們的非全限定名是一樣的(就是在不同的命名空間里面),你應該設置auto-import=“false”。假若說你把一個“import過”的名字同時對應兩個類,NHibernate會拋出一個異常。

class

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

 1 <class
 2  name="ClassName" (1)
 3  table="tableName"(2)
 4  discriminator-value="discriminator_value"(3)
 5  mutable="true|false"(4)
 6  schema="owner"(5)
 7  proxy="ProxyInterface"(6)
 8  dynamic-update="true|false"(7)
 9  dynamic-insert="true|false"(8)
10  polymorphism="implicit|explicit"(9)
11  where="arbitrary sql where condition"(10)
12  persister="PersisterClass"(11)
13  lazy="true|false"(12)
14 />

(1)name:持久化類(或者接口)的全限定名。
(2)table:對應的數據庫表名。

(3)discriminator-value(可選-默認和類名一樣):一個用於區分不同的子類的值,在多態行為時使用。

(4)mutable(可選,默認為true):表明該類的實例可變(不可變)。

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

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

(7)dynamic-update(可選,默認為false):指定用於UPDATE的sql將會在運行時動態生成,並且只更新哪些改變過的字段。

(8)dynamic-insert(可選,默認為false):指定用於INSERT的sql將會在運行時動態生成,並且只包含哪些非空字段。

(9)polymorphism(可選,默認為implicit(隱式)):界定是隱式還是顯式的使用查詢多態。

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

(11)persister(可選):指定一個定制的IClassPersister。

(12)lazy(可選):假若設置lazy=“true”,就是設置這個類自己的名字作為proxy接口的一種等價快捷方式。

若指明的持久化類實際上是一個接口,也可以被完美的接受。其后你可以用<subclass>來指定該接口的實際實現類名。你可以持久化任static(靜態的)內部類。記得應該使用標准的類名格式,就是說比如:Eg.Foo+Bar.

不可變類,mutable="false"不可以被應用更新或者刪除。這可以讓NHibernate做一些小小的性能優化。

可選的proxy屬性可以允許延遲加載類的持久化實例。NHibernate開始會返回實現了這個命名接口或者子類(通過的Castle.DynamicProxy)。當代理的某個方法被實際調用的時候,真實的持久化對象才會被裝載。參見下面的“用於延遲加載的代理”。

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>元素中可能需要再次設置。這些設置是否能夠提高效率要視情況而定。

id 

被映射的類必須聲明對應數據表主鍵字段。大多數類有一個屬性,為每一個實現包含唯一的標識。<id>元素定義了該屬性到數據庫表主鍵字段的映射。

1 <id 
2  name="propertyName" (1)
3  type="typename" (2)
4  column="column_name" (3)
5  unsaved-value="any|none|null|id_value" (4)
6  access="field|property|nosetter|ClassName"> (5)
7  <generator class="generatorClass"/>
8 </id>  

(1)name(可選):標識屬性的名字。

(2)type(可選):標識NHibernate類型的名字。

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

(4)unsaved-value(可選-默認為null):一個特定的標識屬性值,用來標識該實例是剛剛創建的,尚未保存。這可以把這種實例和從以前的sssion中裝載過(可能又做過修改)但未再次持久化的實例區分開來。

(5)access(可選-默認為property):NHibernate用來訪問屬性值的策略。

如果name屬性不存在,會認為這個類沒有標識屬性。

unsaved-value屬性很重要!如果你的類的標識屬性不是默認為null的,你應該指定正確的默認值。特別重要的是在使用值類型System.ValueType,例如System.Int32或者System.Guid作為你的<id>屬性時確保清楚的設置這個屬性,因為System.ValueType對象不可能為null值。

還有一個另外的<composite-id>聲明可以訪問舊式的多主鍵數據。不鼓勵使用這種方式。

gennerator

 必須聲明的<generator>子元素是一個.NET類的名字,用來為該持久化類的屬性生成唯一的標識。如果這個生成器實例需要某些配置值或者初始化參數,用<param>元素傳遞。

1 <id name="Id" type="Int64" column="uid" unsaved-value="0">
2  <generator class="NHibernate.Id.TableHiLoGenerator">
3   <param name="table">uid_table</param>
4   <param name="column">next_hi_value_column</param>
5  </generator>
6 </id>

所有的生成器都實現NHibernate.Id.IdentifierGenerator接口。這是一個非常簡單的接口;某些應用程序可以選擇提供他們自己特定的實現。當然,NHibernate提供了很多內置的實現。下面是一些內置生成器的快捷名字:

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

hilo和seqhilo生成器給出了兩種hi/lo算法實現,這是一種很令人滿意的標識符生成算法。第一種實現需要一個“特殊”的數據庫表來保存下一個可用的“hi”值。第二種實現使用一個Oracle風格的序列(在被支持的情況下)。

 1 <id name="Id" type="Int64" column="cat_id">
 2  <generator class="hilo">
 3   <param name="table">hi_value</param>
 4   <param name="column">next_value</param>
 5   <param name="max_lo">100</param>
 6  </generator>
 7 </id>
 8 <id name="Id" type="Int64" column="cat_id">
 9  <generator class="seqhilo">
10   <param name="sequence">hi_value</param>
11   <param name="max_lo">100</param>
12  </generator>
13 </id>

很不幸,你在為NHibernate自行提供Connection時無法使用hilo。Hibernate必須能夠在一個新的事務中得到一個“hi”值。

UUID Hex算法 

1 <id name="Id" type="String" column="cat_id">
2  <generator class="uuid.hex">
3   <param name="format">format_value</param>
4   <param name="seperator">seperator_value</param>
5  </generator>
6 </id>

UUID是通過調用Guid.NewGuid().ToString(format)產生的。format值的設置請參考MSDN文檔。默認的seperator很少也不應該被改變。format決定是否配置好的seperator,並提供自己使用。

UUID String算法

UUID是通過調用Guid.NewGuid().ToByteArray()並且把byte[]轉換成char[],char[]作為一個16個字符組成的字符串返回。

 GUID算法

guid標識符通過調用Guid.NewGuid()產生。為了提升Guids在MS SQL中作為主鍵,外鍵和索引的一部分時的性能,可以使用guid.comb。在別的數據庫中使用guid.comb的好處是支持非標准的GUID。

標識字段和序列(Identity columns and Sequences)

對於內部支持標識字段的數據庫(DB2,MySQL,Sybase,MS SQL),你可以使用identity關鍵字生成。對於內部支持序列的數據庫(DB2,Oracle, PostgreSQL),你可以使用sequence風格的關鍵字生成。這兩種方式對於插入一個新的對象都需要兩次SQL查詢。當使用MS SQL並且采用identity主鍵生成器,select SCOPE_IDENTITY()將會被附加到insert的sql語句,因而不可避免的執行兩個不同的IDbCommand。

1 <id name="Id" type="Int64" column="uid">
2  <generator class="sequence">
3    <param name="sequence">uid_sequence</param>
4  </generator>
5 </id>
6 <id name="Id" type="Int64" column="uid" unsaved-value="0">
7  <generator class="identity"/>
8 </id>

對於跨平台開發,native策略會從identity, sequence 和hilo中進行選擇,取決於底層數據庫的支持能力。

程序分配的標識符(Assigned Identifiers)

如果你需要應用程序分配一個標示符(而非NHibernate來生成它們),你可以使用assigned生成器。這種特殊的生成器會使用已經分配給對象的標識符屬性的標識符值。用這種特性來分配商業行為的關鍵字要特別小心(基本上總是一種可怕的設計決定)。

因為其繼承天性,使用這種生成器策略的實體不能通過ISession的SaveOrUpdate()方法保存。作為替代,你應該明確告知NHibernate是應該被save還是update,分別調用ISession的Save()或Update()方法。

聯合ID(composite-id)

1 <composite-id
2  name="propertyName"(1)
3  class="ClassName"(2)
4  unsaved-value="any|none"(3)
5  access="field|property|nosetter|ClassName">
6  <key-property name="propertyName" type="typename" column="column_name"/>
7  <key-many-to-one name="propertyName class="ClassName" column="column_name"/>
8  ......
9 </composite-id>

如果表使用聯合主鍵,你可以把類的多個屬性組合成為標識符屬性。<composite-id>元素接受<key-property>屬性映射和<key-many-to-one>屬性映射作為子元素。

1 <composite-id>
2  <key-property name="medicareNumber"/>
3  <key-property name="dependent"/>
4 </composite-id>

你的持久化類必須重載Equals()和HashCode()方法,來實現組合的標識符判斷等價.也必須實現Serializable接口

不幸的是,這種組合關鍵字的方法意味着一個持久化類是它自己的標識。除了對象自己之外,沒有什么方便的“把手”可用。你必須自己初始化持久化類的實例,在使用組合關鍵字Load()持久化狀態之前,必須填充他的聯合屬性。我們會在TODO: LINKTOCOMPENENTS 中說明一種更加方便的方法,把聯合標識實現為一個獨立的類,下面描述的屬性只對這種備用方法有效:

(1)

name (可選): 一個組件類型,持有聯合標識(參見下一節)。

(2)

class (可選 - 默認為通過反射(reflection)得到的屬性類型): 作為聯合標識的組件類名(參見下一節)。

(3)

unsaved-value (可選 - 默認為 none): 假如被設置為any的值,就表示新創建,尚未被持久化的實例將持有的值。

 識別器(discriminator)

在"一棵對象繼承樹對應一個表"的策略中,<discriminator>元素是必需的,它聲明了表的識別器字段。識別器字段包含標志值,用於告知持久化層應該為某個特定的行創建哪一個子類的實例。只能使用如下受到限制的一些類型:String, Char, Int32, Byte, Int16, Boolean, YesNo, TrueFalse.

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

(1)

column (可選 - 默認為 class) 識別器字段的名字

(2)

type (可選 - 默認為 String) 一個NHibernate字段類型的名字

(3)

force (可選 - 默認為 false) "強制"NHibernate指定允許的識別器值,就算取得的所有實例都是根類的。

(4)

insert (可選 - 默認為 true) 當識別器是被映射的組件的標識符的一部分時設置為false。

標識器字段的實際值是根據<class> 和<subclass>元素的discriminator-value得來的。

總結

ORM深入學習。

本文來自

《NHibernate中文文檔》


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM