系列文章
[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 中說明一種更加方便的方法,把聯合標識實現為一個獨立的類,下面描述的屬性只對這種備用方法有效:
name (可選): 一個組件類型,持有聯合標識(參見下一節)。 |
|
class (可選 - 默認為通過反射(reflection)得到的屬性類型): 作為聯合標識的組件類名(參見下一節)。 |
|
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 />
column (可選 - 默認為 class) 識別器字段的名字 |
|
type (可選 - 默認為 String) 一個NHibernate字段類型的名字 |
|
force (可選 - 默認為 false) "強制"NHibernate指定允許的識別器值,就算取得的所有實例都是根類的。 |
|
insert (可選 - 默認為 true) 當識別器是被映射的組件的標識符的一部分時設置為false。 |
標識器字段的實際值是根據<class> 和<subclass>元素的discriminator-value得來的。
總結
ORM深入學習。
本文來自
《NHibernate中文文檔》