NHibernate 快速入門

oschina發表於2013-07-14

  這篇教程將透過你的第一個NHibernate專案,講述配置和對映所需要的基本步驟。

  僅關注我們想要的,我們將從一個很簡單的例子開始。為了讓程式碼量最小,我們也去掉了單元測試和介面的程式碼,專一地關注持久化資料的程式碼並闡述一些基本的CRUD(Change, Read, Update, Delete) 技術。

  這篇教程和其他樣例中要用到的工具:

  Microsoft SQL Server 2005 Express Edition - 存放我們的持久化資料。這篇教程在SQL Server 2008環境下也有效。在這篇教程中,我假設你有一個本地SQL Server的例項並且熟練地使用SQL Manger Studio。

  NHibernate version 2.1.2 - 你可以點選下方連結下載最先的 NHibernate:  http://sourceforge.net/projects/nhibernate/files/NHibernate/

  Microsoft Visual Studio 2008 - 我們的開發環境。

  建立專案檔案

  在這個例子裡,因為我們不關注使用者介面 ,所以我們將從建立一個新的控制檯應用程式專案開始

  根據這篇教程的目的,我們把我們的應用程式取名叫'HelloNHibernate'。我也推薦堅持這種命名的傳統,並遵循不同的樣例使用不同的名字,儘管在很多情形下,教程的其他部分也會使用這樣的命名。 

  新增對NHibernate的引用

  我們下一步要新增一系列讓NHibernate發揮作用的必須新增的引用。當你下載並解壓你的NHibernate安裝包後,電腦上就會建立一些目錄,包括 'Required_bins'和'Required_for_LazyLoading。

  首先,你要把 Required Bins目錄下所有的DLL包含進來 - 它們代表NHibernate使用的核心元件。

  其次,我們把Required_for_LazyLoading的一個子目錄下所有的Dll包含進來。選擇哪個目錄的Dll來完成延遲載入,最終將取決於你。 在這篇教程和其他文章中,我使用的是Castle dynamic proxy。

  配置NHibernate

  建立好我們的引用之後,我們現在需要建立和更新我們的App.config (如果是網站,就修改Web.config)。在我們的專案中,我做以下幾個假設:

  • 我們安裝了SQL Server 2005並建立好了資料庫.

  • 資料庫開啟了信任連線

  • 使用Castle proxy factory

  • 把TSQL語句輸出到控制檯(在你學習NHibernate的時候,我強烈推薦你這樣做,因為你可以看到後臺正在做什麼).

  這是一個App.config配置的例子,它包含了這篇教程中所需要的部分。

<?xmlversion="1.0"encoding="utf-8" ?>

<configuration>

    <configSections>
        <sectionname="hibernate-configuration"requirePermission="false"type="NHibernate.Cfg.ConfigurationSectionHandler, NHibernate" />
    </configSections>

    <hibernate-configurationxmlns="urn:nhibernate-configuration-2.2">
        <reflection-optimizeruse="false" />
        <session-factory>
            <propertyname="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
            <propertyname="dialect">NHibernate.Dialect.MsSql2005Dialect</property>
            <propertyname="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
            <propertyname="connection.connection_string">Data Source=(local); Initial Catalog=quickstart; Trusted_Connection=true;</property>
            <propertyname='proxyfactory.factory_class'>NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle</property>
            <propertyname="show_sql">true</property>
        </session-factory>
    </hibernate-configuration>
</configuration>

  在配置中,你將看到我們建立NHibernate來使用SQL Server和SQL 2005的語句。你也能看到連線字串(可以根據需要修改),還有對Castle Proxy Factory的引用,也有一個告訴Nhibernate輸出TSQL語句到控制檯的標誌(show_sql)。 

  建立我們的模型

  NHibernate允許你直接使用Plain Old CLR Objects (POCOs),而不用通過儲存程式來直接和資料庫互動。使用POCOs的一個優勢在於我們不用繫結特定的持久化層。舉個例子,有些ORM解決方案需要特殊屬性,或者是基於你的模型物件,這些物件又是從特定的基類中繼承而來的。

  在NHibernate中,不用特殊的修飾,就可以讓我們的物件和持久化層互動。在NHibernate中,我們唯一要意識到的是所有的我們希望持久化的屬性必須是虛擬的,並且要開啟延遲載入,所有類中的公共方法必須是虛擬的,哪怕它們並沒有包含到我們的對映檔案中。

  通常來講,最好把所有的屬性都設定為虛擬的,這樣你的基類就封裝了,不管你使用延遲載入與否。

  另外,我們可以讓POCO裡的屬性和方法不持久,並且可以讓多個POCOs對映到資料庫中相同的資料表。舉個例子,我們有一個POCO提供了產品的詳情,包括圖片和描述,其他的輕量版本只包括用來建立商品清單的名稱和數量。

  在我們的教程中,我們將建立一個極端的簡單物件。在後期的教程中,我們會講述更加複雜的話題,比如父子關係,高階搜尋技術等等。

  建立在你的專案中建立一個類,命名為‘Produce’,並設定如下的屬性。注意Nhibernate是大小寫敏感的,所以我們的屬性名要準確地匹配後面要建立的Nhibernate對映。

namespace HelloNHibernate
{
    public classProduct
    {
        public virtual int? ProductId { get; set; }
        public virtual string ProductName { get; set; }
    }
}

  注意我們設定ProductId為可為null的值。這允許我們建立一個沒有ID的例項,反過來告訴NHibernate這是一個新物件而不是一個ID為'0'的物件。

  建立NHibernate對映檔案

  Nhibernate使用XML對映檔案來對映POCO到資料庫物件。雖然在很多案例中這可能是一對一關係,但這並不是必要的。你可以輕易地更改欄位名稱並建立不同的對映來給資料庫物件建立可選的表示區域。

  舉個例子,我們有一個複雜的'Product'物件,它包含了大量的文字資訊,一個與訂單和複雜清單資料的容易。這對於我們正在編輯的單個產品的產品詳情區域來說,將是一個很好的對映,它將變得十分臃腫,以至於在產品的下拉選單中,它不能作為一個強型別列表的成員。在後面,要建立一個更簡單一點到只有產品ID和產品名稱的持久化區域的對映。

  在我們的教程中,我們使用一個簡單的直接關係到我們上面建立的POCO的對映。來建立這樣的一個對映,我們要新增一個新的XML檔案到上面展示的工程中。當建立XML檔案時,要讓它以.hbm.xml命名方式結尾-這種傳統告訴NHibernate這是一個對映檔案。我們的名稱叫做'Product.hbm.xml'。

  你的XML檔案應該包含以下資訊。下面我們將一一講述每一個標籤:

<?xmlversion="1.0"encoding="utf-8" ?>
<hibernate-mappingxmlns="urn:nhibernate-mapping-2.2"
                           assembly="HelloNHibernate"
                           namespace="HelloNHibernate">

    <class name="Product" table="Products">
        <id name="ProductId">
            <generatorclass="identity"/>
        </id>
        <property name="ProductName"length="50" />
    </class>
</hibernate-mapping>
  • 在hibernate-maping標籤中,我們同時引用類集(POCOs)所屬的程式集合名稱空間。當你的對映檔案在單獨的名稱空間或者程式集而不是模型中,這種方式是十分方便的。

  • class元素表示到單個POCO的對映。name表示上面的程式集和名稱空間中的類名,table屬性告訴NHibernate資料庫中的哪個表或者檢視將被對映。

  • id元素告訴NHibernate哪個資料庫的欄位和對應的物件作為一個唯一鍵來使用。在本例子中,我們使用ProductId這個欄位。

  • generator元素告訴NHibernate怎樣給新實體來建立唯一ID。因為我們使用SQL Server屬性中的ID屬性,所以我們指明generator類為'identity'。

  • property標籤是你見得最多的標籤。它簡單地對映一個到資料表或者檢視中對應欄位的對映。我已經把這個例子中最長的屬性包含進來了,因為我們想為對映建立一個架構。對於我們所需要的產品名稱,預設的255個字元已經足夠大了。

  一旦XML檔案建立好了,你需要確保它被設定為嵌入式資源,否則NHibernate不會讀出你的XML檔案,那麼對映就不會生效了。完成這一步,你要在解決方案資源瀏覽器中點選你新建的XML檔案,更改XML的生成方式。

 

  到這一步,你的解決方案應該和下圖類似:

 

  使用 NHibernate連線資料庫

  到了這一步,我們已經完成了安裝和配置,現在就準備使Nhibernate和我們的資料庫來互動。

  透過這些例子,我推薦你單獨跟著做,並觀察物件是如何發生改變的,資料又是如何持久化到資料庫的。在你觀察持久化物件改變的時候,這有助於你更好地理解NHibernate是如何工作的,有助於你鞏固下面的樣例。

  下面到了程式碼了!在第一個樣例中,我們只關注使用NHibernate來完成持久化資料所需要的最小量的程式碼,而不關注測試和其他更好的實踐,注意到這些事很重要的。當我們引申到更復雜的樣例中,我們要花費更多的時間為你的實際程式來建立執行更好的,更加健壯的架構。

  在NHibernate中,我們的根物件將是一個實現ISessionFactory介面的物件。一個SessionFactory是一個很昂貴的物件,在你的程式中,你只有在需要一個就夠了。

  要建立我們的SessionFactory,我們將使用NHibernate Configuration物件的一個方法來建立一個基於App.config配置中的會話工廠,並建立不同對映檔案。

  讓我們來看看一些程式碼:

using NHibernate;
using NHibernate.Cfg;
using NHibernate.Tool.hbm2ddl;

namespace HelloNHibernate
{
    class Program
    {
        static void Main(string[] args)
        {

            Configuration config= new Configuration();
            config.AddAssembly(typeof(Product).Assembly);
            ISessionFactory sessionFactory = config.BuildSessionFactory();


            var schema = new SchemaExport(config);
            schema.Create(true, true);
        }
    }
}

  一一來講述前5行程式碼.

  第一行程式碼基於app.config檔案中的值建立了一個NHibernate COnfiguration物件的例項,這個物件在名稱空間NHibernate.cfg中。

Configuration config= new Configuration();

  接下來,我們要載入對映檔案,它們用來把POCOs對映到持久化資料庫物件。載入對映檔案有很多方法,但是所有的方法都需要傳遞一個程式集作為引數。NHibernate用這些程式集來查詢所有的持久化對映資料。

  在第一個樣例中,我們僅僅使用Product物件,所以我們可以用下面這行程式碼。(另外,我們也可以用Assembly.GetExecutingAssembly(),因為Product hbm 檔案和類物件都在主程式集中。)

config.AddAssembly(typeof(Product).Assembly);

  然後我們使用Configuration物件的BuildSessionFactory()方法建立會話工廠。這個函式返回一個實現我們要使用的ISeesionFactory的物件。

ISessionFactorysessionFactory = config.BuildSessionFactory(); 

  下面兩行程式碼用來重構我們的架構。這些命令可以用來引導一個新應用程式的安裝,清除集合測試期間的資料庫,或者輸出DDL到一個檔案用來給DBA提供一個指令碼。

  我們用這種方法來重置樣例程式執行中的資料庫,有了這樣的程式碼,你不用擔心資料庫的清理工作。

var schema = newSchemaExport(config);
schema.Create(true, true);

  (你應該看看控制檯下的SQL語句。)

if exists (select * from dbo.sysobjects where id = object_id(N'Products')
and OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table Products
create table Products (
    ProductId INT IDENTITY NOT NULL,
   ProductName NVARCHAR(50) null,
   primary key (ProductId)
)
)

  使用NHibernate實現CRUD基本操作

  現在我們可以通過一個有效的SessionFactory物件來建立我們的資料儲存,是時候儲存一些資料了。

  第一個例子,我們要完成product表中基本的CRUD操作。我們將從下面的程式碼片段開始(你可以在schema.Create之後立即新增這些程式碼),然後分別詳細地講述每個操作。

ProductpNew = newProduct() {ProductName = "Canned Salmon"};

using (var session = sessionFactory.OpenSession())
{
    //Create a Product
    using (var trans = session.BeginTransaction())
    {
        session.Save(pNew);
        trans.Commit();
    }

    //Retrieve a product by ID
    ProductpGet = session.Get<Product>(pNew.ProductId);

    //Update a Product
    pGet.ProductName = "Canned Tuna";

    using (var trans = session.BeginTransaction())
    {
        session.Update(pGet);
        trans.Commit();
    }

    //Delete a Product
    using (var trans = session.BeginTransaction())
    {
        session.Delete(pNew);
        trans.Commit();
    }
}

  從建立product ( Canned Salmon)開始。我們讓ProductId置空-這是因為這是一個ID欄位,我們還沒有持久化這個物件。就像你所看到的,NHibernate不僅處理資料庫的持久化,還為我們設定這個屬性到新建立的ID值。(這是一個我們以往要用從儲存程式中返回的@@Identity變數來完成的一個任務。)

  接下來,從SessionFactory中開啟一個Session.所有的NHibernate持久化操作將會在Session的上下文中處理。

using (var session = sessionFactory.OpenSession())

  完成會話建立後,我們可以來談談業務。在檢索資料的情況下,使用Session物件是不錯的選擇。但是在我們需要在資料庫中更改資料的情形下,我們要使用從當前Session物件建立的Transaction物件(當我們建立,更新,刪除product物件的時候,你會看到這一點。)

  建立一個持久化物件

  Wh不管我們有一個還是多個屬性,建立新的持久化物件遵循相同的模式,並能用幾行程式碼就能完成。

  • 為你要更新的資料建立一個轉化
  • 在當前會話中執行Save方法,把你想要持久化的物件作為引數來傳遞
  • 提交轉化
//Create a Product
using (var trans = session.BeginTransaction())
{
    session.Save(pNew);
    trans.Commit();
}

  (你應該在控制檯下看看下面的程式碼)

  NHibernate: INSERT INTO Products (ProductName) VALUES (@p0); select SCOPE_IDENTITY();@p0 = 'Canned Salmon'

  如果你看看pNew的屬性,你會看到Nhibernate已經更新基於持久化資料庫實體的物件了(本例子中,將ProductId設為'1')。

  通過ID檢索持久化物件

  從資料庫中檢索物件時,NHibernate提供了一種簡單的方法,如果知道了檢索的實體的ID號。在這裡,建立一個新物件(pGet)基於剛新增到資料庫(pNew)中的物件的ID。

//Retrieve a product by ID
ProductpGet = session.Get<Product>(pNew.ProductId);

  有一件重要的事情要注意,在會話中,pGet和pNew指向相同的持久化後的實體,所以改變任何一個物件(pGet或者pNew)都會改變物件中的反射。

  你同樣要注意,控制檯中的輸出中沒有'Select'語句。這是因為我們能夠查詢在會話中已經載入的物件(pNew)來連線pGet。

  另一方面,如果我們想在不同的會話上下文中查詢pGet,有兩種情況是不同的。

  第一,pNew和pGet不再引用記憶體中的相同物件(儘管它們還是繫結到資料庫中相同的持久化物件)。第二,你將會在控制檯中看到Select語句。因為我們將會建立一個新會話,這個會話正是NHibernate用資料庫中值為'1'的ID檢索實體時所需要的。

NHibernate: SELECT product0_.ProductId as ProductId0_0_, product0_.ProductName as ProductN2_0_0_ FROM Products product0_ WHERE product0_.ProductId=@p0;@p0 = 1

  更新一個持久化物件

  同建立持久化物件一樣,我們更新物件是在轉化上下文中的一個物件之前執行一個方法,如下所示:

//Update a Product
pGet.ProductName = "Canned Tuna";
using (var trans = session.BeginTransaction())
{
    session.Update(pGet);
    trans.Commit();
}

  (你在控制檯會看到以下程式碼)

NHibernate: UPDATE Products SET ProductName = @p0 WHERE ProductId = @p1;@p0 = 'Canned Tuna', @p1 = 1

  會話物件同樣有一個非常方便的'SaveOrUpdate'方法,它會自動地執行恰當的語句。比如,如果POCO的ID為空,會執行插入操作。如果不為空,會執行更新操作。

  刪除一個持久化物件

  Fo最後一個例子,我們將刪除一個product。同樣,因為這會影響到我們的持久化實體,我們將在轉化的作用域中執行刪除操作,並傳遞我們希望刪除的POCO最為引數。

//Delete a Product
using (var trans = session.BeginTransaction())
{
    session.Delete(pNew);
    trans.Commit();
}

  (你在控制檯中會看到以下程式碼)

NHibernate: DELETE FROM Products WHERE ProductId = @p0;@p0 = 1

  總結

  雖然這是一個非常簡單的例子,以下幾點要仔細思考。

  • 我們沒有建立任何的DDL。大多數評價ORMs的開發者在尋找一個小程式執行方案時,寫了海量的TSQL語句。我們並沒有徹底地更改我們的POCOs-它們沒有屬性,介面或者基類。我們唯一的操作就是把屬性的值改為Virtual。有了這樣的變化,在沒有Nhibernate的情況下,我們可以輕易地使用相同的物件,這就提高了柔韌性和易測性。

  • 我們已經完成了要建立的TSQL的可視性。儘管某些資料庫(比如微軟的SQL Server)優化儲存程式的的能力還存在爭議,但在資料庫服務中,效能優勢最小量地分配到當前的處理能力。

  • 另一個有爭議的是,好的DBA可能會建立更加優化的程式碼,比NHibernate建立的程式碼還要好。
     
    雖然謹慎的手動查詢比建立的程式碼更好,但是大多數時候NHibernate建立的程式碼遠比很多開發者寫的程式建立的程式碼好得多。
     
    舉個例子,一個一般的更新程式可能會帶來一百個引數,這可能會在資料庫記錄中潛在地發生改變。NHibernate,另一方面,將會智慧地根據動態更新語句來改變實際變化的列,而表現得更好。

  在第一個教程中,我們闡述了使用NHibernate連線一個持久化資料資源的資本步驟,和如何實現簡單的CRUD操作。在下一系列的教程中,我們將會關注更加複雜的情況,並闡述更好的實踐來幫助你實現更好的可測試性和維持性的資料訪問層。

  原文地址:https://docs.google.com/document/d/1OTC16N1GnKvgkIDvQn_RkQS_y3vvjYav-Ons9JLEUCU/edit?pli=1