NHibernate學習手記(5) - 簡單的對象映射

NH的online document中討論了三種情況的的o/r mapping:
1)one-to-one
2)one-to-many / many-to-one
3)many-to-many

因爲官方文檔介紹得很少,學起來非常費勁,我在這裏做一個學習總結,希望能引起大家的繼續討論。

爲了便於描述。,本系列學習手記將引入Category和Item對象,分別實現以下關係:
1)Category和Item對象之前不存在關係(none-association);
2)Category和Item對象之前存在着one-to-many的關係,即一個Category對象對應多個Item對象;
3)Category和Item對象之間存在着many-to-many的關係;
4)Category和Item對象之間存在着one-to-one的關係(我認爲這是最少用到的關係類型)。

本文將以Category對象的簡單操作來示例第一種情況。Category對象只有兩個屬性:ID(guid)和Name(string),我們來看看怎麼使用NH來進行Category對象的CRUD操作。

主要內容
1、準備數據庫
2、編寫配置文件
3、編寫POCO類
4、Category對象的CRUD操作

一、準備數據庫
新建數據表,對應於Category對象的屬性,該數據表只有CategoryID和Name兩個字段:
CREATE TABLE [dbo].[nh_categories] (
    
[CategoryID] [uniqueidentifier] NOT NULL ,
    
[Name] [nvarchar] (50) COLLATE Chinese_PRC_CI_AS NOT NULL 
ON [PRIMARY]
GO

二、編寫配置文件
nh最令我不滿的一點是即使是嘗試一個非常簡單的crud操作,都要先編寫配置文件,而且目前好像也沒有很好的自動生成工具。下面讓我們新建Console工程BasicMappings,編寫nhibernate配置文件和xml mappings文件。
1、新建文件hibernate.cfg.xml,並輸入一下內容:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-configuration  xmlns="urn:nhibernate-configuration-2.0" >
    
<session-factory name="CollectionMappings">
        
<!-- properties -->
        
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
        
<property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
        
<property name="connection.connection_string">Server=localhost;database=NHTrial;User Id=sa;Password=sa</property>
        
<property name="show_sql">false</property>
        
<property name="dialect">NHibernate.Dialect.MsSql2000Dialect</property>
        
<property name="use_outer_join">true</property>
    
</session-factory>
    
</hibernate-configuration>
其中配置節的解釋可以參考我之前發的NHibernate學習手記(3) - NH的配置信息,其中的show_sql表示是否在Console輸出nh進行數據操作生成的sql語句。
同時,因爲hibernate.cfg.xml文件要求在輸出目錄下,還需要在[項目屬性]->[通用屬性]->[生成事件]->[生成後事件命令行]中添加
copy $(ProjectDir)/hibernate.cfg.xml $(TargetDir)
用於在生成exe時把hibernate.cfg.xml文件拷貝到輸出目錄中。

2、新建objects.hbm.xml文件,並把文件屬性設爲”嵌入資源“。NH將根據*.hbm.xml中的配置進行o/r mapping的操作。
輸入以下內容:
<?xml version="1.0" encoding="utf-8" ?> 
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.0">
    
<!-- mapping for Category -->
    
<class name="BasicMappings.Category, BasicMappings" table="nh_categories">
        
<id name="CategoryID" column="CategoryID" type="Guid">
            
<generator class="guid" />
        
</id>
        
        
<property name="Name" type="string" length="50" />
    
</class>
</hibernate-mapping>

emidea.gif把NH源文件中包含的nhibernate-mapping-2.0.xsd文件拷貝到%.NET 2003安裝目錄%/Common7/Packages/schemas/xml文件夾下,可獲得intellisense的輸入支持。

1) class節點。該節點指示NH對該類的對象進行orm操作的所需的信息。
class節點的部分重要屬性含義:

Attributes<?XML:NAMESPACE PREFIX = O />

Usage

Example

name

指示所映射類的全限定名稱,格式如namespace.className.assemblyNameRequired

BasicMappings.Category, BasicMappings

table

指示該類所對應的數據表名稱。Required

nh_categories

schema

指示所使用的數據庫schema,默認繼承hibernate-mappingsschema設定。

NhTrial.dbo

mutable

指示該類的對象可變,即nh是否可對此對象進行保存和修改的持久化操作。Optional

 


其他還有lazy, persister和proxy等可選屬性,在後續文章中會有介紹。

2) id節點。id節點設定了該class的主鍵信息:

Attributes

Usage

Example

name

指示對象的主鍵屬性的名稱

ID

type

指示該屬性的NH數據類型。可選,NH可自動轉換。

Guid

column

指示該屬性對應的數據字段名稱,默認取name屬性值。Optional

ID

unsaved-value

指示該對象未保存的主鍵屬性的取值,用於ISession.SaveOrUpdate()操作提供根據。Optional

 

access

指示NH對該屬性所採取的access-strategynaming-strategy。默認從hibernate-mappingdefault-access繼承。Optional

field.camelcase


3)generator節點。該節點指示了主鍵的生成方式

class

usage

identity

支持DB2/MySql/MsSql/Sybase/HypersonicSql,生成整型自增id

sequence

支持DB2/PostgreSql/Oracle,生成整型自增id

guid

指示使用Guid.NewGuid來生成主鍵值

native

指示根據數據類型,按照identitysequence或hilo的方式生成主鍵

assigned

指示主鍵值的設定由用戶代碼完成,NH無需理會



4)property節點。該節點指示了NH進行屬性映射所需要的信息:

Attributes

Usage

Example

name

指示對象的屬性名稱

Name

column

指示該屬性對應的數據列名稱,默認取name屬性。Optional

Name

type

指示該屬性的NH數據類型。可選,NH可自動轉換

String

update

指示在進行update時是否保存該屬性的設置。

true|false

insert

指示在進行save操作時是否保存該屬性的設置。

true | false

formula

指示該屬性爲表達式,將使column設置失效。

ID + Name

access

指示NH對該屬性所採取的access-strategynaming-strategy。默認從hibernate-mappingdefault-access繼承。Optional

field.camelcase


解釋一下反覆出現access設置,access=access-strategy + . + naming-strategy。
access-trategy的取值包括:
1)Property:默認值,NH在進行orm時將使用已定義的getter和setter來進行該屬性的讀取和設置。
2)field:NH在進行orm時,將使用反射來讀取和設置數據成員。
3)nosetter:使用getter來讀取屬性值,使用反射方式來設置對應的數據成員。
4) ClassName:使用指定的訪問類進行屬性的訪問和設置,ClassName爲該訪問類的全限定名稱。

naming -strategy指示了在映射時,應該對name屬性進行格式轉換的方式。除非access-strategy爲nosetter,naming- strategy爲可選設置,當未設置naming-strategy時,將直接使用name屬性值進行映射。下面以name=FooBar爲例,看看 naming-strategy的轉換後的結果:

naming-strategy

example

未設置

FooBar

camelcase

fooBar

camelcase-underscore

_fooBar

lowercase

Foobar

lowercase-underscore

_foobar

pascalcase-underscore

FooBar

pascalcase-m-underscore

_FooBar

 

 


emthup.gif除了上述編寫xml mappings文件的方式外,NHibernateContrib庫中還有通過Attribute的方式來設定orm信息的方式,在後續文章中會有介紹。

三、編寫POCO類
POCO類的說明可參考我之前發的NHibernate學習手記(4) - 持久化類(Persistent class)的設計
新建類Category.cs,內容如下:
 1 using System;
 2 
 3 namespace BasicMappings
 4 {
 5     /// <summary>
 6     /// Category 的摘要說明
 7     /// </summary>
 8     /// 創 建 人: Aero
 9     /// 創建日期: 2006-3-17
10     /// 修 改 人: 
11     /// 修改日期:
12     /// 修改內容:
13     /// 版    本:
14     public class Category
15     {
16         private Guid _categoryId;
17 
18         private string _name = string.Empty;
19 
20         public Guid CategoryID
21         {
22             get { return this._categoryId; }
23             set { this._categoryId = value; }
24         }
25 
26         public string Name
27         {
28             get { return this._name; }
29             set { this._name = value; }
30         }
31 
32         #region 構造函數
33         /// <summary>
34         /// 默認無參構造函數
35         /// </summary>
36         /// 創 建 人: Aero
37         /// 創建日期: 2006-3-17
38         /// 修 改 人: 
39         /// 修改日期:
40         /// 修改內容:
41         public Category()
42         {
43 
44         }
45         
46         #endregion
47     }
48 }
49 

四、Category對象的CRUD操作
本文將演示如何保存隨機生成的Category對象,和如何實現對象的查詢。簡單的CRUD操作可參考NHibernate學習手記(1) - 對象的簡單CRUD操作 。新建文件Programm.cs:

1、保存Category對象
 1 static readonly ISessionFactory _factory;
 2 
 3 static ISessionFactory Factory
 4 {
 5     get { return Programm._factory; }
 6 }
 7 
 8 /// <summary>
 9 /// initializing
10 /// </summary>
11 static Programm()
12 {
13     Configuration cfg = new Configuration().Configure();
14     cfg.AddAssembly("BasicMappings");
15 
16     // initialize factory
17     Programm._factory = cfg.BuildSessionFactory();
18 }
19 
20 /// <summary>
21 /// save transient object
22 /// </summary>
23 /// <param name="persistentObj"></param>
24 static void Save(object persistentObj)
25 {
26     using (ISession session = Programm.Factory.OpenSession())
27     {
28         ITransaction trans = session.BeginTransaction();
29 
30         try
31         {
32             session.Save(persistentObj);
33             trans.Commit();
34         }
35         catch
36         {
37             trans.Rollback();
38             throw;
39         }
40     }
41 }

1)line13-18,初始化Configuration和SessionFactory。line13中的
Configuration cfg = new Configuration().Configure();
將根據hibernate.cfg.xml文件的配置創建Congfiguration對象,但它並不知道任何orm信息。我們可以通過以下幾種方式給Configuration對象提供o/r mapping的信息:

A、通過Configuration.AddAssembly(assemblyName),nh將自動查找所制定程序集中的所有*.hbm.xml嵌入文件獲取o/r mapping信息
cfg.AddAssembly("BasicMappings");

B、通過Configuration.AddType(Type t),nh將自動查找當前程序集中名爲typeName.hbm.xml的嵌入文件該類的o/r mapping信息
cfg.AddClass(typeof(Category));

C、通過Configuration.AddXmlFile(fileName),nh將自動查找當前程序集中名爲fileName的嵌入文件,獲取o/r mapping信息
cfg.AddXmlFile("objects.hbm.xml");

2)line24-41,保存對象。NH中進行對象的save/update/delete操作時都必須打開事務,推薦使用using的方式來使用ISession對象。

2、查詢全部對象

 1 /// <summary>
 2 /// list all category objects in the repository
 3 /// </summary>
 4 static void ListCategory()
 5 {
 6     using (ISession session = Programm.Factory.OpenSession())
 7     {
 8         IList categories = session.CreateCriteria(typeof(Category)).List();
 9 
10         foreach (Category category in categories)
11         {
12             Console.WriteLine(category.CategoryID + " " + category.Name);
13         }
14     }
15 }

3、刪除全部對象
作爲一種好習慣,我們在示例代碼的開始前和結束後都應該清空所添加的臨時數據:)
 1 /// <summary>
 2 /// remove all category objects from the repository
 3 /// </summary>
 4 static void Clear()
 5 {
 6     using (ISession session = Programm.Factory.OpenSession())
 7     {
 8         ITransaction trans = session.BeginTransaction();
 9 
10         try
11         {
12             session.Delete("from Category");
13             trans.Commit();
14         }
15         catch
16         {
17             trans.Rollback();
18             throw;
19         }
20     }
21 }

在刪除所有Category對象時,我們用了一小段HQL代碼“from Category”,在後續的文章裏會有介紹。

完整的代碼可從下載:ObjectsMapping.rar,其中的ExtendedCategory示例了更多的ISession的操作。
發佈了70 篇原創文章 · 獲贊 5 · 訪問量 31萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章