原文地址:http://jasondentler.com/blog/2009/08/how-to-using-the-n-stack-part-3/?
Java – 一種代碼松散的XML
在我們學習 Fluent NHibernate 之前, 應該先了解下老式的 NHibernate 映射文件應該是怎樣寫的。 在一個典型的 NHibernate 配置中,你會有很多類似這樣的映射文件:
<?xml version="1.0" encoding="utf-8" ?> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"><class name="NStackExample.Address, NStackExample.Core" table="Address"><composite-id><key-many-to-one name="Person" class="NStackExample.Person, NStackExample.Core" column="ID" /><key-property name="Type" type="Int32" /></composite-id><property name="City" type="String" length="255" /><property name="Lines" type="String" length="255" /><property name="State" type="String" length="2" /><property name="Zip" type="String" length="10" /></class> </hibernate-mapping>
你必須為每一個實體類配置一個這樣的映射文件,這種做法是從 Java 的 Hibernate 中遺留下來的。在我看來,這么做是非常痛苦的,不過幸運的是,有一個好辦法來解決這個問題。
更好的選擇: Fluent Mappings
有了 Fluent NHibernate ,上面的映射文件就可以用下面這個類來代替:
using FluentNHibernate.Mapping;namespace NStackExample.Data {public class AddressMapping : ClassMap<Address>{public AddressMapping(){UseCompositeId().WithKeyReference(x => x.Person).WithKeyProperty(x => x.Type);Map(x => x.Lines).WithLengthOf(255);Map(x => x.City).WithLengthOf(255);Map(x => x.State).WithLengthOf(2);Map(x => x.Zip).WithLengthOf(5);}} }
看起來這個類可能比之前的映射文件還要復雜,但是因為有智能感知,我們能輕而易舉的完成,并且我們不用擔心魔字符串(magic strings)的問題。當你使用重構工具來改變屬性名稱的時候,你的映射文件也會同步改變。
現在大家都知道基本的概念了吧,那么讓我們繼續。
Where?
因為數據庫連接、 NHibernate 配置、實體類映射和 DAO 的實現只是我們選擇的ORM的執行細節,所以應該把他們放到一個單獨的程序集中。
- 創建一個新的類庫項目,名字叫做:NStackExample.Data 。
- 添加新項目的引用,將 Core 項目,NHibernate.dll 和 FluentNHibernate.dll 添加進去。
- 為了以后我們能輕松的檢索一些應用程序的設置,將System.Configuration.dll 也添加進去。
- 此外,在我們的 Web 項目中也需要將新建的項目添加到引用當中。
下面讓我們來完成我們的映射文件。
using NStackExample; using FluentNHibernate.Mapping;namespace NStackExample.Data {public class CourseMapping : ClassMap<Course> {public CourseMapping(){Id(x => x.ID).GeneratedBy.GuidComb();Map(x => x.CourseNumber).Not.Nullable().WithLengthOf(4).UniqueKey("CourseNaturalKey");Map(x => x.Subject).Not.Nullable().WithLengthOf(4).UniqueKey("CourseNaturalKey");Map(x => x.Title).Not.Nullable().WithLengthOf(255);Map(x => x.Description).Not.Nullable().WithLengthOf(1024);Map(x => x.Hours).Not.Nullable();HasMany(x => x.Sections).AsSet().WithForeignKeyConstraintName("CourseSections");}} }
上面的代碼非常容易理解,最后得到的就是我們需要的映射文件。
我們的映射類繼承自 ClassMap<Course> ,ClassMap 類是 Fluent NHibernate 搜索查找映射時的具體類型。在這里,這個類提供了 Course 實體類的映射。在構造函數中,我們定義了每個屬性的具體映射。
- 將 Id 設置成持久化對象標示符(POID),基本上這就是數據表的主鍵。對于有多個屬性的主鍵,請參照我們上面 AddressMapping 示例中的 UseCompositeId 。我不建議使用多重主鍵,并且據我所知,Fluent NHibernate 也不支持多重主鍵。
- GeneratedBy 是用來指定 POID 生成策略。在這里我們使用的是 GuidComb 。使用 GUID 做主鍵有非常多的好處,具體的內容大家可以參考 Davy Brion在NHForge博客上發表的隨筆。
- Map 只是將屬性映射到數據庫的列上。如果有需要的話,你可以指定 Not.Nullable 和 WithLengthOf 。
- UniqueKey 指定了列的唯一索引。如果你對多個列指定了相同的名稱,那么這些列都會變為這個唯一索引的一部分。在這個示例中,我們強制要求我們的自然鍵是唯一的。每個 subject 和 course number 都必須是唯一的。
- HasMany 是定義了一個一對多的關系,你可以指定集合的確切行為。在這里有 Set 和 Bag 兩個選項。
- AsSet 不允許重復的項目
- AsBag 允許重復的項目
默認情況下,所有關系都是延遲加載的。就是說當你從數據庫中獲取到了 course ,和其相關聯的 sections 并不是馬上獲取出來,而是直到當你訪問該屬性的時候才會被加載進來,如果你從未訪問過該屬性的話,那么它永遠都不會被加載,這樣可以大大的提高性能。這些功能都是用代理來實現的。
下面容我們來映射 sections:
using NStackExample; using FluentNHibernate.Mapping;namespace NStackExample.Data {public class SectionMapping : ClassMap<Section> {public SectionMapping(){Id(x => x.ID).GeneratedBy.GuidComb();Map(x => x.FacultyName).WithLengthOf(255);Map(x => x.RoomNumber).WithLengthOf(10);Map(x => x.SectionNumber).WithLengthOf(4).Not.Nullable().UniqueKey("SectionNaturalKey");References(x => x.Course).Not.Nullable().UniqueKey("SectionNaturalKey");References(x => x.Term).Not.Nullable().UniqueKey("SectionNaturalKey");HasMany(x => x.StudentSections).AsSet().WithForeignKeyConstraintName("SectionStudentSections");}} }
在這里引入了多對一關系的映射,可以把它看成一對多關系的另一邊。 本示例中就是從孩子 section 到它的父親 course 的關系。
練習:完成所有實體類的映射。
到這里或許你會在想這個系列是不是會很長很長呢?到現在我們甚至連數據庫都還沒有開始建立。不用擔心,這些事 NHibernate 會幫我們做的。
8小時 or 8分鐘?
在我沒有使用 NHibernate 之前,我至少需要一天的時間來建立數據庫。這讓我很郁悶,估計大家也一樣很不喜歡浪費這么多時間去建立數據庫。不過,這樣的事情將在今天結束。
聲明: 如果你嘗試使用現有的舊版數據庫和數據庫架構,沒有什么調整的機會或者很渺茫,Fabio Maulo 的這篇隨筆 將告訴你如何選擇。
首先,讓我們配置 NHibernate 。在 Fluent NHibernate Wiki 上 有一篇非常好的文章 介紹了應該如何配置。
using FluentNHibernate.Cfg; using FluentNHibernate.Cfg.Db; using NHibernate; using NHibernate.Cfg; using NHibernate.Tool.hbm2ddl; using System.IO; using System.Configuration;namespace NStackExample.Data {public class Configuration{private ISessionFactory m_Factory;private string m_SchemaPath;public Configuration Configure(){m_SchemaPath = ConfigurationManager.AppSettings["NStackExample.Data.Configuration.SchemaPath"];m_Factory = Fluently.Configure().Database(MsSqlConfiguration.MsSql2005.ConnectionString(x => x.FromConnectionStringWithKey("NStackExample.Data.Configuration.Db"))).Mappings(x => x.FluentMappings.AddFromAssemblyOf<CourseMapping>().ExportTo(m_SchemaPath)).ExposeConfiguration(BuildSchema).BuildSessionFactory();return this;}private void BuildSchema(NHibernate.Cfg.Configuration cfg){SchemaExport SchemaExporter = new SchemaExport(cfg);SchemaExporter.SetOutputFile(Path.Combine(m_SchemaPath, "schema.sql"));SchemaExporter.Create(true, false);}public ISession OpenSession(){if (m_Factory == null) Configure();return m_Factory.OpenSession();}} }
配置分為兩個部分:數據庫和映射。在本示例中,數據庫使用的是 SQL 2005,連接字符串是從 Web.config 文件中讀取的。 所有的映射部分都是從
fluent 中讀取的,沒有自動映射。注意,我們出口的映射是在 web.config 文件的 appsettings 節中指定的一個目錄,這會將我們 fluent 的映射分別轉換成 hbm.xml文件。這么做是為了方便我們映射部分的調試,尤其是在需要 NHibernate 在線幫助的時候。
這里有個附加項,我們使用 ExposeConfiguration 方法調用 BuildSchema 函數來完成對 NHibernate 的配置。
在 BuildSchema 中,我們使用NHibernate中非常好的一個工具:schema export 。這個工具將會幫助我們創建數據庫。構造函數接受兩個布爾值的參數。第一個參數指定是否輸出 DDL 文件 -- 一個包含所有表、鍵、索引和關系的數據庫腳本。 第二個參數指定了是否將腳本應用到指定的數據庫上。
非常簡單吧。
兩個警告:
- 執行腳本將刪除并創建和你的 Model 相關的每一個表,這對已有的環境可能是極具破壞性的操作。
- 腳本一般不會帶有“use [databasename]”語句,所以你如果不小心執行了,可能會建立到 master 數據庫中,在執行腳本的時候請注意選擇數據庫。
在下篇教程當中,我們將介紹如何對你的映射進行測試,包括讀取、查詢和寫入數據庫。