NHibernate 快速指南



NHibernate - 概述

在本節中,我們將討論 NHibernate 是什麼,它可以在哪些平臺上實現,它的優勢是什麼以及其他相關方面。

什麼是 NHibernate?

NHibernate 是一個成熟的、開源的 .NET 框架物件關係對映器。它處於積極開發階段,功能齊全,並被成千上萬個成功的專案使用。它構建在 **ADO.NET** 之上,當前版本為 **NHibernate 4.0.4**。

  • NHibernate 是一個開源的 .NET 物件關係對映器,並在 **GNU Lesser General Public License** 下分發。

  • 它基於 Hibernate,Hibernate 是一個流行的 Java 物件關係對映器,它擁有一個非常成熟和活躍的程式碼庫。

  • 它提供了一個框架,用於將面向物件的領域模型對映到傳統的關聯資料庫。

  • NHibernate 由 **Tom Barrett** 發起,該專案自 2003 年 2 月以來一直存在,當時是他們的第一次提交。

  • 這是一個大型專案,提供了許多功能。

  • 有一個 **NuGet 包** 可用,這使得將其新增到專案中變得非常容易。

為什麼要使用 NHibernate?

現在問題是,我們為什麼要需要 **物件關係對映器?** 這是因為物件世界和關係世界之間存在脫節。

  • 在物件世界中,一切都是圍繞 **物件** 構建的;我們稱之為物件,那些包含我們資料的事物。

  • 關係世界是基於集合的,我們處理的是表和行,這與物件世界不同。

  • 在物件世界中,我們有 **單向關聯**。如果一個客戶有一個指向訂單的指標,它並不一定意味著訂單有一個指向客戶的指標,它可能存在也可能不存在。

  • 在關係世界中,所有關聯都是 **雙向** 的,可以透過外部索引鍵實現。

  • 所有關聯本質上都是雙向的,因此當我們處理物件關係對映時,我們也需要處理這種脫節。

  • 在物件世界中,我們使用的是單向指標,而在關係世界中,我們使用的是本質上是雙向的外部索引鍵。

  • 物件世界具有繼承的概念,其中車輛可以有多個不同的子類,因此汽車是一種車輛,船是一種車輛,跑車是一種汽車,這些型別的繼承關係。

  • 關係世界沒有這種繼承的概念。

對映

那麼我們如何對映所有這些 **不相關的關係?** 這個對映的概念來自物件關係對映器。主要有三個方面需要理解,如下面的圖所示。

Mapping
  • 在您的應用程式中,您將需要類定義,這通常是 C# 程式碼,以及表示我們的類的 .NET 程式碼,例如 Employee 類、Customer 類、Order 類等。

  • 在底部,您可以看到一個數據庫模式,它是在關係資料庫中定義 **資料定義語言**,用於指定客戶表的外觀,員工表的外觀。

  • 在這兩者之間,我們有對映元資料,它告訴物件關係對映器如何從 C# 中的物件世界轉換為資料庫世界中的行和列以及外部索引鍵關係。

  • 此對映元資料可以透過多種不同的方式表示,我們將檢視 NHibernate 應用程式中最典型的一些不同方式。

  • 它由 **HBM(Hibernate 對映)** 檔案表示,它們是 XML 檔案。

支援的資料庫

NHibernate 支援各種不同的資料庫。任何現有的關係資料庫都可以被 NHibernate 訪問。

  • SQL Server 是主要支援的資料庫,大多數開發人員在開發過程中都在使用它,它可能是最常見的資料庫。

  • 它也 **與 Oracle 相容良好**。

  • 它還支援 DB2、Firebird、MySQL、PostgreSQL、SQL Lite

  • 它還具有 **ODBC 和 OLEDB 驅動程式**。

NHibernate - 架構

如今,許多系統都是使用分層架構設計的,NHibernate 也擁有它,並且可以與該設計完美配合。

分層架構

分層架構將系統劃分為多個組,其中每個組包含解決特定問題領域的程式碼,這些組稱為層。大多數企業級應用程式使用由三層組成的 **高階應用程式架構** -

  • 表示層
  • 業務層
  • 持久層
Layered Architecture

例如,使用者介面層,也稱為表示層,可能包含構建網頁和處理使用者輸入的所有應用程式程式碼。

分層方法的一個主要好處是,您通常可以更改一層而不會對其他層造成任何重大幹擾,從而使系統 **更不容易出錯且更易於維護**。

表示層

  • 它是最頂層,包含負責繪製使用者介面、頁面、對話方塊或螢幕以及收集使用者輸入和控制導航的程式碼。

業務層

  • 業務層負責實現使用者在問題域中理解的任何業務規則或系統需求。

  • 它還重用持久層定義的模型。

持久層

  • 持久層由負責儲存和檢索應用程式資料的類和元件組成。

  • 此層還定義了模型類和資料庫之間的對映。NHibernate 主要用於此層。

資料庫

  • 資料庫存在於 .NET 應用程式之外。
  • 它是系統狀態的實際持久表示。
  • 如果使用 SQL 資料庫,則資料庫將包含關係模式和可能儲存的過程。

幫助程式/實用程式類

  • 每個應用程式都有一組幫助程式或實用程式類來支援其他層:例如,UI 小部件、訊息傳遞類、異常類和日誌記錄實用程式。

  • 這些元素不被視為層,因為它們不遵循分層體系結構中的層間依賴規則。

NHibernate 架構

  • 這是 NHibernate 應用程式的高階檢視,您還可以看到簡單的 NHibernate 架構。

.Net Environment
  • 應用程式程式碼使用 NHibernate 的 **ISession** 和 **IQuery** API 進行持久化操作,並且只需要管理資料庫事務,理想情況下使用 NHibernate 的 **ITransaction** API。

NHibernate - ORM

在我們真正開始使用 NHibernate 之前,我們需要了解它構建的基礎。NHibernate 是一種持久化技術,它基於物件關係對映或 ORM 的思想。

什麼是 ORM?

物件關係對映 (ORM) 是一種 **程式設計技術**,用於在面向物件程式語言中轉換不相容型別系統之間的資料。換句話說,它是將應用程式的業務物件對映到關係資料庫表的概念,以便可以透過應用程式的物件模型輕鬆地完全訪問和更新資料。

  • 正如您已經知道的那樣,關係資料庫提供了一種儲存資料的良好方法,而面向物件程式設計是構建複雜應用程式的一種良好方法。

  • NHibernate 和 ORM 通常與具有重要業務邏輯、領域模型和某種資料庫的應用程式最相關。

  • 使用 ORM,建立轉換層非常容易,該層可以輕鬆地將物件轉換為關係資料,然後再轉換回來。

  • ORM 首字母縮寫詞還可以表示物件角色建模,這個術語是在物件/關係對映變得相關之前發明的。

  • 它描述了一種資訊分析方法,用於資料庫建模。

為什麼要使用 ORM?

ORM 是一個 **框架**,使您能夠將面嚮物件語言中找到的物件世界對映到關係資料庫中找到的關係表中的行。

要理解這個概念,讓我們看一下下面的圖。

Employee
  • 在上圖中,您可以看到我們有一個名為 Employee 的表在右側,其中包含列,每個列都與單個員工關聯的資料。

  • 我們有一列用於 Id,用於唯一標識每個員工。

  • 一列用於員工姓名,另一列用於其入職日期,最後是一列用於員工年齡。

  • 如果我們想編寫一些程式碼來將新員工儲存到表中,這並不容易。

  • 在上圖中,您還可以看到我們有一個 Employee 物件,它具有 Id、name、joining date 和 age 的欄位。

  • 如果沒有 ORM,我們必須將此物件轉換為幾個不同的 SQL 語句,這些語句將員工資料插入到 Employee 表中。

  • 因此,編寫程式碼來建立 SQL 以執行上述方案並不難,但有點乏味且很容易出錯。

  • 使用像 NHibernate 這樣的 ORM,我們可以宣告某些類應該如何對映到關係表,並讓 ORM 或 NHibernate 處理建立 SQL 的繁瑣工作,以便在我們的 Employee 表中插入、更新、刪除和查詢資料。

  • 這使我們能夠將程式碼集中在使用物件上,並使這些物件自動轉換為關係表。

  • 因此,ORM 真正做的是它使我們免於手動將物件對映到表。

NHibernate - 環境搭建

要開始使用 NHibernate,我們將需要 Visual Studio 和 NHibernate 包。

Visual Studio 安裝

Microsoft 提供了 Visual Studio 的 **免費版本**,其中還包含 **SQL Server**,可以從 https://www.visualstudio.com 下載。以下是安裝步驟。

**步驟 1** - 下載完成後,執行安裝程式,然後將顯示以下對話方塊。

Visual Studio

**步驟 2** - 單擊“安裝”按鈕,它將啟動安裝過程。

Installation Process

**步驟 3** - 安裝過程成功完成後,您將看到以下對話方塊。

Installation Process Completed

**步驟 4** - 關閉此對話方塊,並在需要時重新啟動計算機。

**步驟 5** - 現在從“開始”選單開啟 Visual Studio,它將開啟以下對話方塊。第一次準備需要一些時間。

Visual Studio First Use

步驟 6 − 完成所有這些操作後,您將看到 Visual Studio 的主視窗。

Window of Visual Studio

NHibernate 包安裝

NHibernate 是一個成熟的、開源的,用於 .NET 框架的物件關係對映器。它處於積極開發階段,功能齊全,並已用於數千個成功的專案。您可以使用以下方法安裝 NHibernate 包。

直接下載

  • https://sourceforge.net/ 下載包含所有必需二進位制檔案的 zip 檔案。

  • 解壓縮此 zip 檔案並將所有這些二進位制檔案包含到您的專案中。

使用 NuGet 安裝

  • 安裝 NHibernate 的另一種方法是使用 NuGet 安裝 NHibernate 包,這迄今為止是將 NHibernate 整合到專案中最簡單的方法。

  • 它將下載所有 NHibernate 依賴項並建立對所有必需程式集的引用。

  • 要安裝 NHibernate,請在程式包管理器控制檯中執行以下命令。

install-package NHibernate

Package Manager Console

您現在可以開始您的應用程式了。

NHibernate - 入門

在本章中,我們將瞭解如何使用 NHibernate 啟動一個簡單的示例。我們將構建一個簡單的控制檯應用程式。要建立控制檯應用程式,我們將使用 Visual Studio 2015,其中包含建立、使用 NHibernate 包測試應用程式所需的所有功能。

以下是使用 Visual Studio 中提供的專案模板建立專案的步驟。

步驟 1 − 開啟 Visual Studio 並單擊“檔案”→“新建”→“專案”選單選項。

步驟 2 − 將開啟一個“新建專案”對話方塊。

Project Dialog

步驟 3 − 從左側窗格中,選擇“模板”→“Visual C#”→“Windows”。

步驟 4 − 在中間窗格中,選擇“控制檯應用程式”。

步驟 5 − 在“名稱”欄位中輸入專案名稱“NHibernateDemoApp”,然後單擊“確定”繼續。

步驟 6 − Visual Studio 建立專案後,您將看到解決方案資源管理器視窗中顯示了許多檔案。

Project Dialog

如您所知,我們建立了一個簡單的控制檯應用程式專案,現在我們需要將 NHibernate 包包含到我們的控制檯專案中。

轉到“工具”選單,然後選擇“NuGet 包管理器”→“程式包管理器控制檯”,這將開啟“程式包管理器控制檯”視窗。

Manager Console Window

指定上面程式包管理器控制檯視窗中顯示的命令並按 Enter 鍵,它將下載所有 NHibernate 依賴項並建立對所有必需程式集的引用。安裝完成後,您將看到如下所示的訊息。

NHibernate Demo App

現在我們已經添加了 NHibernate,我們可以開始實現。因此,我們將從對映一個名為Student的非常簡單的開始,它只有一個名為 ID 的整數主鍵以及一個 FirstName 和 LastName 列。

Add New Item

我們需要一個類來表示這個學生,因此讓我們建立一個名為 Student 的新類,方法是右鍵單擊解決方案資源管理器中的專案,然後選擇“新增”→“類”,這將開啟“新增新項”對話方塊。

Add New Item NHibernate

在“名稱”欄位中輸入Student.cs,單擊“新增”按鈕。在這個 Student 類中,我們需要擁有名為 ID 的整數主鍵,並且我們需要建立這個字串、FirstNameLastName欄位,如下所示的 Student 類的完整實現。

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks;

namespace NHibernateDemoApp { 
   
   class Student { 
      public virtual int ID { get; set; } 
      public virtual string LastName { get; set; } 
      public virtual string FirstMidName { get; set; } 
   } 
}

在處理 NHibernate 應用程式中的模型時,最簡單的方法是將所有欄位設為虛擬的。因此,這是我們將使用的簡單的 NHibernate 模型,並將將其對映到後端資料庫。

現在讓我們轉到 Program 類中的 Main 方法並建立一個新的 NHibernate 配置物件。

我們需要提供的第一個內容是連線字串。這是一個特定於資料庫的連線字串,找到連線字串的最簡單方法是右鍵單擊SQL Server 物件資源管理器中的資料庫,然後選擇“屬性”。

SQL Server Object Explorer

它將開啟“屬性”視窗,現在向下滾動,您將在“屬性”視窗中看到“連線字串”欄位。

Properties Window

複製連線字串並在程式碼中指定。以下是 Main 方法的實現,我們需要在其中進行 NHibernate 配置。

using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;

using System;
using System.Linq;
using System.Reflection;

namespace NHibernateDemoApp {

   class Program {
   
      static void Main(string[] args) {
         var cfg = new Configuration();
			
         String Data Source = asia13797\\sqlexpress;
         String Initial Catalog = NHibernateDemoDB;
         String Integrated Security = True;
         String Connect Timeout = 15;
         String Encrypt = False;
         String TrustServerCertificate = False;
         String ApplicationIntent = ReadWrite;
         String MultiSubnetFailover = False;
			
         cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source + 
            Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
            TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";
         
                     
            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect>();
         });
         
         cfg.AddAssembly(Assembly.GetExecutingAssembly());
         
         var sefact = cfg.BuildSessionFactory(); 
         
         using (var session = sefact.OpenSession()) {
         
            using (var tx = session.BeginTransaction()) {
               //perform database logic 
               tx.Commit();
            }
            
            Console.ReadLine(); 
         } 
      } 
   } 
}

在連線字串之後,我們需要提供一個驅動程式,即SQLClientDriver,然後我們還需要為它提供一個方言,即 SQL Server 的版本,我們將使用 MS SQL 2008。

NHibernate 現在知道如何連線到資料庫。我們需要做的另一件事是為它提供一個我們將對映的模型列表。

我們可以透過新增一個程式集來做到這一點,因此透過指定Assembly.GetExecutingAssembly,程式將在其中找到對映檔案。對映檔案告訴 NHibernate 如何從 C# 類轉換為資料庫表。

SessionFactory 編譯初始化 NHibernate 所需的所有元資料。SessionFactory 可用於構建會話,會話大致類似於資料庫連線。因此,適當的方法是在 using 塊中使用它。我可以說var session 等於sessionFactory.OpenSession,並且我想在它的事務中執行此操作。

開啟會話後,我們可以告訴會話開始一個新事務,然後我們可以在此處執行一些邏輯。因此,執行一些資料庫邏輯,最後提交該事務。

NHibernate - 基本 ORM

在本章中,我們將介紹一些基本對映,並且您知道從上一章我們擁有資料庫表以及 C# 類定義。現在我們需要一個對映來解釋如何從 C# 轉換為資料庫,然後再轉換回來。

因此,讓我們繼續透過右鍵單擊解決方案資源管理器中的專案並選擇“新增”→“新建項...”來新增一個新的 XML 檔案。

New XML File

在“名稱”欄位中輸入Student.hbm.xml。我們需要指定一個預設程式集,它將是NHibernateDemoApp,還需要指定一個預設名稱空間。這只是簡化了我們將在該檔案中進行的大量其他型別定義。

以下是 XML 檔案中的實現 -

<?xml version = "1.0" encoding = "utf-8" ?> 

<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" 
   assembly = "NHibernateDemoApp" namespace = "NHibernateDemoApp">

   <class name = "Student"> 
      <id name = "ID">
        <generator class = "native"/> 
      </id> 
		
      <property name = "LastName"/> 
      <property name = "FirstMidName"/> 
   </class> 
   
</hibernate-mapping>

接下來我們需要定義一個類;這個類將是我們的Student 類。接下來,我們需要告訴 NHibernate id 的名稱,即 ID,我還必須告訴 NHibernate 如何生成 ID,因此我們的生成器將是 native 型別。

native 型別生成器意味著在像 SQL Server 這樣的資料庫中,它將使用 identity 列,即 identity 型別。

接下來我們要做的事情是給出屬性的名稱。因此,為 FirstName 和 LastName 新增另外兩個屬性。

現在,我們正在從程式集中讀取這些對映檔案。因此,首選的方法是將這些HBM 檔案烘焙到您的程式集中。我們可以透過簡單地設定一個屬性來做到這一點。

現在右鍵單擊解決方案資源管理器中的專案,然後選擇“屬性”,您將看到“生成操作”欄位,其中預設情況下選擇了“內容”。

Build Action Field

從下拉列表中選擇“嵌入的資源”。

Embedded Resource

因此,這實際上將該 XML 檔案嵌入到NHibernateDemoApp程式集中。

NHibernate - 基本 CRUD 操作

在本章中,我們將介紹基本的CRUD 操作。現在我們的系統已準備好啟動,因為我們已成功實現了我們的域 Student 類,我們還定義了對映檔案並配置了 NHibernate。我們現在可以使用一些查詢來執行 CRUD 操作。

建立資料

如您所見,我們的NHibernateDemoDB資料庫中的 Student 表中沒有資料。

NHibernate DemoDB

因此,要新增一些資料,我們需要執行如下所示的新增/建立操作。

using (var session = sefact.OpenSession()) { 

   using (var tx = session.BeginTransaction()) { 
     
      var student1 = new Student { 
         ID = 1, 
         FirstMidName = "Allan", 
         LastName = "Bommer" 
      }; 
      
      var student2 = new Student { 
         ID = 2, 
         FirstMidName = "Jerry", 
         LastName = "Lewis" 
      }; 
      
      session.Save(student1); 
      session.Save(student2); 
      tx.Commit(); 
   } 
   
   Console.ReadLine(); 
}

如您所見,我們建立了兩個學生,然後呼叫OpenSession的 Save() 方法,然後呼叫BeginTransaction的 Commit()。以下是Program.cs檔案中的完整實現

using NHibernate.Cfg; 
using NHibernate.Dialect; 
using NHibernate.Driver; 

using System; 
using System.Linq; 
using System.Reflection;

namespace NHibernateDemoApp { 
   
   class Program { 
      
      static void Main(string[] args) { 
         var cfg = new Configuration();
			
         String Data Source = asia13797\\sqlexpress;
         String Initial Catalog = NHibernateDemoDB;
         String Integrated Security = True;
         String Connect Timeout = 15;
         String Encrypt = False;
         String TrustServerCertificate = False;
         String ApplicationIntent = ReadWrite;
         String MultiSubnetFailover = False;
			
         cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source + 
            Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
            TrustServerCertificate + ApplicationIntent + MultiSubnetFailover"; 

            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect>(); 
         }); 
   
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         var sefact = cfg.BuildSessionFactory(); 
			
         using (var session = sefact.OpenSession()) { 
			
            using (var tx = session.BeginTransaction()) { 
               
               var student1 = new Student { 
                  ID = 1,  
                  FirstMidName = "Allan", 
                  LastName = "Bommer" 
               }; 

               var student2 = new Student { 
                  ID = 2, 
                  FirstMidName = "Jerry", 
                  LastName = "Lewis" 
               }; 
            
               session.Save(student1); 
               session.Save(student2); 
               tx.Commit();
            } 
            
            Console.ReadLine(); 
         } 
      } 
   } 
}

現在讓我們執行此應用程式,然後轉到 SQL Server 物件資源管理器並重新整理您的資料庫。您將看到上述兩個學生現在已新增到 NHibernateDemoDB 資料庫的 Student 表中。

SQL Server Object

從 Student 表讀取資料

您可以看到,現在我們的學生表中有兩條記錄。要從表中讀取這些記錄,我們需要呼叫 OpenSession 的CreateCriteria(),如下面的程式碼所示。

using (var session = sefact.OpenSession()) { 
   
   using (var tx = session.BeginTransaction()) { 
      var students = session.CreateCriteria<Student>().List<Student>(); 
      
      foreach (var student in students) { 
         Console.WriteLine("{0} \t{1} \t{2}", 
            student.ID,student.FirstMidName, student.LastName); 
      } 
      
      tx.Commit(); 
   } 
   
   Console.ReadLine(); 
}

因此,如果您想要記錄列表,我們可以簡單地說 Student 型別的列表。

現在使用foreach遍歷所有學生,並在控制檯上列印 ID、FirstMidNameLastName。現在,讓我們再次執行此應用程式,您將在控制檯視窗中看到以下輸出。

1 Allan Bommer
2 Jerry Lewis

您還可以透過在使用以下程式碼的 OpenSession 的Get()方法中指定 ID 來檢索任何記錄。

using (var session = sefact.OpenSession()) { 
   
   using (var tx = session.BeginTransaction()) { 
      var students = session.CreateCriteria<Student>().List<Student>(); 
      
      foreach (var student in students) { 
         Console.WriteLine("{0} \t{1} \t{2}", student.ID, 
            student.FirstMidName, student.LastName); 
      }
      
      var stdnt = session.Get<Student>(1); 
      Console.WriteLine("Retrieved by ID"); 
      Console.WriteLine("{0} \t{1} \t{2}", stdnt.ID, 
         stdnt.FirstMidName, stdnt.LastName); 
      tx.Commit();
   } 
	
   Console.ReadLine(); 
}

現在,當您執行應用程式時,您將看到以下輸出。

1 Allan Bommer
2 Jerry Lewis
Retrieved by ID
1 Allan Bommer

更新記錄

要更新表中的記錄,我們需要首先獲取該特定記錄,然後透過呼叫 OpenSession 的Update()方法來更新該記錄,如下面的程式碼所示。

using (var session = sefact.OpenSession()) { 

   using (var tx = session.BeginTransaction()) { 
      var students = session.CreateCriteria<Student>().List<Student>(); 
     
      foreach (var student in students) { 
         Console.WriteLine("{0} \t{1} \t{2}", student.ID, 
            student.FirstMidName, student.LastName); 
      }
      
      var stdnt = session.Get<Student>(1); 
      Console.WriteLine("Retrieved by ID"); 
      Console.WriteLine("{0} \t{1} \t{2}", stdnt.ID, stdnt.FirstMidName, stdnt.LastName);
      
      Console.WriteLine("Update the last name of ID = {0}", stdnt.ID); 
      stdnt.LastName = "Donald"; 
      session.Update(stdnt); 
      Console.WriteLine("\nFetch the complete list again\n"); 
      
      foreach (var student in students) { 
         Console.WriteLine("{0} \t{1} \t{2}", student.ID, 
            student.FirstMidName, student.LastName); 
      } 
      
      tx.Commit();
   } 
   
   Console.ReadLine();
}

現在,當您執行應用程式時,您將看到以下輸出。

1 Allan Bommer
2 Jerry Lewis
Retrieved by ID
1 Allan Bommer
Update the last name of ID = 1
Fetch the complete list again
1 Allan Donald
2 Jerry Lewis

如您所見,ID 等於 1 的 LastName 從 Bommer 更新為 Donald。

刪除記錄

要從表中刪除任何記錄,我們需要首先獲取該特定記錄,然後透過呼叫 OpenSession 的Delete()方法來刪除該記錄,如下面的程式碼所示。

using (var session = sefact.OpenSession()) { 
   
   using (var tx = session.BeginTransaction()) { 
      var students = session.CreateCriteria<Student>().List<Student>();
      
      foreach (var student in students) { 
         Console.WriteLine("{0} \t{1} \t{2}", student.ID, 
            student.FirstMidName, student.LastName); 
      }
      
      var stdnt = session.Get<Student>(1); 
      Console.WriteLine("Retrieved by ID"); 
      Console.WriteLine("{0} \t{1} \t{2}", stdnt.ID, stdnt.FirstMidName, stdnt.LastName);
      
      Console.WriteLine("Delete the record which has ID = {0}", stdnt.ID); 
      session.Delete(stdnt);
      Console.WriteLine("\nFetch the complete list again\n"); 
      
      foreach (var student in students) { 
         Console.WriteLine("{0} \t{1} \t{2}", student.ID, student.FirstMidName, 
            student.LastName); 
      } 
      
      tx.Commit();
   } 
	
   Console.ReadLine(); 
}

現在,當您執行應用程式時,您將看到以下輸出。

1 Allan Donald
2 Jerry Lewis
Retrieved by ID
1 Allan Bommer
Delete the record which has ID = 1
Fetch the complete list again
2 Jerry Lewis

如您所見,ID 等於 1 的記錄不再存在於資料庫中。您也可以在 SQL Server 物件資源管理器中檢視資料庫。

Object Explorer

NHibernate - Profiler

在本章中,我們將瞭解如何檢索、更新、建立和刪除資料庫中的所有記錄,以及這些查詢是如何執行的?

要了解所有這些,我們可以簡單地在我們的配置中新增一個選項,該選項將 SQL 記錄到控制檯中。以下是在控制檯中記錄 SQL 查詢的簡單語句 -

x.LogSqlInConsole = true;

現在,我們的 NHibernateDemoDB 資料庫中的學生表中有兩條記錄。讓我們從資料庫中檢索所有記錄,如下面的程式碼所示。

using NHibernate.Cfg; 
using NHibernate.Dialect; 
using NHibernate.Driver; 

using System; 
using System.Linq; 
using System.Reflection;

namespace NHibernateDemoApp { 

   class Program { 
      
      static void Main(string[] args) { 
         var cfg = new Configuration();
			
         String Data Source = asia13797\\sqlexpress;
         String Initial Catalog = NHibernateDemoDB;
         String Integrated Security = True;
         String Connect Timeout = 15;
         String Encrypt = False;
         String TrustServerCertificate = False;
         String ApplicationIntent = ReadWrite;
         String MultiSubnetFailover = False;			
         
         cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source + 
            Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
            TrustServerCertificate + ApplicationIntent + MultiSubnetFailover"; 
            
            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect>(); 
            x.LogSqlInConsole = true; 
         }); 
      
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         var sefact = cfg.BuildSessionFactory();
      
         using (var session = sefact.OpenSession()) { 
         
            using (var tx = session.BeginTransaction()) { 
               Console.WriteLine("\nFetch the complete list again\n");
               var students = session.CreateCriteria<Student>().List<Student>(); 
      
               foreach (var student in students) { 
                  Console.WriteLine("{0} \t{1} \t{2}", student.ID, student.FirstMidName,
                     student.LastName); 
               } 
               
               tx.Commit(); 
            } 
				
            Console.ReadLine(); 
         } 
      } 
   } 
}

因此,讓我們繼續再次執行此應用程式,您將看到以下輸出 -

NHibernate: SELECT this_.ID as ID0_0_, this_.LastName as LastName0_0_,
   this_.FirstMidName as FirstMid3_0_0_ FROM Student this_

Fetch the complete list again

3 Allan Bommer
4 Jerry Lewis

如您所見,傳送到資料庫的select 子句實際上就像一個子句,它將檢索 ID、FirstMidName 和 LastName。因此,所有這些都發送到資料庫並在那裡處理,而不是將大量記錄帶回您的伺服器並在伺服器端處理。

NHibernate Profiler

檢視這些結果的另一種方法是使用 NHibernate Profiler。NHibernate Profiler 是一款商業工具,但它對於使用 NHibernate 應用程式非常有用。您可以輕鬆地從 NuGet 將 NHibernate Profiler 安裝到您的應用程式中。

讓我們從“工具”選單中的 NuGet 管理器控制檯中選擇“NuGet 包管理器”→“程式包管理器控制檯”。這將開啟“程式包管理器控制檯”視窗。輸入以下命令並按 Enter 鍵。

PM> install-package NHibernateProfiler

它將安裝 NHibernate Profiler 的所有必需二進位制檔案,一旦成功安裝,您將看到以下訊息。

NHibernate Profiler

您還將看到 NHibernate Profiler 啟動,一旦安裝完成。它需要許可證才能使用,但出於演示目的,我們可以使用 NHibernate Profiler 的 30 天試用版。

現在,NHibernate Profiler 已針對 Web 應用程式進行了最佳化,您將看到它已在解決方案資源管理器中添加了App_Start 資料夾。為了使所有這些都保持簡單,請刪除 App_Start 資料夾,並且您還會注意到在 Program 類中的 Main 方法開頭添加了一個語句。

App_Start.NHibernateProfilerBootstrapper.PreStart();

也請刪除此語句,只需新增一個簡單的呼叫NHibernateProfiler.Initialize,如下面的程式碼所示。

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cfg; 
using NHibernate.Dialect; 
using NHibernate.Driver; 

using System; 
using System.Linq; 
using System.Reflection;

namespace NHibernateDemoApp { 
   
   class Program { 
	
      static void Main(string[] args) { 
		
         NHibernateProfiler.Initialize(); 
         var cfg = new Configuration();
			
         String Data Source = asia13797\\sqlexpress;
         String Initial Catalog = NHibernateDemoDB;
         String Integrated Security = True;
         String Connect Timeout = 15;
         String Encrypt = False;
         String TrustServerCertificate = False;
         String ApplicationIntent = ReadWrite;
         String MultiSubnetFailover = False;			
         
         cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source + 
            Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
            TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";
				
            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect>(); 
            x.LogSqlInConsole = true; 
         }); 

         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         var sefact = cfg.BuildSessionFactory(); 
         
         using (var session = sefact.OpenSession()) { 
            
            using (var tx = session.BeginTransaction()){ 
               var students = session.CreateCriteria<Student>().List<Student>(); 
               Console.WriteLine("\nFetch the complete list again\n");
               
               foreach (var student in students) { 
                  Console.WriteLine("{0} \t{1} \t{2}", student.ID, student.FirstMidName,
                     student.LastName); 
               } 
					
               tx.Commit(); 
            } 
				
            Console.ReadLine(); 
         } 
      } 
   
   }
}

現在,當你執行應用程式時,它會將資料傳送到 NHibernate Profiler 應用程式。

NHibernate Profiler Application

你可以在這裡看到,我們有一個很好的顯示,顯示我們已經啟動了事務,SQL 以一種很好的格式對資料庫做了什麼操作。

所以這對於確定 NHibernate 應用程式內部到底發生了什麼非常有用。一旦應用程式達到一定的複雜程度,它就會變得非常有用,這時你需要一些更像 SQL Profiler 的東西,但同時具備 NHibernate 的知識。

為對映檔案新增 IntelliSense

在本節中,我們將向我們的 NHibernate 對映檔案(*.hbm.xml 檔案)新增IntelliSense。正如你所觀察到的,在對映域 Student 類時,我們目前沒有可用的 IntelliSense。擁有XML 架構非常有用。因此,在本節中,你將瞭解如何在 Visual Studio 中為這些 NHibernate XML 檔案新增 IntelliSense。

開啟對映檔案,你將看到 XML 選單選項出現在主選單中。

XML Menu

選擇 XML → 架構… 選單選項,它將顯示 XML 架構對話方塊。

XML Schemas

選擇位於對話方塊右上角的新增… 按鈕,它將開啟檔案對話方塊。現在轉到包資料夾,它位於專案解決方案資料夾中,你將看到專案中包含的不同包。

Packages Folder

現在,雙擊NHibernate.4.*** 資料夾,你將看到兩個架構 (*.xsd) 檔案或 XML 架構定義檔案,它們定義了 NHibernate 配置和對映。

Nhibernate Configuration

選擇這兩個架構檔案,然後單擊開啟按鈕。

Two Schema Files

你可以看到 NHibernate 架構已新增到 XML 架構對話方塊中。單擊確定按鈕。現在,讓我們開始一個新的屬性標籤,你將看到我們在這裡擁有完整的 IntelliSense。

IntelliSense

IntelliSense 現在可供你使用,這節省了物件關係對映期間的大量時間。

NHibernate - 資料型別對映

在本節中,我們將介紹對映資料型別。對映實體很簡單,實體類始終使用<class>、<subclass> 和 <joined-subclass>對映元素對映到資料庫表。值型別需要更多內容,這就是需要對映型別的地方。

NHibernate 能夠對映各種資料型別。以下是支援的最常見資料型別的列表。

對映型別 .NET 型別 System.Data.DbType
Int16 System.Int16 DbType.Int16
Int32 System.Int32 DbType.Int32
Int64 System.Int64 DbType.Int64
Single System.Single DbType.Single
Double System.Double DbType.Double
Decimal System.Decimal DbType.Decimal
String System.String DbType.String
AnsiString System.String DbType.AnsiString
Byte System.Byte DbType.Byte
Char System.Char DbType.StringFixedLength—一個字元
AnsiChar System.Char DbType.AnsiStringFixedLength—一個字元
Boolean System.Boolean DbType.Boolean
Guid System.Guid DbType.Guid
PersistentEnum System.Enum(列舉) 底層值的 DbType
TrueFalse System.Boolean DbType.AnsiStringFixedLength—'T' 或 'F'
YesNo System.Boolean DbType.AnsiStringFixedLength—'Y' 或 'N'
DateTime DateTime DbType.DateTime—忽略毫秒
Ticks System.DateTime DbType.Int64
TimeSpan System.TimeSpan DbType.Int64
Timestamp System.DateTime DbType.DateTime—儘可能具體地支援資料庫
Binary System.Byte[] DbType.Binary
BinaryBlob System.Byte[] DbType.Binary
StringClob System.String DbType.String
Serializable 任何用 SerializableAttribute 標記的 System.Object DbType.Binary
CultureInfo System.Globalization.CultureInfo DbType.String—文化五字元
Type System.Type DbType.String 儲存程式集限定名

上表詳細說明了下列要點。

  • 從簡單的數字型別到字串,可以使用varchar、char等各種方式進行對映,以及字串 blob 和資料庫支援的所有型別的各種型別。

  • 它還能夠對映布林值,既可以對映到使用零和一的欄位,也可以對映到包含 true、false 或 T 和 F 的字元欄位。

  • 有各種方法可以定義它如何對映到後端,資料庫中的布林值。

  • 我們可以處理DateTime的對映,包括和不包括時區偏移、夏令時等。

  • 我們還可以對映列舉;我們可以將它們對映到字串或其底層數值。

讓我們來看一個簡單的示例,其中我們在資料庫和 Student 類中都具有相同的屬性名稱。

現在讓我們將 Student 類中的 FirstMidName 更改為 FirstName,我們不會更改 FirstMidName 列,但我們將看到如何告訴 NHibernate 執行此轉換。以下是更新後的 Student 類。

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks;

namespace NHibernateDemoApp { 
  
   class Student { 
      public virtual int ID { get; set; } 
      public virtual string LastName { get; set; } 
      public virtual string FirstName { get; set; } 
   }
}

這是 NHibernate 對映檔案的實現。

<?xml version = "1.0" encoding = "utf-8" ?> 
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemoApp" 
   namespace = "NHibernateDemoApp"> 
   
   <class name = "Student">
	
      <id name = "ID"> 
         <generator class = "native"/>
      </id> 
   
      <property name = "LastName"/> 
      <property name = "FirstName" column = "FirstMidName" type = "String"/> 
   </class> 

</hibernate-mapping>

在此示例中,假設 FirstName 欄位是 .NET 字串,而 FirstMidName 列是SQL nvarchar。現在要告訴 NHibernate 如何執行此轉換,請將 name 設定為FirstName,將 column 設定為FirstMidName,並將對映型別指定為 String,這適合此特定轉換。

以下是Program.cs檔案實現。

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cfg; 
using NHibernate.Dialect; 
using NHibernate.Driver; 

using System; 
using System.Linq; 
using System.Reflection;

namespace NHibernateDemoApp { 

   class Program { 
	
      static void Main(string[] args) { 
		
         NHibernateProfiler.Initialize(); 
         var cfg = new Configuration(); 
			
         String Data Source = asia13797\\sqlexpress;
         String Initial Catalog = NHibernateDemoDB;
         String Integrated Security = True;
         String Connect Timeout = 15;
         String Encrypt = False;
         String TrustServerCertificate = False;
         String ApplicationIntent = ReadWrite;
         String MultiSubnetFailover = False;
         
         cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source + 
            Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
            TrustServerCertificate + ApplicationIntent + MultiSubnetFailover"; 
            
            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect>(); 
            x.LogSqlInConsole = true; 
         }); 
         
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         var sefact = cfg.BuildSessionFactory();
			
         using (var session = sefact.OpenSession()) { 
            
            using (var tx = session.BeginTransaction()) { 
               var students = session.CreateCriteria<Student>().List<Student>(); 
               Console.WriteLine("\nFetch the complete list again\n"); 
               
               foreach (var student in students) { 
                  Console.WriteLine("{0} \t{1} \t{2}", student.ID, student.FirstName,
                     student.LastName); 
               } 
					
               tx.Commit(); 
            } 
				
            Console.ReadLine(); 
         } 
      } 
   }
}

現在,當您執行應用程式時,您將看到以下輸出。

NHibernate: SELECT this_.ID as ID0_0_, this_.LastName as LastName0_0_, 
   this_.FirstMidName as FirstMid3_0_0_ FROM Student this_

Fetch the complete list again
3 Allan Bommer
4 Jerry Lewis

正如你所看到的,它已將不同的屬性名稱對映到資料庫中的列名稱。

讓我們看另一個示例,在這個示例中,我們將在 Student 類中新增另一個enum型別的屬性。以下是 Student 類實現。

using System; 
using System.Collections.Generic; 
using System.Linq; using System.Text; 
using System.Threading.Tasks; 

namespace NHibernateDemoApp { 
   
   class Student { 
      public virtual int ID { get; set; } 
      public virtual string LastName { get; set; } 
      public virtual string FirstName { get; set; } 
      public virtual StudentAcademicStanding AcademicStanding { get; set; } 
   } 
   
   public enum StudentAcademicStanding { 
      Excellent, 
      Good, 
      Fair, 
      Poor, 
      Terrible 
   } 
}

正如你所看到的,列舉可以具有各種不同的值,例如 Excellent、Good、Fair、Poor 和 Terrible。

跳到對映檔案,你可以看到這些屬性中的每一個都列在對映檔案中,包括新新增的AcademicStanding屬性。

<?xml version = "1.0" encoding = "utf-8" ?> 
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" 
   assembly = "NHibernateDemoApp" namespace = "NHibernateDemoApp"> 
   
   <class name = "Student"> 
	
      <id name = "ID"> 
         <generator class = "native"/> 
      </id> 

      <property name = "LastName"/> 
      <property name = "FirstName" column = "FirstMidName" type = "String"/> 
      <property name = "AcademicStanding"/> 
   </class>  

</hibernate-mapping>

現在我們也需要更改資料庫,因此請轉到 SQL Server 物件資源管理器,右鍵單擊資料庫並選擇新建查詢… 選項。

New Query

它將開啟查詢編輯器,然後指定以下查詢。

DROP TABLE [dbo].[Student]

CREATE TABLE [dbo].[Student] ( 
   [ID] INT IDENTITY (1, 1) NOT NULL, 
   [LastName] NVARCHAR (MAX) NULL, 
   [FirstMidName] NVARCHAR (MAX) NULL, 
   [AcademicStanding] NCHAR(10) NULL, 
   CONSTRAINT [PK_dbo.Student] PRIMARY KEY CLUSTERED ([ID] ASC) 
);

此查詢將首先刪除現有的 student 表,然後建立一個新表。

Create New Table

單擊上面所示的執行圖示。查詢成功執行後,你將看到一條訊息。

展開資料庫和表下拉選單,然後右鍵單擊 Student 表並選擇檢視設計器。

Table Dropdown

現在,你將看到新建立的表,該表也具有新的屬性 AcademicStanding。

Academic Standing

讓我們新增兩條記錄,如下面的Program.cs檔案所示。

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cfg; 
using NHibernate.Dialect; 
using NHibernate.Driver; 

using System; 
using System.Linq; 
using System.Reflection;

namespace NHibernateDemoApp { 

   class Program { 
      
      static void Main(string[] args) { 
		
         NHibernateProfiler.Initialize(); 
         var cfg = new Configuration(); 
			
         String Data Source = asia13797\\sqlexpress;
         String Initial Catalog = NHibernateDemoDB;
         String Integrated Security = True;
         String Connect Timeout = 15;
         String Encrypt = False;
         String TrustServerCertificate = False;
         String ApplicationIntent = ReadWrite;
         String MultiSubnetFailover = False;
         
         cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source + 
            Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
            TrustServerCertificate + ApplicationIntent + MultiSubnetFailover"; 
            
            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect>(); 
         }); 
         
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         var sefact = cfg.BuildSessionFactory(); 
         
         using (var session = sefact.OpenSession()) { 
            using (var tx = session.BeginTransaction()) { 
               
               var student1 = new Student { 
                  ID = 1, 
                  FirstName = "Allan", 
                  LastName = "Bommer",
                  AcademicStanding = StudentAcademicStanding.Excellent 
               };
               
               var student2 = new Student { 
                  ID = 2, 
                  FirstName = "Jerry", 
                  LastName = "Lewis", 
                  AcademicStanding = StudentAcademicStanding.Good 
               };
					
               session.Save(student1); 
               session.Save(student2);
               var students = session.CreateCriteria<Student>().List<Student>(); 
               Console.WriteLine("\nFetch the complete list again\n");
               
               foreach (var student in students) { 
                  Console.WriteLine("{0} \t{1} \t{2} \t{3}", student.ID,
                     student.FirstName, student.LastName, student.AcademicStanding); 
               } 
					
               tx.Commit(); 
            }
				
            Console.ReadLine(); 
         } 
      } 
   } 
}

現在讓我們執行你的應用程式,你將在控制檯視窗中看到以下輸出。

Fetch the complete list again

1 Allan Bommer Excellent
2 Jerry Lewis Good

現在讓我們透過右鍵單擊 Student 表來檢視資料庫。

Database

選擇檢視資料,你將在學生表中看到兩條記錄,如下面的螢幕截圖所示。

View Data

你可以看到添加了兩條記錄,Allan 的 AcademicStanding 為 0,Jerry 的 AcademicStanding 為 1。這是因為在 .Net 中,第一個列舉值預設值為 0,如果檢視StudentAcademicStanding,則為 Excellent。而,在 Student.cs 檔案中,Good 是第二個,所以它的值為 1。

NHibernate - 配置

在本節中,我們將瞭解 NHibernate 配置。我們可以透過不同的方式配置 NHibernate。它分為兩大類

  • 基於 XML 的配置
  • 基於程式碼的配置

基於程式碼的配置

基於程式碼的配置內置於 NHibernate 中。它是在 NHibernate 3 左右引入的,到目前為止,我們一直在使用基於程式碼的配置。

String Data Source = asia13797\\sqlexpress;
String Initial Catalog = NHibernateDemoDB;
String Integrated Security = True;
String Connect Timeout = 15;
String Encrypt = False;
String TrustServerCertificate = False;
String ApplicationIntent = ReadWrite;
String MultiSubnetFailover = False;

cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source + 
   Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
   TrustServerCertificate + ApplicationIntent + MultiSubnetFailover"; 
   
   x.Driver<SqlClientDriver>(); 
   x.Dialect<MsSql2008Dialect>(); 
   x.LogSqlInConsole = true; 
}); 

cfg.AddAssembly(Assembly.GetExecutingAssembly());

所有配置都指定在 C# 程式碼中。你可以在這裡看到我們有新的配置物件,然後我們使用 NHibernate 3.1 中引入的流暢配置來配置資料庫。我們使用什麼連線字串,連線到什麼資料庫以及要使用的方言。我們還將我們的對映程式集直接新增到此處。

基於 XML 的配置

如果使用基於 XML 的配置,則可以使用hibernate.cfg.xml檔案,它只是一個使用 NHibernate 架構的獨立 xml 檔案,或者可以將該 NHibernate 特定配置嵌入到你的應用程式或web.cfg中。hibernate.cfg.xml 名稱預設為此,但我們也可以為此 xml 檔案使用任意名稱。

讓我們透過向 NHibernateDemoApp 專案新增一個新的 xml 檔案並將其命名為 hibernate.cfg.xml 來了解基於 XML 的配置。

將以下資訊輸入 hibernate.cfg.xml 檔案。

<?xml version = "1.0" encoding = "utf-8" ?> 
<hibernate-configuration xmlns = "urn:nhibernate-configuration-2.2"> 
   <session-factory> 
   
      <property name = "connection.connection_string">
         Data Source = asia13797\\sqlexpress;
         Initial Catalog = NHibernateDemoDB;
         Integrated Security = True;
         Connect Timeout = 15;
         Encrypt = False;
         TrustServerCertificate = False;
         ApplicationIntent = ReadWrite;
         MultiSubnetFailover = False;
      </property> 
      
      <property name = "connection.driver_class">
         NHibernate.Driver.SqlClientDriver
      </property> 
		
      <property name = "dialect">
         NHibernate.Dialect.MsSql2008Dialect
      </property> 
		
      <mapping assembly = "NHibernateDemoApp"/>
		
   </session-factory> 
	
</hibernate-configuration>

正如你在上面的 xml 檔案中看到的,我們指定了與 C# 中提到的相同的配置。

現在讓我們註釋掉 Program.cs 檔案中的此配置,並只調用Configure()方法,它將載入hibernate.cfg.xml檔案,如下所示。

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cfg; 
using NHibernate.Dialect; 
using NHibernate.Driver; 

using System; 
using System.Linq; 
using System.Reflection; 

namespace NHibernateDemoApp { 

   class Program { 
      
      static void Main(string[] args) { 
		
         NHibernateProfiler.Initialize(); 
         var cfg = new Configuration(); 
         
         //cfg.DataBaseIntegration(x =>
         
         //{ 
            // x.ConnectionString = "Data Source = asia13797;\\sqlexpress
            Initial Catalog = NHibernateDemoDB;
            Integrated Security = True;
            Connect Timeout = 15;
            Encrypt =False;
            TrustServerCertificate = False;
            ApplicationIntent = ReadWrite;
            MultiSubnetFailover = False"; 
            
            // x.Driver<SqlClientDriver>(); 
            // x.Dialect<MsSql2008Dialect>(); 
            // x.LogSqlInConsole = true; 
         //}); 
         
         //cfg.AddAssembly(Assembly.GetExecutingAssembly());
         cfg.Configure();
         var sefact = cfg.BuildSessionFactory();
			
         using (var session = sefact.OpenSession()) { 
            
            using (var tx = session.BeginTransaction()) { 
               var students = session.CreateCriteria<Student>().List<Student>(); 
               Console.WriteLine("\nFetch the complete list again\n"); 
               
               foreach (var student in students) { 
                  Console.WriteLine("{0} \t{1} \t{2} \t{3}", student.ID,
                     student.FirstName, student.LastName, student.AcademicStanding); 
               } 
					
               tx.Commit(); 
            } 
				
            Console.ReadLine(); 
         } 
      } 
   } 
}

讓我們再次執行你的應用程式,你將看到相同的輸出。

Fetch the complete list again

1 Allan Bommer Excellent
2 Jerry Lewis Good

NHibernate - 覆蓋配置

在本節中,我們將介紹如何覆蓋 NHibernate 配置。你只需要記住幾件事。

  • 首先,NHibernate 中的配置是累加的。

  • 因此,你不僅可以使用單個 xml 檔案,也不必只使用基於程式碼的配置或 Fluent NHibernate。

  • 你可以根據你希望如何配置應用程式來混合和匹配所有這些方法。

  • 要記住的重要一點是,最後的配置獲勝。

在以下示例中,你可以看到我們建立了配置物件,使用基於程式碼的配置對其進行配置,最後呼叫cfg.configure()方法,該方法載入 hibernate.cfg.xml 檔案。

String Data Source = asia13797\\sqlexpress;
String Initial Catalog = NHibernateDemoDB;
String Integrated Security = True;
String Connect Timeout = 15;
String Encrypt = False;
String TrustServerCertificate = False;
String ApplicationIntent = ReadWrite;
String MultiSubnetFailover = False;

cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source + 
   Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
   TrustServerCertificate + ApplicationIntent + MultiSubnetFailover"; 
	
   x.Driver<SqlClientDriver>(); 
   x.Dialect<MsSql2008Dialect>(); 
   x.LogSqlInConsole = true; 
}); 

cfg.Configure();
  • 因此,hibernate.cfg.xml 中的任何內容都會覆蓋基於程式碼的配置設定。

  • 透過反轉這兩個過程,我們可以將預設值放在 hibernate.cfg.xml 中,然後在基於程式碼的配置中進行覆蓋。

  • 如果您使用基於程式碼的配置,沒有任何內容排除它,並且也沒有任何內容阻止您使用 hibernate.cfg.xml 檔案。

讓我們來看一個簡單的示例,在這個示例中,我們將使用 XML 基於和程式碼基於配置的混合來覆蓋配置。

讓我們也使用以下程式碼將連線字串移動到 **app.config** 檔案中。

<?xml version = "1.0" encoding = "utf-8" ?> 

<configuration> 
   
   <startup> 
      <supportedRuntime version = "v4.0" sku = ".NETFramework,Version = v4.5" /> 
   </startup> 
   
   <connectionStrings> 
      <add name = "default" connectionString = "Data Source =
         asia13797\\sqlexpress;
         Initial Catalog = NHibernateDemoDB;
         Integrated Security = True;
         Connect Timeout = 15;
         Encrypt = False;
         TrustServerCertificate = False;
         ApplicationIntent = ReadWrite;
         MultiSubnetFailover = False"/> 
   </connectionStrings> 

</configuration>

連線字串位於某個具有預設名稱的 **app.config** 檔案中。現在,我們需要在 hibernate.cfg.xml 檔案中提及預設名稱,而不是連線字串。

<?xml version = "1.0" encoding = "utf-8" ?> 
<hibernate-configuration xmlns = "urn:nhibernate-configuration-2.2"> 

   <session-factory> 
      <property name = "connection.connection_string">default</property> 
		
      <property name = "connection.driver_class">
         NHibernate.Driver.SqlClientDriver
      </property> 
		
      <property name = "dialect">
         NHibernate.Dialect.MsSql2008Dialect
      </property> 
		
      <mapping assembly = "NHibernateDemoApp"/> 
   </session-factory> 

</hibernate-configuration>

讓我們註釋掉基於程式碼的配置中的連線字串部分、驅動程式和方言部分,因為程式將從 hibernate.cfg.xml 檔案中讀取它,並且 **LogSqlInConsole** 部分將保留在基於程式碼的配置中。

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cfg; 
using NHibernate.Dialect; 
using NHibernate.Driver; 

using System; 
using System.Linq; 
using System.Reflection;
namespace NHibernateDemoApp { 
   
   class Program { 
	
      static void Main(string[] args) { 
		
         NHibernateProfiler.Initialize(); 
         var cfg = new Configuration();
			
         String Data Source = asia13797\\sqlexpress;
         String Initial Catalog = NHibernateDemoDB;
         String Integrated Security = True;
         String Connect Timeout = 15;
         String Encrypt = False;
         String TrustServerCertificate = False;
         String ApplicationIntent = ReadWrite;
         String MultiSubnetFailover = False;
			
         cfg.DataBaseIntegration(x = > { //x.ConnectionString = "Data Source + 
            Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
            TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";
				
            //x.Driver<SqlClientDriver>(); 
            //x.Dialect<MsSql2008Dialect>(); 
            x.LogSqlInConsole = true; 
         }); 
         
         cfg.Configure(); 
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         var sefact = cfg.BuildSessionFactory(); 
			
         using (var session = sefact.OpenSession()) { 
			
            using (var tx = session.BeginTransaction()) { 
               
               var students = session.CreateCriteria<Student>().List<Student>();
               Console.WriteLine("\nFetch the complete list again\n"); 
               
               foreach (var student in students) { 
                  Console.WriteLine("{0} \t{1} \t{2} \t{3}", student.ID,
                  student.FirstName, student.LastName, student.AcademicStanding); 
               } 
					
               tx.Commit(); 
            }
				
            Console.ReadLine(); 
         } 
      } 
   } 
}

現在,當您執行應用程式時,您將看到程式已從基於程式碼的配置中讀取日誌,並從 hibernate.cfg.xml 檔案中讀取其他配置。

NHibernate: SELECT this_.ID as ID0_0_, this_.LastName as LastName0_0_,   
   this_.FirstMidName as FirstMid3_0_0_, this_.AcademicStanding as Academic4_0_0_ FROM
   Student this_

Fetch the complete list again
1 Allan Bommer Excellent
2 Jerry Lewis Good

因此,現在我們有一些配置在 **hibernate.cfg.xml** 檔案中,一些在基於程式碼的配置中,並且根據呼叫基於程式碼的配置與 **configure()** 的順序,我們可以更改哪個覆蓋另一個。

NHibernate - 批處理大小

在本章中,我們將介紹批次大小更新。批次大小允許您 **控制** 在單個往返資料庫操作中傳送的更新數量(對於支援的資料庫)。

  • 從 NHibernate 3.2 開始,更新批處理大小已設定為預設值。

  • 但是,如果您使用的是早期版本或需要調整 NHibernate 應用程式,則應檢視更新批處理大小,這是一個非常有用的引數,可用於調整 NHibernate 的效能。

  • 實際上,批次大小控制要按組推送到資料庫中的插入數量。

  • 目前,只有 SQL Server 和 Oracle 支援此選項,因為底層資料庫提供程式需要支援查詢批處理。

讓我們來看一個簡單的示例,在這個示例中,我們將批處理大小設定為 10,這將以一組插入 10 條記錄。

cfg.DataBaseIntegration(x => { 
  
   x.ConnectionString = "default"; 
   x.Driver<SqlClientDriver>(); 
   x.Dialect<MsSql2008Dialect>(); 
   x.LogSqlInConsole = true; 
   x.BatchSize = 10; 

});

以下是完整的實現,其中 25 條記錄將新增到資料庫中。

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cfg; 
using NHibernate.Dialect; 
using NHibernate.Driver; 

using System; 
using System.Linq; 
using System.Reflection;
namespace NHibernateDemoApp {
   
   class Program {
	
      static void Main(string[] args) { 
		
         NHibernateProfiler.Initialize(); 
         var cfg = new Configuration(); 
			
         String Data Source = asia13797\\sqlexpress;
         String Initial Catalog = NHibernateDemoDB;
         String Integrated Security = True;
         String Connect Timeout = 15;
         String Encrypt = False;
         String TrustServerCertificate = False;
         String ApplicationIntent = ReadWrite;
         String MultiSubnetFailover = False;
			
         cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source + 
            Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
            TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";
         
            x.Driver>SqlClientDriver<(); 
            x.Dialect>MsSql2008Dialect>(); 
            x.LogSqlInConsole = true; 
            x.BatchSize = 10; 
         }); 
         
         //cfg.Configure(); 
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         var sefact = cfg.BuildSessionFactory();
			
         using (var session = sefact.OpenSession()) {
			
            using (var tx = session.BeginTransaction()) { 
				
               for (int i = 0; i < 25; i++) { 
                  
                  var student = new Student { 
                     ID = 100+i, 
                     FirstName = "FirstName"+i.ToString(), 
                     LastName = "LastName" + i.ToString(), 
                     AcademicStanding = StudentAcademicStanding.Good 
                  };
						
                  session.Save(student); 
               } 
					
               tx.Commit();
               var students = session.CreateCriteria<Student>().List<Student>(); 
               Console.WriteLine("\nFetch the complete list again\n"); 
               
               foreach (var student in students) { 
                  Console.WriteLine("{0} \t{1} \t{2} \t{3}", student.ID,student.FirstName,
                     student.LastName, student.AcademicStanding); 
               } 
            } 
				
            Console.ReadLine(); 
         } 
      }
   } 
}

現在讓我們執行您的應用程式,您會看到所有這些更新都跳到了 NHibernate 效能分析器中。我們有 26 次單獨的資料庫往返操作,25 次用於插入,一次用於檢索學生列表。

現在,為什麼會這樣?原因是 NHibernate 需要執行 **select scope identity**,因為我們在對映檔案中使用了本機識別符號生成策略作為 ID,如下面的程式碼所示。

<?xml version = "1.0" encoding = "utf-8" ?> 
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" 
   assembly = "NHibernateDemoApp" 
   namespace = "NHibernateDemoApp"> 
   
   <class name = "Student"> 

      <id name = "ID"> 
         <generator class = "native"/> 
      </id> 

      <property name = "LastName"/> 
      <property name = "FirstName" column = "FirstMidName" type = "String"/> 
      <property name = "AcademicStanding"/> 
   
   </class> 
</hibernate-mapping>

因此,我們需要使用不同的方法,例如 **guid.comb** 方法。如果要使用 guid.comb,我們需要轉到我們的客戶並將其更改為 **guid**。這樣就可以正常工作了。現在讓我們使用以下程式碼從 native 更改為 guid.comb。

<?xml version = "1.0" encoding = "utf-8" ?> 
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly =
   "NHibernateDemoApp" namespace = "NHibernateDemoApp"> 
   <class name = "Student"> 

      <id name = "ID"> 
         <generator class = "guid.comb"/> 
      </id> 

      <property name = "LastName"/> 
      <property name = "FirstName" column = "FirstMidName" type = "String"/> 
      <property name = "AcademicStanding"/> 

   </class> 

</hibernate-mapping>

因此,資料庫負責生成這些 ID。NHibernate 找出生成的 ID 的唯一方法是在之後立即選擇它。否則,如果我們建立了一批學生,它將無法匹配建立的學生的 ID。

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks;

namespace NHibernateDemoApp { 

   class Student { 
      public virtual Guid ID { get; set; } 
      public virtual string LastName { get; set; } 
      public virtual string FirstName { get; set; } 
      public virtual StudentAcademicStanding AcademicStanding { get; set; }
   } 
	
   public enum StudentAcademicStanding { 
      Excellent, 
      Good, 
      Fair, 
      Poor, 
      Terrible 
   }
}

我們只需要更新我們的資料庫。讓我們刪除學生表並透過指定以下查詢建立一個新表,因此轉到 SQL Server 物件資源管理器並右鍵單擊資料庫並選擇 **新建查詢**…選項。

它將開啟查詢編輯器,然後指定以下查詢。

DROP TABLE [dbo].[Student]
CREATE TABLE [dbo].[Student] ( 

   -- [ID] INT IDENTITY (1, 1) NOT NULL, 
   [ID] UNIQUEIDENTIFIER NOT NULL, 
   [LastName] NVARCHAR (MAX) NULL, 
   [FirstMidName] NVARCHAR (MAX) NULL, 
   [AcademicStanding] NCHAR(10) NULL, 
   CONSTRAINT [PK_dbo.Student] PRIMARY KEY CLUSTERED ([ID] ASC) 
);

此查詢將首先刪除現有的學生表,然後建立一個新表。如您所見,我們使用了 **UNIQUEIDENTIFIER** 而不是使用整數主鍵作為 ID。

執行此查詢,然後轉到 **設計器檢視**,您將看到現在 ID 使用唯一識別符號建立,如下面的影像所示。

Designer View

現在,我們需要從 program.cs 檔案中刪除 ID,在插入資料時,因為它現在會自動為此生成 **guids**。

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cfg; 
using NHibernate.Dialect; 
using NHibernate.Driver; 

using System; 
using System.Linq; 
using System.Reflection;
namespace NHibernateDemoApp { 
   
   class Program { 
	
      static void Main(string[] args) { 
		
         NHibernateProfiler.Initialize();
         var cfg = new Configuration();

         String Data Source = asia13797\\sqlexpress;
         String Initial Catalog = NHibernateDemoDB;
         String Integrated Security = True;
         String Connect Timeout = 15;
         String Encrypt = False;
         String TrustServerCertificate = False;
         String ApplicationIntent = ReadWrite;
         String MultiSubnetFailover = False;
			
         cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source + 
            Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
            TrustServerCertificate + ApplicationIntent + MultiSubnetFailover"; 
				
            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect>(); 
            x.LogSqlInConsole = true;
            x.BatchSize = 10; 
         }); 
         
         //cfg.Configure(); 
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         var sefact = cfg.BuildSessionFactory(); 
			
         using (var session = sefact.OpenSession()) { 
			
            using (var tx = session.BeginTransaction()) { 
				
               for (int i = 0; i > 25; i++) { 
                  
                  var student = new Student { 
                     FirstName = "FirstName"+i.ToString(), 
                     LastName = "LastName" + i.ToString(), 
                     AcademicStanding = StudentAcademicStanding.Good 
                  }; 
                  
                  session.Save(student); 
               } 
					
               tx.Commit(); 
               var students = session.CreateCriteria<Student>().List<Student>(); 
               Console.WriteLine("\nFetch the complete list again\n"); 
               
               foreach (var student in students) { 
                  Console.WriteLine("{0} \t{1} \t{2} \t{3}", student.ID,
                     student.FirstName,student.LastName, student.AcademicStanding);
               } 
            
            }
				
            Console.ReadLine(); 
         } 
      } 
   } 
}

現在再次執行應用程式並檢視 NHibernate 效能分析器。現在,NHibernate 效能分析器而不是進行 26 次往返操作,只會進行 4 次。

NHibernate profiler Round Trips

它已將 10 行插入表中,然後是另外 10 行,然後是剩餘的 5 行。提交後,它還插入了一行以檢索所有記錄。

  • 所以它將其劃分為 10 組,儘可能地進行。

  • 因此,如果您進行大量插入,這可以顯著提高應用程式中的插入效能,因為您可以將其批處理。

  • 這是因為 NHibernate 本身使用 **guid.comb** 演算法分配這些 guid,並且它不必依賴資料庫來執行此操作。

  • 因此,使用批處理大小是調整它的好方法。

NHibernate - 快取

在本章中,我們將介紹 NHibernate 應用程式中的 **快取** 如何工作。它內建支援快取。它看起來像一個簡單的功能,但實際上,它是最複雜的功能之一。我們將從一級快取開始。

一級快取

此快取機制在 NHibernate 中預設啟用,我們無需執行任何操作即可使用快取。為了理解這一點,讓我們來看一個簡單的示例,如您所見,我們的資料庫中有兩條記錄。

First Level Cache

現在在這個示例中,我們將檢索 ID 為 1 的學生,並且我們將使用相同的會話查詢兩次,如下面的程式碼所示。

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cache; 
using NHibernate.Cfg; 
using NHibernate.Dialect;
using NHibernate.Driver; 
using NHibernate.Linq; 

using System; 
using System.Linq; 
using System.Reflection;
namespace NHibernateDemoApp { 
   
   class Program { 
      static void Main(string[] args) {
		
         NHibernateProfiler.Initialize(); 
         var cfg = new Configuration(); 
			
         String Data Source = asia13797\\sqlexpress;
         String Initial Catalog = NHibernateDemoDB;
         String Integrated Security = True;
         String Connect Timeout = 15;
         String Encrypt = False;
         String TrustServerCertificate = False;
         String ApplicationIntent = ReadWrite;
         String MultiSubnetFailover = False;
         
         cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source + 
            Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
            TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";
				
            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect>(); 
            x.LogSqlInConsole = true; 
            x.BatchSize = 10; 
         }); 
         
         //cfg.Configure(); 
         
         cfg.Cache(c => { 
            c.UseMinimalPuts = true; 
            c.UseQueryCache = true; 
         }); 
			
         cfg.SessionFactory().Caching .Through<HashtableCacheProvider>()
            .WithDefaultExpiration(1440); 
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         var sefact = cfg.BuildSessionFactory(); 
         
         using (var session = sefact.OpenSession()){ 
			
            using (var tx = session.BeginTransaction()) { 
               var studentUsingTheFirstQuery = session.Get<Student>(1);
               var studentUsingTheSecondQuery = session.Get<Student>(1); 
            } 
            
            Console.ReadLine(); 
         } 
      } 
   } 
}

現在讓我們執行此應用程式並在 NHibernate 效能分析器中檢視結果。

Run the Application

您會驚訝地發現 NHibernate 只觸發了一個查詢。這就是 NHibernate 如何使用一級快取。當第一個查詢執行時,NHibernate 將 ID = 1 的 Student 快取在其一級快取中。

因此,當執行第二個查詢時,NHibernate 首先查詢 ID = 1 的一級快取 Student 實體,如果找到該實體,則 NHibernate 知道無需再次觸發另一個查詢來檢索相同的員工物件。

NHibernate - 對映元件

在本章中,我們將討論對映元件。在 NHibernate 中,**元件是一個值物件**。它本身沒有標識。

  • 一個例子是金錢物件,錢包可能裝有金錢,但那筆錢的確切身份無關緊要。

  • 它沒有自己的主鍵,但元件本身與擁有物件儲存在同一個表中。

讓我們來看一個簡單的示例,其中學生有一個地址,它是與之關聯的 **Location 類** 的物件。

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks;

namespace NHibernateDemoApp { 

   class Student { 
      public virtual int ID { get; set; } 
      public virtual string LastName { get; set; } 
      public virtual string FirstName { get; set; } 
      public virtual StudentAcademicStanding AcademicStanding { get; set; } 
      public virtual Location Address { get; set; } 
   }

   public class Location { 
      public virtual string Street { get; set; } 
      public virtual string City { get; set; } 
      public virtual string Province { get; set; } 
      public virtual string Country { get; set; } 
   }
  
   public enum StudentAcademicStanding { 
      Excellent, 
      Good, 
      Fair, 
      Poor, 
      Terrible 
   } 
}

現在,我們還需要透過執行以下查詢來更新資料庫,該查詢將首先刪除 Student 表,然後建立一個新表,該表還將包含 Location 類的列。

DROP TABLE [dbo].[Student]
CREATE TABLE [dbo].[Student] ( 

   [ID] INT IDENTITY (1, 1) NOT NULL, 
   [LastName] NVARCHAR (MAX) NULL, 
   [FirstMidName] NVARCHAR (MAX) NULL, 
   [AcademicStanding] NCHAR(10) NULL, 
   [Street] NVARCHAR (100) NULL, 
   [City] NVARCHAR (100) NULL, 
   [Province] NVARCHAR (100) NULL, 
   [Country] NVARCHAR (100) NULL, 
   CONSTRAINT [PK_dbo.Student] PRIMARY KEY CLUSTERED ([ID] ASC) 

);

現在要對映那些不是 Student 類直接一部分的列,但它們是 Location 類屬性,並且 Location 類物件在 student 類中定義。我們需要一個元件來正確對映它。讓我們在 **student.hbm.xml** 檔案中建立一個元件,如下面的程式碼所示。

<?xml version = "1.0" encoding = "utf-8" ?> 
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" 
   assembly = "NHibernateDemoApp" namespace = "NHibernateDemoApp"> 

   <class name = "Student"> 
      <id name = "ID"> 
         <generator class = "native"/>
      </id> 
   
      <property name = "LastName"/> 
		
      <property name = "FirstName" column = "FirstMidName" type = "String"/> 
      <property name = "AcademicStanding"/>
      
      <component name = "Address"> 
         <property name = "Street"/> 
         <property name = "City"/> 
         <property name = "Province"/> 
         <property name = "Country"/> 
      </component> 
   
   </class> 
</hibernate-mapping>

此元件是地址,它具有這些不同的屬性。有了這些資訊,NHibernate 現在就有了足夠的資訊來實際對映它。

現在,這是 Program.cs 檔案,其中建立並初始化了一個新的學生物件,然後將其儲存到資料庫中。然後它將從資料庫中檢索列表。

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cache; 
using NHibernate.Caches.SysCache; 
using NHibernate.Cfg; 
using NHibernate.Dialect; 
using NHibernate.Driver; 
using NHibernate.Linq; 

using System; 
using System.Linq; 
using System.Reflection;
namespace NHibernateDemoApp { 

   class Program {
	
      static void Main(string[] args) { 
		
         NHibernateProfiler.Initialize(); 
         var cfg = new Configuration();

         String Data Source = asia13797\\sqlexpress;
         String Initial Catalog = NHibernateDemoDB;
         String Integrated Security = True;
         String Connect Timeout = 15;
         String Encrypt = False;
         String TrustServerCertificate = False;
         String ApplicationIntent = ReadWrite;
         String MultiSubnetFailover = False;			
         
         cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source + 
            Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
            TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";
				
            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect>(); 
         }); 
         
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         var sefact = cfg.BuildSessionFactory(); 
			
         using (var session = sefact.OpenSession()) { 
			
            using (var tx = session.BeginTransaction()) { 
               
               var student1 = new Student { 
                  ID = 1, 
                  FirstName = "Allan", 
                  LastName = "Bommer", 
                  AcademicStanding = StudentAcademicStanding.Poor, 
                  
                  Address = new Location { 
                     Street = "123 Street", 
                     City = "Lahore", 
                     Province = "Punjab", 
                     Country = "Pakistan" 
                  } 
               }; 
					
               session.Save(student1); 
               tx.Commit(); 
               var students = session.Query<Student>().ToList<Student>();
               Console.WriteLine("\nFetch the complete list again\n"); 
               
               foreach (var student in students) { 
                  Console.WriteLine("{0} \t{1} \t{2} \t{3} \t{4} \t{5} \t{6} \t{7}", 
                     student.ID, 
                     student.FirstName, 
                     student.LastName, 
                     student.AcademicStanding, 
                     student.Address.Street, 
                     student.Address.City, 
                     student.Address.Province, 
                     student.Address.Country
                  ); 
               } 
            } 
            Console.ReadLine(); 
         } 
      }
   } 
}

現在我們可以執行此應用程式,NHibernate 可以將這些值儲存到資料庫中。當您執行應用程式時,您將看到以下輸出。

Fetch the complete list again

2 Allan Bommer Poor 123 Street Lahore Punjab Pakistan

以下是資料庫中的值。

Values Database

元件允許我們將資料庫表中的列分離到它們自己的單獨類中。

  • 這裡要注意的另一件事是,因為 Location 是一個類,它不是一個實體。

  • 它是一個值型別物件,並且沒有自己的主鍵。

  • 它與包含它的 Student 儲存在同一個表中。

  • 這就是我們在這裡使用元件的原因。

  • 這允許很大的靈活性來更改我們的類層,我們的類是如何定義的以及我們的資料庫是如何佈局的。

NHibernate - 關係

在本章中,我們將研究 NHibernate 中的關係。讓我們把注意力轉向如何理解 NHibernate 中的關係。最簡單的方法是從資料庫的角度考慮關係。

  • 我們首先建立一個新的應用程式,在該應用程式中,我們將建立客戶和訂單實體之間的一些關係。

  • 我們將要研究的第一個關係是一個經典的集合關係。

  • 我們有一個客戶,擁有一個訂單集合。

  • 這是一個一對多關係,它在資料庫中由 2 個表表示,訂單表上有一個客戶 ID,並且我們有一個指向客戶的反向外部索引鍵關係。

首先,我們需要建立一個數據庫和兩個表 Customer 和 Order。您可以透過在 SQL Server 資源管理器中指定以下查詢來建立它。

USE [master] 
GO 
CREATE DATABASE [NHibernateDemo] 
GO 
USE [NHibernateDemo] 
GO

CREATE TABLE [dbo].[Customer]( 
   [Id] [uniqueidentifier] NOT NULL, 
   [FirstName] [nvarchar](100) NOT NULL, 
   [LastName] [nvarchar](100) NOT NULL, 
   [Points] [int] NULL, [HasGoldStatus] [bit] NULL, 
   [MemberSince] [date] NULL, 
   [CreditRating] [nchar](20) NULL, 
   [AverageRating] [decimal](18, 4) NULL, 
   [Street] [nvarchar](100) NULL, 
   [City] [nvarchar](100) NULL, 
   [Province] [nvarchar](100) NULL, 
   [Country] [nvarchar](100) NULL,
   PRIMARY KEY CLUSTERED ([Id] ASC) 
) 

GO 
CREATE TABLE [dbo].[Order]( 
   [Id] [uniqueidentifier] NOT NULL, 
   [CustomerId] [uniqueidentifier] NULL, 
   [Ordered] [datetime] NULL, 
   [Shipped] [datetime] NULL, 
   [Street] [nvarchar](100) NULL, 
   [City] [nvarchar](100) NULL, 
   [Province] [nvarchar](100) NULL, 
   [Country] [nvarchar](100) NULL, 
   PRIMARY KEY CLUSTERED ([Id] ASC) 
) 
GO

它將在資料庫中建立兩個表。下圖顯示了 Customer 表。

Customer Table

下圖顯示了 Order 表,您可以在其中看到指向客戶的反向外部索引鍵關係。

Foreign Key Relationship

我們需要在 **app.config** 檔案中定義連線字串,以下是 app.config 檔案的實現。

<?xml version = "1.0" encoding = "utf-8" ?> 
<configuration> 
   
   <connectionStrings> 
      <add name = "default" connectionString = "Data Source =
         (localdb)\MSSQLLocalDB;Initial Catalog = NHibernateDemo;Integrated Security =
         True;Connect Timeout = 30;Encrypt = False;TrustServerCertificate = False;
         ApplicationIntent = ReadWrite;MultiSubnetFailover = False"/> 
   </connectionStrings> 

</configuration>

要在您的應用程式中安裝 NHibernate,請在 NuGet Manager Console 視窗中執行以下命令。

install-package NHibernate

要配置 NHibernate 配置,我們需要在 **hibernate.cfg.xml** 檔案中定義配置,如下面的程式碼所示。

<xml version = "1.0" encoding = "utf-8" ?> 
<hibernate-configuration xmlns = "urn:nhibernate-configuration-2.2"> 
   
   <session-factory> 
      <property name = "connection.connection_string_name">default</property>
		
      <property name = "connection.driver_class">
         NHibernate.Driver.SqlClientDriver 
      </property> 
		
      <property name = "dialect">
         NHibernate.Dialect.MsSql2008Dialect
      </property> 
		
      <property name = "show_sql">true</property> 
   </session-factory> 

</hibernate-configuration>

在這個示例中,我們將使用兩個域類,Customer 和 Order。

以下是 Customer.cs 檔案的實現,其中我們有兩個類,一個是 Customer 類,另一個是 Location 類,其中物件用作 Customer 類中的地址。

using System; 
using System.Text; 
using Iesi.Collections.Generic;

namespace NHibernateDemo { 

   public class Customer { 
      
      public Customer() { 
         MemberSince = DateTime.UtcNow; 
         Orders = new HashedSet<Order>(); 
      } 
      
      public virtual Guid Id { get; set; } 
      public virtual string FirstName { get; set; } 
      public virtual string LastName { get; set; } 
      public virtual double AverageRating { get; set; } 
      public virtual int Points { get; set; } 
		
      public virtual bool HasGoldStatus { get; set; }
      public virtual DateTime MemberSince { get; set; } 
      public virtual CustomerCreditRating CreditRating { get; set; } 
      public virtual Location Address { get; set; }
		
      public virtual ISet<Order> Orders { get; set; }
      public virtual void AddOrder(Order order) { Orders.Add(order); order.Customer = this; }
      
      public override string ToString() { 
         var result = new StringBuilder();
			
         result.AppendFormat("{1} {2} ({0})\r\n\tPoints: {3}\r\n\tHasGoldStatus:
            {4}\r\n\tMemberSince: {5} ({7})\r\n\tCreditRating: {6}\r\n\tAverageRating:
            {8}\r\n", Id, FirstName, LastName, Points, HasGoldStatus, MemberSince,
            CreditRating, MemberSince.Kind, AverageRating);
            result.AppendLine("\tOrders:"); 
         
         foreach(var order in Orders) { 
            result.AppendLine("\t\t" + order); 
         } 
			
         return result.ToString(); 
      } 
   }
   
   public class Location { 
      public virtual string Street { get; set; } 
      public virtual string City { get; set; } 
      public virtual string Province { get; set; } 
      public virtual string Country { get; set; } 
   }
   
   public enum CustomerCreditRating { 
      Excellent,
      VeryVeryGood, 
      VeryGood, 
      Good, 
      Neutral, 
      Poor, 
      Terrible 
   } 
}

以下是對映檔案 **Customer.hbm.xml**,其中 Customer 類對映到 Customer 表。

<?xml version = "1.0" encoding = "utf-8" ?> 
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo" 
   namespace = "NHibernateDemo"> 
	
   <class name = "Customer"> 
   
      <id name = "Id"> 
         <generator class = "guid.comb"/> 
      </id> 

      <property name = "FirstName"/> 
      <property name = "LastName"/> 
      <property name = "AverageRating"/> 
      <property name = "Points"/> 
      <property name = "HasGoldStatus"/> 
      <property name = "MemberSince" type = "UtcDateTime"/> 
      <property name = "CreditRating" type = "CustomerCreditRatingType"/>
      
      <component name = "Address"> 
         <property name = "Street"/> 
         <property name = "City"/> 
         <property name = "Province"/> 
         <property name = "Country"/> 
      </component> 

   </class> 
</hibernate-mapping>

我們還有一個 Order 類,以下是 **Order.cs** 檔案的實現。

using System; using Iesi.Collections.Generic;

namespace NHibernateDemo { 

   public class Order { 
      
      public virtual Guid Id { get; set; } 
      public virtual DateTime Ordered { get; set; } 
      public virtual DateTime? Shipped { get; set; }
      public virtual Location ShipTo { get; set; } 
      public virtual Customer Customer { get; set; }
      
      public override string ToString() { 
         return string.Format("Order Id: {0}", Id); 
      } 
   } 
}

多對一關係

我們還需要將 Order 類對映到資料庫中的 Order 表,因此以下是 **Order.hbm.xml** 檔案的實現。

<?xml version = "1.0" encoding = "utf-8" ?> 
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo" 
   namespace = "NHibernateDemo"> 

   <class name = "Order" table = "`Order`"> 
   
      <id name = "Id"> 
         <generator class = "guid.comb"/> 
      </id> 
		
      <property name = "Ordered"/> 
      <property name = "Shipped"/> 
   
      <component name = "ShipTo"> 
         <property name = "Street"/> 
         <property name = "City"/> 
         <property name = "Province"/> 
         <property name = "Country"/> 
      </component> 

      <!--<many-to-one name = "Customer" column = "CustomerId" cascade =
         "save-update"/>--> 
			
   </class> 
</hibernate-mapping>

一對多關係

在這裡,我們將研究一對多關係,在這種情況下,客戶和訂單之間存在一對多關係。我們在這裡有我們的客戶,我們正在建立一個新的客戶,您可以看到該集合使用以下訂單對進行初始化。

private static Customer CreateCustomer() { 
   
   var customer = new Customer { 
      FirstName = "John", 
      LastName = "Doe", 
      Points = 100, 
      HasGoldStatus = true, 
      MemberSince = new DateTime(2012, 1, 1), 
      CreditRating = CustomerCreditRating.Good, 
      AverageRating = 42.42424242, 
      Address = CreateLocation() 
   }; 
   
   var order1 = new Order { 
      Ordered = DateTime.Now 
   }; 
   
   customer.AddOrder(order1); 
   var order2 = new Order { 
      Ordered = DateTime.Now.AddDays(-1), 
      Shipped = DateTime.Now, 
      ShipTo = CreateLocation() 
   }; 
   
   customer.AddOrder(order2); 
   return customer; 
}

因此,我們將建立一個新客戶並儲存它,儲存後,我們將找到 ID,然後在 Main 方法中的另一個會話中重新載入它,如下面的程式所示。

private static void Main() { 

   var cfg = ConfigureNHibernate(); 
   var sessionFactory = cfg.BuildSessionFactory();
   
   Guid id; 
   using(var session = sessionFactory.OpenSession()) 
	
   using(var tx = session.BeginTransaction()) { 
      var newCustomer = CreateCustomer(); 
      Console.WriteLine("New Customer:"); 
      Console.WriteLine(newCustomer);
      session.Save(newCustomer); 
      id = newCustomer.Id; 
      tx.Commit(); 
   }
   
   using(var session = sessionFactory.OpenSession()) 
	
   using(var tx = session.BeginTransaction()) { 
      var reloaded = session.Load<Customer>(id); 
      Console.WriteLine("Reloaded:"); 
      Console.WriteLine(reloaded); 
      tx.Commit(); 
   }
   
   Console.WriteLine("Press <ENTER> to exit..."); 
   Console.ReadLine(); 
}

以下是完整的 **Program.cs** 檔案實現。

using System; 
using System.Data; 
using System.Linq; 
using System.Reflection; 

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cfg; 
using NHibernate.Dialect; 
using NHibernate.Driver; 
using NHibernate.Linq;

namespace NHibernateDemo {
 
   internal class Program { 
	
      private static void Main() { 
		
         var cfg = ConfigureNHibernate(); 
         var sessionFactory = cfg.BuildSessionFactory();
         
         Guid id; 
         using(var session = sessionFactory.OpenSession())
			
         using(var tx = session.BeginTransaction()) { 
            var newCustomer = CreateCustomer(); 
            Console.WriteLine("New Customer:"); 
            Console.WriteLine(newCustomer); 
            session.Save(newCustomer); 
            id = newCustomer.Id; 
            tx.Commit(); 
         } 
         
         using(var session = sessionFactory.OpenSession()) 
			
         using(var tx = session.BeginTransaction()) { 
            var reloaded = session.Load<Customer>(id); 
            Console.WriteLine("Reloaded:"); 
            Console.WriteLine(reloaded); 
            tx.Commit();
         }
         
         Console.WriteLine("Press <ENTER> to exit..."); 
         Console.ReadLine();
      }
      
      private static Customer CreateCustomer() { 
		
         var customer = new Customer { 
            FirstName = "John", 
            LastName = "Doe", 
            Points = 100, 
            HasGoldStatus = true, 
            MemberSince = new DateTime(2012, 1, 1), 
            CreditRating = CustomerCreditRating.Good, 
            AverageRating = 42.42424242, 
            Address = CreateLocation() 
         }; 
         
         var order1 = new Order { 
            Ordered = DateTime.Now
         }; 
         
         customer.AddOrder(order1); 
			
         var order2 = new Order { 
            Ordered = DateTime.Now.AddDays(-1), 
            Shipped = DateTime.Now, 
            ShipTo = CreateLocation() 
         }; 
         
         customer.AddOrder(order2); 
         return customer; 
      } 
      
      private static Location CreateLocation() { 
		
         return new Location { 
            Street = "123 Somewhere Avenue", 
            City = "Nowhere", 
            Province = "Alberta", 
            Country = "Canada" 
         }; 
      } 
      
      private static Configuration ConfigureNHibernate() { 
         NHibernateProfiler.Initialize(); 
         var cfg = new Configuration(); 
         
         cfg.DataBaseIntegration(x =&ht; { 
            x.ConnectionStringName = "default"; 
            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect>(); 
            x.IsolationLevel = IsolationLevel.RepeatableRead; 
            x.Timeout = 10; x.BatchSize = 10; 
         }); 
         
         cfg.SessionFactory().GenerateStatistics();
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         return cfg; 
      } 
   } 
}

當您執行此應用程式時,您將看到以下輸出。

New Customer:

John Doe (00000000-0000-0000-0000-000000000000)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Unspecified)
   CreditRating: Good
   AverageRating: 42.42424242

   Orders:
      Order Id: 00000000-0000-0000-0000-000000000000
      Order Id: 00000000-0000-0000-0000-000000000000

Reloaded:
John Doe (9b0fcf10-83f6-4f39-bda5-a5b800ede2ba)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
Press <ENTER> to exit...

如您所見,最初客戶有 2 個訂單,但當我們重新載入它時,沒有看到任何訂單。如果您檢視 **customer.hbm.xml** 檔案,您可以在此處看到我們沒有對映實際的訂單集合。因此,NHibernate 對此一無所知。讓我們繼續新增它。

<?xml version = "1.0" encoding = "utf-8" ?> 
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" 
   assembly = "NHibernateDemo" namespace = "NHibernateDemo">
	
   <class name = "Customer"> 
      
      <id name = "Id">
         <generator class = "guid.comb"/> 
      </id> 
      
      <property name = "FirstName"/>
      <property name = "LastName"/> 
      <property name = "AverageRating"/> 
      <property name = "Points"/> 
      <property name = "HasGoldStatus"/> 
      <property name = "MemberSince" type = "UtcDateTime"/> 
      <property name = "CreditRating" type = "CustomerCreditRatingType"/>
      
      <component name = "Address"> 
         <property name = "Street"/> 
         <property name = "City"/> 
         <property name = "Province"/> 
         <property name = "Country"/> 
      </component>
      
      <set name = "Orders" table = "`Order`"> 
         <key column = "CustomerId"/> 
         <one-to-many class = "Order"/> 
      </set> 
   
   </class> 
</hibernate-mapping>

這是一個集合,此集合的名稱為“Orders”,它儲存在一個名為 order 的表中。我們需要指定一個鍵,即外部索引鍵的名稱或查詢訂單。這些訂單透過客戶 ID 識別或屬於客戶。然後我必須注意這是一個一對多關係,它與訂單類相關。

我們還需要稍微修改一下 Main 方法,將新的客戶訂單儲存到資料庫中,如下面的程式所示。

private static void Main() { 

   var cfg = ConfigureNHibernate(); 
   var sessionFactory = cfg.BuildSessionFactory();
   
   Guid id; 
   using(var session = sessionFactory.OpenSession()) 
	
   using(var tx = session.BeginTransaction()) {
      var newCustomer = CreateCustomer(); 
      Console.WriteLine("New Customer:"); 
      Console.WriteLine(newCustomer);
      session.Save(newCustomer); 
      
      foreach (var order in newCustomer.Orders) { 
         session.Save(order); 
      } 
		
      id = newCustomer.Id; 
      tx.Commit(); 
   }
   
   using(var session = sessionFactory.OpenSession()) 
	
   using(var tx = session.BeginTransaction()) { 
      var reloaded = session.Load<Customer>(id); 
      Console.WriteLine("The orders were ordered by: "); 
      
      foreach (var order in reloaded.Orders) { 
         Console.WriteLine(order.Customer); 
      } 
		
      tx.Commit(); 
   }
   Console.WriteLine("Press <ENTER> to exit..."); Console.ReadLine();
}

我們還指定了哪個客戶訂購了特定產品。因此,我們需要建立一個多對一關係,將該訂單關聯回該客戶。

所以讓我們進入Order.hbm.xml檔案並新增一個多對一關係,然後命名客戶欄位和包含客戶 ID 的列。

<?xml version = "1.0" encoding = "utf-8" ?> 
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo"
   namespace = "NHibernateDemo"> 
	
   <class name = "Order" table = "`Order`">
      
      <id name = "Id"> 
         <generator class = "guid.comb"/> 
      </id>
      
      <property name = "Ordered"/> 
      <property name = "Shipped"/> 
      
      <component name = "ShipTo"> 
         <property name = "Street"/> 
         <property name = "City"/> 
         <property name = "Province"/> 
         <property name = "Country"/> 
      </component> 

      <many-to-one name = "Customer" column = "CustomerId"/> 
		
   </class> 
</hibernate-mapping>

讓我們再次執行此應用程式,現在您將看到以下輸出。

New Customer:
John Doe (00000000-0000-0000-0000-000000000000)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Unspecified)
   CreditRating: Good
   AverageRating: 42.42424242

   Orders:
      Order Id: 00000000-0000-0000-0000-000000000000
      Order Id: 00000000-0000-0000-0000-000000000000

Reloaded:
John Doe (660a6f29-650e-4380-99e0-a5b800febbde)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: 57314deb-e023-4e55-ac1e-a5b800febbe3
      Order Id: fc065683-d5f5-484b-ae42-a5b800febbe3

The orders were ordered by:
John Doe (660a6f29-650e-4380-99e0-a5b800febbde)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: 57314deb-e023-4e55-ac1e-a5b800febbe3
      Order Id: fc065683-d5f5-484b-ae42-a5b800febbe3

John Doe (660a6f29-650e-4380-99e0-a5b800febbde)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: 57314deb-e023-4e55-ac1e-a5b800febbe3
      Order Id: fc065683-d5f5-484b-ae42-a5b800febbe3
		
Press <ENTER> to exit...

NHibernate - 集合對映

在本章中,我們將介紹如何表示集合。在 NHibernate 中,我們可以使用不同型別的集合,例如:

  • 列表
  • 集合

現在,從 .NET 的角度來看,我們通常處理列表或非常簡單的資料結構,例如列表、字典。.NET 沒有各種各樣的集合型別。那麼為什麼 NHibernate 需要所有這些不同的型別呢?這實際上回到了資料庫。

列表

  • 列表是有序的元素集合,這些元素不一定唯一。

  • 我們可以使用IList <T>進行對映。

  • 因此,雖然我們可能通常有一個地址列表,並且從應用程式的角度來看,我們知道這些元素是唯一的,但是列表中沒有任何內容可以阻止我們在該列表中插入重複元素。

集合

  • 集合是唯一元素的無序集合。如果您嘗試將 2 個重複元素插入集合中,它將丟擲異常。

  • NHibernate 中對此沒有具體說明。

  • 這只是一個方便的方法,可以使用泛型集合實現。如果您使用的是 .NET 4,則可以使用新的HashSet <T>來表示這些集合,但在大多數 NHibernate 應用程式中,我們將其表示為 ISet。

  • 它是無序的,如果您從資料庫中提取地址列表或訂單列表,除非您放入特定的 Order by 子句,否則您不知道它們的順序。

  • 因此,通常,您從資料庫中提取的資料是集合。

  • 它們是唯一元素的無序集合。

  • 我們在資料庫世界中會看到的另一個常見集合是包,它就像集合一樣,除了它可以包含重複元素。

  • 在 .NET 世界中,我們將其表示為 IList。

集合可能是最常見的,但您也會看到列表和包,具體取決於您的應用程式。讓我們看一下上一章中customer.hbm.xml檔案,其中定義了 Set 訂單。

<?xml version = "1.0" encoding = "utf-8" ?> 
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo" 
   namespace = "NHibernateDemo"> 
	
   <class name = "Customer"> 
      
      <id name = "Id"> 
         <generator class = "guid.comb"/> 
      </id> 
   
      <property name = "FirstName"/> 
      <property name = "LastName"/> 
      <property name = "AverageRating"/> 
      <property name = "Points"/> 
      <property name = "HasGoldStatus"/> 
      <property name = "MemberSince" type = "UtcDateTime"/> 
      <property name = "CreditRating" type = "CustomerCreditRatingType"/>
      
      <component name = "Address"> 
         <property name = "Street"/> 
         <property name = "City"/> 
         <property name = "Province"/> 
         <property name = "Country"/> 
      </component>
      
      <set name = "Orders" table = "`Order`"> 
         <key column = "CustomerId"/> 
         <one-to-many class = "Order"/> 
      </set> 
   
   </class> 
</hibernate-mapping>

如您所見,我們將訂單集合對映為集合。請記住,集合是唯一元素的無序集合。

現在,如果您檢視 Customer 類,您將看到 Orders 屬性使用 ISet 定義,如下面的程式所示。

public virtual ISet<Order> Orders { get; set; }

現在,當此應用程式執行時,您將看到以下輸出。

New Customer:
John Doe (00000000-0000-0000-0000-000000000000)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Unspecified)
   CreditRating: Good
   AverageRating: 42.42424242

   Orders:
      Order Id: 00000000-0000-0000-0000-000000000000
      Order Id: 00000000-0000-0000-0000-000000000000

Reloaded:
John Doe (1f248133-b50a-4ad7-9915-a5b8017d0ff1)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: c41af8f2-7124-42a7-91c5-a5b8017d0ff6
      Order Id: 657f6bb0-1f42-45fc-8fc7-a5b8017d0ff7

The orders were ordered by:
John Doe (1f248133-b50a-4ad7-9915-a5b8017d0ff1)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: c41af8f2-7124-42a7-91c5-a5b8017d0ff6
      Order Id: 657f6bb0-1f42-45fc-8fc7-a5b8017d0ff7

John Doe (1f248133-b50a-4ad7-9915-a5b8017d0ff1)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: c41af8f2-7124-42a7-91c5-a5b8017d0ff6
      Order Id: 657f6bb0-1f42-45fc-8fc7-a5b8017d0ff7
		
Press <ENTER> to exit...

如果集合中的專案不需要唯一,如果您可以在此集合中多次出現具有相同主鍵的多個訂單,那麼最好將其對映為包,如下面的程式所示。

<bag name = "Orders" table = "`Order`"> 
   <key column = "CustomerId"/> 
   <one-to-many class = "Order"/> 
</bag>

現在,如果您執行此應用程式,您將收到異常,因為如果我們檢視 customer 類,您會注意到訂單在 C# 程式碼中被標記為 ISet。

因此,我們還需要將其更改為 IList,然後在此處,我們需要將建構函式中的 HashSet 更改為 List。

public class Customer { 
   public Customer() { 
      MemberSince = DateTime.UtcNow; 
      Orders = new List<Order>(); 
   } 
	
   public virtual Guid Id { get; set; } 
   public virtual string FirstName { get; set; } 
   public virtual string LastName { get; set; } 
   public virtual double AverageRating { get; set; } 
   public virtual int Points { get; set; } 
	
   public virtual bool HasGoldStatus { get; set; } 
   public virtual DateTime MemberSince { get; set; } 
   public virtual CustomerCreditRating CreditRating { get; set; } 
   public virtual Location Address { get; set; }
   public virtual IList<Order> Orders { get; set; }
   public virtual void AddOrder(Order order) { Orders.Add(order); order.Customer = this; }
   
   public override string ToString() { 
      var result = new StringBuilder(); 
		
      result.AppendFormat("{1} {2} ({0})\r\n\tPoints: {3}\r\n\tHasGoldStatus:
         {4}\r\n\tMemberSince: {5} ({7})\r\n\tCreditRating: {6}\r\n\tAverageRating:
         {8}\r\n", Id, FirstName, LastName, Points, HasGoldStatus, MemberSince,
         CreditRating, MemberSince.Kind, AverageRating); result.AppendLine("\tOrders:"); 
      
      foreach(var order in Orders) { 
         result.AppendLine("\t\t" + order); 
      } 
		
      return result.ToString(); 
   } 
}

執行應用程式後,您將看到相同的行為。但是,現在我們可以使訂單在同一個集合中多次出現。

John Doe (00000000-0000-0000-0000-000000000000)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Unspecified)
   CreditRating: Good
   AverageRating: 42.42424242

   Orders:
      Order Id: 00000000-0000-0000-0000-000000000000
      Order Id: 00000000-0000-0000-0000-000000000000

Reloaded:
John Doe (fbde48f5-d620-4d1c-9a7f-a5b8017c3280)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: 6dd7dbdb-354f-4c82-9c39-a5b8017c3286
      Order Id: 9b3e2441-a81b-404d-9aed-a5b8017c3287

The orders were ordered by:
John Doe (fbde48f5-d620-4d1c-9a7f-a5b8017c3280)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: 6dd7dbdb-354f-4c82-9c39-a5b8017c3286
      Order Id: 9b3e2441-a81b-404d-9aed-a5b8017c3287

John Doe (fbde48f5-d620-4d1c-9a7f-a5b8017c3280)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: 6dd7dbdb-354f-4c82-9c39-a5b8017c3286
      Order Id: 9b3e2441-a81b-404d-9aed-a5b8017c3287
		
Press <ENTER> to exit...

NHibernate - 級聯操作

在本章中,我們將介紹如何使用 Cascade 功能。如果您有一組或多組專案或兩個類(例如我們的客戶和訂單)之間的關係,並且存在外部索引鍵關係。如果我們刪除客戶,預設情況下,NHibernate 不會對子物件執行任何操作,因此屬於該客戶的物件可能會變成孤立訂單。

  • 我們也可能違反外部索引鍵約束,因此我們可以使用級聯的概念。

  • 預設情況下,NHibernate 不會將操作級聯到子物件。

  • 這樣做的原因是,您可能存在這樣的關係:客戶擁有預設送貨地址,並且該送貨地址與許多不同的客戶共享。

  • 因此,您不希望一定級聯該關係,因為其他客戶仍在引用它。

  • 因此,級聯的整個概念是告訴 NHibernate 如何處理其子實體。

級聯有不同的選項,如下所示:

  • none - 這是預設值,表示不級聯。

  • all - 將級聯儲存、更新和刪除操作。

  • save-update - 它將級聯儲存和更新操作。

  • delete - 它將級聯刪除操作。

  • all-delete-orphan - 這是一個特殊的選項,非常常用,與 All 相同,除了它會查詢 Delete-orphan 行並將其刪除。

您可以在hbm.xml檔案中指定預設值,因此您可以在該 Hibernate 對映元素上提供預設級聯,或者也可以為特定的集合和關係(例如多對一關係)指定預設級聯。

讓我們看一下簡單的示例級聯,讓我們修復程式中的問題,我們需要手動將儲存操作級聯到訂單,如下面的程式碼所示。

using(var session = sessionFactory.OpenSession()) 

using(var tx = session.BeginTransaction()) { 
   var newCustomer = CreateCustomer(); 
   Console.WriteLine("New Customer:"); 
   Console.WriteLine(newCustomer); 
   session.Save(newCustomer); 
	
   foreach (var order in newCustomer.Orders) { 
      session.Save(order); 
   } 
	
   id = newCustomer.Id; 
   tx.Commit(); 
}

在上面的程式碼片段中,您可以看到我們正在手動儲存客戶的所有訂單。現在讓我們刪除手動級聯程式碼,其中儲存了所有訂單。

using(var session = sessionFactory.OpenSession())
 
using(var tx = session.BeginTransaction()) { 
   var newCustomer = CreateCustomer(); 
   Console.WriteLine("New Customer:"); 
   Console.WriteLine(newCustomer);
	
   session.Save(newCustomer); 
   id = newCustomer.Id; 
   tx.Commit(); 
}

我們需要在customer.hbm.xml中指定級聯選項。

<?xml version = "1.0" encoding = "utf-8" ?> 
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo"
   namespace = "NHibernateDemo"> 
	
   <class name = "Customer"> 
   
      <id name = "Id"> 
         <generator class = "guid.comb"/> 
      </id> 
      
      <property name = "FirstName"/> 
      <property name = "LastName"/> 
      <property name = "AverageRating"/> 
      <property name = "Points"/> 
      <property name = "HasGoldStatus"/> 
      <property name = "MemberSince" type = "UtcDateTime"/> 
      <property name = "CreditRating" type = "CustomerCreditRatingType"/>
      
      <component name = "Address"> 
         <property name = "Street"/> 
         <property name = "City"/> 
         <property name = "Province"/> 
         <property name = "Country"/> 
      </component>
      
      <set name = "Orders" table = "`Order`" cascade = "all-delete-orphan"> 
         <key column = "CustomerId"/> 
         <one-to-many class = "Order"/> 
      </set> 
   
   </class> 
</hibernate-mapping>
  • 現在,訂單完全屬於客戶。因此,如果客戶從資料庫中刪除,我們的應用程式將希望刪除所有這些訂單,包括可能已成為孤立訂單的任何訂單。

  • 它最終會執行刪除操作。透過此操作,它會說刪除 order 表中 customer ID 等於您要刪除的客戶的所有行。

  • 因此,您實際上可以級聯這些刪除操作。因此,使用All,它將執行儲存、更新和刪除操作。

現在,當您執行此應用程式時,您將看到以下輸出。

New Customer:
John Doe (00000000-0000-0000-0000-000000000000)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Unspecified)
   CreditRating: Good
   AverageRating: 42.42424242

   Orders:
      Order Id: 00000000-0000-0000-0000-000000000000
      Order Id: 00000000-0000-0000-0000-000000000000

Reloaded:
John Doe (10b2a3d7-7fcf-483c-b1da-a5bb00b8512e)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: e6680e30-5b3b-4efa-b017-a5bb00b85133
      Order Id: b03858e7-8c36-4555-8878-a5bb00b85134

The orders were ordered by:
John Doe (10b2a3d7-7fcf-483c-b1da-a5bb00b8512e)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: e6680e30-5b3b-4efa-b017-a5bb00b85133
      Order Id: b03858e7-8c36-4555-8878-a5bb00b85134

John Doe (10b2a3d7-7fcf-483c-b1da-a5bb00b8512e)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: e6680e30-5b3b-4efa-b017-a5bb00b85133
      Order Id: b03858e7-8c36-4555-8878-a5bb00b85134
		
Press <ENTER> to exit...

如您所見,我們已從程式中刪除了手動級聯的程式碼,並且我們的應用程式仍在工作。

因此,根據您的關係,您可能希望級聯這些關係。現在,讓我們看一下不同的級聯關係。讓我們轉到Order.hbm.xml檔案,我們可以級聯該多對一關係。

<?xml version = "1.0" encoding = "utf-8" ?> 
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo"
   namespace = "NHibernateDemo"> 
   
   <class name = "Order" table = "`Order`"> 
	
      <id name = "Id"> 
         <generator class = "guid.comb"/> 
      </id> 

      <property name = "Ordered"/> 
      <property name = "Shipped"/> 
      
      <component name = "ShipTo"> 
         <property name = "Street"/> 
         <property name = "City"/> 
         <property name = "Province"/> 
         <property name = "Country"/> 
      </component> 
      
      <many-to-one name = "Customer" column = "CustomerId" cascade = "save-update"/>
		
   </class> 
</hibernate-mapping>

因此,如果我們建立一個新訂單,並且有一個新的客戶附加到它,並且我們說儲存該訂單,我們可能希望級聯它。但是,我們可能不希望做的一件事是,如果刪除訂單,則刪除相應的客戶。

因此,在這裡,我們希望執行儲存更新操作,因此使用 save-update,它將級聯對該客戶的任何儲存或更新操作。因此,如果我們獲得一個新客戶或如果我們正在更改客戶,它將級聯該操作。如果它是刪除操作,它不會從資料庫中刪除該客戶。

因此,再次執行我們的應用程式,一切仍然按預期工作。

New Customer:
John Doe (00000000-0000-0000-0000-000000000000)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Unspecified)
   CreditRating: Good
   AverageRating: 42.42424242

   Orders:
      Id: 00000000-0000-0000-0000-000000000000
      Order Id: 00000000-0000-0000-0000-000000000000

Reloaded:
John Doe (10b2a3d7-7fcf-483c-b1da-a5bb00b8512e)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: e6680e30-5b3b-4efa-b017-a5bb00b85133
      Order Id: b03858e7-8c36-4555-8878-a5bb00b85134

The orders were ordered by:
John Doe (10b2a3d7-7fcf-483c-b1da-a5bb00b8512e)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: e6680e30-5b3b-4efa-b017-a5bb00b85133
      Order Id: b03858e7-8c36-4555-8878-a5bb00b85134
      John Doe (10b2a3d7-7fcf-483c-b1da-a5bb00b8512e)
		
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: e6680e30-5b3b-4efa-b017-a5bb00b85133
      Order Id: b03858e7-8c36-4555-8878-a5bb00b85134
		
Press <ENTER> to exit...

現在,您應該檢視您的應用程式,請記住預設值為 None,您必須考慮您的實體以及它們之間的關係,以確定每個實體以及資料庫中每個關係的適當級聯。

NHibernate - 延遲載入

在本章中,我們將介紹延遲載入功能。它是一個完全不同的概念,預設情況下,NHibernate 沒有延遲載入,例如,如果您載入客戶,它不會載入所有訂單。

  • 訂單集合將按需載入。

  • 任何關聯(無論是多對一關係還是集合)都預設延遲載入,它需要Open ISession

  • 如果您已關閉會話,或者如果您已提交事務,則可能會收到延遲載入異常,即它無法載入這些其他物件。

  • 您必須注意延遲載入以及您實際需要多少資料。

  • 您可以關閉整個關聯的延遲載入,或者可以將 lazy 設定為 false,或者還可以指定獲取策略。

以下是Program.cs檔案實現。

using System; 
using System.Data; 
using System.Linq; 
using System.Reflection; 

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cfg; 
using NHibernate.Dialect; 
using NHibernate.Driver; 
using NHibernate.Linq;

namespace NHibernateDemo { 

   internal class Program { 
	
      private static void Main() { 
		
         var cfg = ConfigureNHibernate(); 
         var sessionFactory = cfg.BuildSessionFactory();
         
         Guid id; 
         using(var session = sessionFactory.OpenSession()) 
			
         using(var tx = session.BeginTransaction()) {
            var newCustomer = CreateCustomer(); 
            Console.WriteLine("New Customer:"); 
            Console.WriteLine(newCustomer); 
            session.Save(newCustomer); 
            id = newCustomer.Id; 
            tx.Commit(); 
         }
         
         using(var session = sessionFactory.OpenSession()) 
			
         using(var tx = session.BeginTransaction()) { 
            var reloaded = session.Load<Customer>(id); 
            Console.WriteLine("Reloaded:"); 
            Console.WriteLine(reloaded); 
            Console.WriteLine("The orders were ordered by: "); 
            
            foreach (var order in reloaded.Orders) { 
               Console.WriteLine(order.Customer); 
            } 
				
            tx.Commit(); 
         }
			
         Console.WriteLine("Press <ENTER> to exit..."); 
         Console.ReadLine(); 
      }
		
      private static Customer CreateCustomer() { 
         
         var customer = new Customer { 
            FirstName = "John", 
            LastName = "Doe", 
            Points =100, 
            HasGoldStatus = true, 
            MemberSince = new DateTime(2012, 1, 1),
            CreditRating = CustomerCreditRating.Good,
            AverageRating = 42.42424242, 
            Address = CreateLocation() 
         }; 
			
         var order1 = new Order { Ordered = DateTime.Now }; 
         customer.AddOrder(order1); 
         
         var order2 = new Order { 
            Ordered = DateTime.Now.AddDays(-1), 
            Shipped = DateTime.Now, 
            ShipTo = CreateLocation() 
         }; 
			
         customer.AddOrder(order2); return customer; 
      }
		
      private static Location CreateLocation() { 
         return new Location { 
            Street = "123 Somewhere Avenue", 
            City = "Nowhere", 
            Province = "Alberta", 
            Country = "Canada" 
         }; 
      }
		
      private static Configuration ConfigureNHibernate() { 
		
         NHibernateProfiler.Initialize(); 
         var cfg = new Configuration(); 
         
         cfg.DataBaseIntegration(x => { 
            x.ConnectionStringName = "default"; 
            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect<(); 
            x.IsolationLevel = IsolationLevel.RepeatableRead; 
            x.Timeout = 10;
            x.BatchSize = 10;
         }); 
         
         cfg.SessionFactory().GenerateStatistics();
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         return cfg; 
      } 
   } 
}

要理解這一點,讓我們執行應用程式並檢視 NHibernate Profiler。

Customers Collection

如您所見,我們有 Select From Customer,給定特定的客戶 ID,然後我們還有另一個 Select From Orders 表,當它實際訪問該客戶的集合時。

因此,我們對資料庫進行了 2 次往返。現在,有時我們希望對此進行最佳化。為此,讓我們轉到customer.hbm.xml檔案並新增獲取策略,並要求它執行連接獲取。

<?xml version = "1.0" encoding = "utf-8" ?> 
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo"
   namespace = "NHibernateDemo"> 
   
   <class name = "Customer"> 
	
      <id name = "Id"> 
         <generator class = "guid.comb"/> 
      </id> 
   
      <property name = "FirstName"/> 
      <property name = "LastName"/> 
      <property name = "AverageRating"/> 
      <property name = "Points"/> 
      <property name = "HasGoldStatus"/> 
      <property name = "MemberSince" type = "UtcDateTime"/> 
      <property name = "CreditRating" type = "CustomerCreditRatingType"/>
      
      <component name = "Address"> 
         <property name = "Street"/> 
         <property name = "City"/> 
         <property name = "Province"/> 
         <property name = "Country"/> 
      </component>
      
      <set name = "Orders" table = "`Order`" cascade = "all-delete-orphan" 
         fetch = "join"> 
         <key column = "CustomerId"/> 
         <one-to-many class = "Order"/> 
      </set> 
   
   </class> 
</hibernate-mapping>

如您所見,我們沒有更改應用程式中的任何程式碼,我們只是在customer.hbm.xml中添加了一個獲取策略。讓我們再次執行此應用程式,它的行為仍然完全相同。讓我們看看 NHibernate Profiler。

Customer Profile
  • 之前,程式對資料庫進行了兩次往返,現在,它只有一次,這是因為它在這裡執行了左外部連線。

  • 我們可以看到,它正在根據客戶 ID 在客戶表和訂單表之間執行左外部連線,因此,它能夠一次載入所有這些資訊。

  • 我們節省了 1 次對資料庫的往返。

  • 缺點是客戶資訊將在兩行上重複,這是 SQL 左外部連線的工作方式。

  • 因此,使用獲取策略,我們提取了更多資料,並節省了一次往返。

您也可以在查詢級別執行此操作,因此讓我們轉到Program.cs檔案並檢視重新載入的簡單示例。

using(var session = sessionFactory.OpenSession()) 

using(var tx = session.BeginTransaction()) { 
   //var query = from customer in session.Query<Customer>() 
   // select customer; 
   //var reloaded = query.Fetch(x => x.Orders).ToList();
	
   var reloaded = session.Load<Customer>(id); 
   Console.WriteLine("Reloaded:"); 
   Console.WriteLine(reloaded); 
   Console.WriteLine("The orders were ordered by: "); 
   
   foreach (var order in reloaded.Orders) { 
      Console.WriteLine(order.Customer); 
   } 
	
   tx.Commit(); 
}

在這裡,我們正在透過客戶進行載入。現在讓我們將其更改為查詢,我們將使用連結查詢,如下面的程式碼所示。

using(var session = sessionFactory.OpenSession()) 

using(var tx = session.BeginTransaction()) {
   var query = from customer in session.Query<Customer>() 
   where customer.Id == id select customer; 
   var reloaded = query.Fetch(x => x.Orders).ToList().First();
	
   Console.WriteLine("Reloaded:"); 
   Console.WriteLine(reloaded); 
	
   tx.Commit();
}

讓我們還從customer.hbm.xml檔案中刪除獲取策略。

<?xml version = "1.0" encoding = "utf-8" ?> 
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo"
   namespace = "NHibernateDemo"> 

   <class name = "Customer"> 
	
      <id name = "Id"> 
         <generator class = "guid.comb"/> 
      </id> 
   
      <property name = "FirstName"/> 
      <property name = "LastName"/>
      <property name = "AverageRating"/> 
      <property name = "Points"/> 
      <property name = "HasGoldStatus"/> 
      <property name = "MemberSince" type = "UtcDateTime"/> 
      <property name = "CreditRating" type = "CustomerCreditRatingType"/>
   
      <component name = "Address"> 
         <property name = "Street"/> 
         <property name = "City"/> 
         <property name = "Province"/> 
         <property name = "Country"/> 
      </component>
   
      <set name = "Orders" table = "`Order`" cascade = "all-delete-orphan"> 
         <key column = "CustomerId"/> 
         <one-to-many class = "Order"/> 
      </set> 
   
   </class> 
</hibernate-mapping>

讓我們再次執行此應用程式,您將看到以下輸出。

New Customer:
John Doe (00000000-0000-0000-0000-000000000000)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Unspecified)
   CreditRating: Good
   AverageRating: 42.42424242

   Orders:
      Order Id: 00000000-0000-0000-0000-000000000000
      Order Id: 00000000-0000-0000-0000-000000000000

Reloaded:
John Doe (6ebacd17-f9ba-4ad8-9817-a5bb01112a5a)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: 16a6596b-d56e-41c7-9681-a5bb01112a60
      Order Id: d41d615b-0f21-4032-81db-a5bb01112a61
		
Press <ENTER> to exit...

現在讓我們檢視 NHibernate Profiler,您可以看到我們再次執行了此渴望連接獲取,但這次是基於查詢。

Join Fetch

NHibernate - 反向關係

在本章中,我們將介紹另一個功能,即反向關係。這是一個有趣的選項,您將在集合上看到它,這些集合的反向等於 true,它也讓很多開發人員感到困惑。所以讓我們談談這個選項。要理解這一點,您確實需要考慮關係模型。假設您使用單個外部索引鍵進行雙向關聯。

  • 從關係型資料庫的角度來看,你有一個外部索引鍵,它同時表示客戶到訂單和訂單到客戶的關係。

  • 從面向物件模型的角度來看,你使用這些引用實現了單向關聯。

  • 沒有任何規定說兩個單向關聯在資料庫中就代表同一個雙向關聯。

  • 這裡的問題是,NHibernate 沒有足夠的資訊來知道 **customer.orders** 和 **order.customer** 在資料庫中代表的是同一種關係。

  • 我們需要提供 **inverse equals true** 作為提示,這是因為這些單向關聯使用了相同的資料。

  • 如果我們嘗試儲存這些有兩個引用的關係,NHibernate 會嘗試更新該引用兩次。

  • 它實際上會進行額外的資料庫往返操作,並且會對該外部索引鍵進行兩次更新。

  • inverse equals true 告訴 NHibernate 要忽略關係的哪一邊。

  • 當將其應用於集合端時,NHibernate 將始終從另一端,即從子物件端更新外部索引鍵。

  • 這樣,我們只需要對該外部索引鍵進行一次更新,並且不會對該資料進行額外的更新。

  • 這使我們能夠防止對該外部索引鍵進行重複更新,也有助於我們防止外部索引鍵違規。

讓我們看看 **customer.cs** 檔案,你會看到 **AddOrder** 方法,這裡的想法是,我們現在有了從訂單返回到客戶的反向指標,並且需要設定它。因此,當一個訂單被新增到一個客戶時,該客戶的反向指標會被設定,否則它將為 null,所以我們需要這樣做才能在物件圖中正確地將它們連線在一起。

using System; 
using System.Text; 
using Iesi.Collections.Generic;

namespace NHibernateDemo {
 
   public class Customer { 
      
      public Customer() {
         MemberSince = DateTime.UtcNow; Orders = new HashedSet<Order>();
      } 
      
      public virtual Guid Id { get; set; } 
      public virtual string FirstName { get; set; } 
      public virtual string LastName { get; set; } 
      public virtual double AverageRating { get; set; } 
      public virtual int Points { get; set; } 
      public virtual bool HasGoldStatus { get; set; } 
		
      public virtual DateTime MemberSince { get; set; } 
      public virtual CustomerCreditRating CreditRating { get; set; } 
      public virtual Location Address { get; set; }
      public virtual ISet<Order> Orders { get; set; }
      public virtual void AddOrder(Order order) { Orders.Add(order); order.Customer = this; }
      
      public override string ToString() { 
         var result = new StringBuilder(); 
			
         result.AppendFormat("{1} {2} ({0})\r\n\tPoints: {3}\r\n\tHasGoldStatus:
            {4}\r\n\tMemberSince: {5} ({7})\r\n\tCreditRating: {6}\r\n\tAverageRating:
            {8}\r\n", Id, FirstName, LastName, Points, HasGoldStatus, MemberSince,
            CreditRating, MemberSince.Kind, AverageRating);
         result.AppendLine("\tOrders:"); 
         
         foreach(var order in Orders) { 
            result.AppendLine("\t\t" + order); 
         } 
			
         return result.ToString(); 
      } 
   }
   
   public class Location { 
      public virtual string Street { get; set; } 
      public virtual string City { get; set; } 
      public virtual string Province { get; set; } 
      public virtual string Country { get; set; }
   } 
   
   public enum CustomerCreditRating { 
      Excellent, 
      VeryVeryGood, 
      VeryGood, 
      Good, 
      Neutral, 
      Poor, 
      Terrible 
   } 
}

以下是Program.cs檔案實現。

using System; 
using System.Data; 
using System.Linq; 
using System.Reflection; 

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cfg; 
using NHibernate.Dialect; 
using NHibernate.Driver; 
using NHibernate.Linq;

namespace NHibernateDemo { 

   internal class Program { 
	
      private static void Main() { 
		
         var cfg = ConfigureNHibernate(); 
         var sessionFactory = cfg.BuildSessionFactory();
         Guid id; 
         using(var session = sessionFactory.OpenSession()) 
         
         using(var tx = session.BeginTransaction()) { 
            var newCustomer = CreateCustomer(); 
            Console.WriteLine("New Customer:"); 
            Console.WriteLine(newCustomer); 
            session.Save(newCustomer); 
            id = newCustomer.Id;
            tx.Commit(); 
         }
         
         using(var session = sessionFactory.OpenSession())

         using(var tx = session.BeginTransaction()) { 
            var query = from customer in session.Query<Customer>() where
               customer.Id == id select customer; 
					
            var reloaded = query.Fetch(x => x.Orders).ToList().First();
            Console.WriteLine("Reloaded:"); Console.WriteLine(reloaded); 

            tx.Commit(); 
         }
			
         Console.WriteLine("Press <ENTER> to exit..."); 
         Console.ReadLine(); 
      }
      
      private static Customer CreateCustomer() { 
         var customer = new Customer { 
            FirstName = "John", 
            LastName = "Doe", 
            Points = 100, 
            HasGoldStatus = true, 
            MemberSince = new DateTime(2012, 1, 1), 
            CreditRating = CustomerCreditRating.Good, 
            AverageRating = 42.42424242, 
            Address = CreateLocation() 
         }; 
			
         var order1 = new Order { Ordered = DateTime.Now }; 
         
         customer.AddOrder(order1); var order2 = new Order {
            Ordered = DateTime.Now.AddDays(-1), 
            Shipped = DateTime.Now, 
            ShipTo = CreateLocation()
         }; 
			
         customer.AddOrder(order2); 
         return customer; 
      }
      
      private static Location CreateLocation() { 
         return new Location { 
            Street = "123 Somewhere Avenue", 
            City = "Nowhere", 
            Province = "Alberta", 
            Country = "Canada" 
         }; 
      }
      
      private static Configuration ConfigureNHibernate() { 
         NHibernateProfiler.Initialize(); 
         var cfg = new Configuration(); 
         
         cfg.DataBaseIntegration(x => { 
            x.ConnectionStringName = "default"; 
            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect>(); 
            x.IsolationLevel = IsolationLevel.RepeatableRead; 
            x.Timeout = 10; 
            x.BatchSize = 10; 
         }); 
         
         cfg.SessionFactory().GenerateStatistics();
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         return cfg; 
      } 
   } 
}

它會將資料儲存到資料庫中,然後重新載入它。現在讓我們執行你的應用程式並開啟 NHibernate Profiler,看看它是如何實際儲存資料的。

Inverse NHibernate Profiler

你會注意到我們有三組語句。第一個語句會插入客戶,該客戶的 ID 是 Guid,已高亮顯示。第二個語句是插入 orders 表。

Customer Id Guid

你會注意到相同的 Customer Id Guid 在那裡被設定,因此設定了該外部索引鍵。最後一個語句是更新,它會再次將外部索引鍵更新為相同的客戶 ID。

Customer Hbm

現在問題是客戶擁有訂單,訂單也擁有客戶,我們沒有告訴 NHibernate 它們實際上是同一種關係。我們透過將 inverse 設定為 true 來做到這一點。

所以讓我們轉到我們的 **customer.hbm.xml** 對映檔案,並將 inverse 設定為 true,如下面的程式碼所示。

<?xml version = "1.0" encoding = "utf-8" ?> 
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo"
   namespace = "NHibernateDemo"> 
   
   <class name = "Customer">
	
      <id name = "Id"> 
         <generator class = "guid.comb"/> 
      </id> 
      
      <property name = "FirstName"/> 
      <property name = "LastName"/> 
      <property name = "AverageRating"/> 
      <property name = "Points"/> 
      <property name = "HasGoldStatus"/> 
      <property name = "MemberSince" type = "UtcDateTime"/>
      <property name = "CreditRating" type = "CustomerCreditRatingType"/>
      
      <component name = "Address"> 
         <property name = "Street"/> 
         <property name = "City"/> 
         <property name = "Province"/> 
         <property name = "Country"/> 
      </component>
      
      <set name = "Orders" table = "`Order`" cascade = "all-delete-orphan" 
         inverse = "true"> 
         <key column = "CustomerId"/> 
         <one-to-many class = "Order"/> 
      </set> 
   
   </class> 
</hibernate-mapping>

儲存訂單時,它將從訂單端設定該外部索引鍵。現在讓我們再次執行此應用程式並開啟 NHibernate Profiler。

Foreign Key

如果我們看看它們是如何插入的,我們會得到在客戶中插入,以及在訂單中插入,但我們沒有重複更新外部索引鍵,因為它在訂單儲存時被更新了。

  • 現在,請注意,如果你只有一個單向關聯,並且是集合正在維護這種關係,那麼如果你將 inverse 設定為 true,則該外部索引鍵將永遠不會被設定,並且這些項的資料庫中的外部索引鍵將永遠不會被設定。

  • 如果你檢視 **Order.hbm.xml** 檔案中的多對一關係,並查詢 inverse,它實際上沒有 inverse 屬性。

  • 它始終從子項設定,但如果你有多對多集合,則可以從任一側設定。

NHibernate - Load/Get

在本章中,我們將介紹 Load 和 Get 功能的工作原理以及如何使用它們。這是 **ISession** 提供的兩個非常相似的 API,用於透過主鍵載入物件。

  • **Get** - 它將返回物件或 null。

  • **Load** - 它將返回物件或丟擲 **ObjectNotFoundException**。

現在,為什麼我們有這兩個不同的 API 呢?

Load

  • 這是因為 Load 可以更有效地最佳化資料庫往返次數。

  • Load 實際上返回一個代理物件,並且不需要在你發出 Load 呼叫時立即訪問資料庫。

  • 當你訪問該代理時,如果物件碰巧不在資料庫中,它可以在那時丟擲 ObjectNotFoundException。

Get

  • 相反,對於 Get,由於 CLR(公共語言執行時)和 NHibernate 的限制,它必須立即轉到資料庫,檢查物件是否存在,如果不存在則返回 null。

  • 它沒有延遲獲取資料庫往返操作到以後時間的選項,因為它無法返回代理物件,並且當用戶實際訪問它時,它將該代理物件替換為 null。

讓我們來看一個簡單的示例,其中你將看到這些是如何實際使用的以及 Get 和 Load 之間的區別。我們將繼續使用相同的領域類 **Customers** 和 **Orders**,以及上一章中相同的對映檔案。

在這個示例中,我們將首先使用 Get,如下面的程式所示。

using System; 
using System.Data; 
using System.Linq; 
using System.Reflection; 

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cfg; 
using NHibernate.Criterion; 
using NHibernate.Dialect; 
using NHibernate.Driver;
using NHibernate.Linq; 

namespace NHibernateDemo { 

   internal class Program { 
	
      private static void Main() { 
		
         var cfg = ConfigureNHibernate(); 
         var sessionFactory = cfg.BuildSessionFactory();
         using(var session = sessionFactory.OpenSession()) 
         
         using(var tx = session.BeginTransaction()) { 
            var id1 = Guid.Parse("4e97c816-6bce-11e1-b095-6cf049ee52be"); 
            var id2 = Guid.Parse("AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE");
				
            var customer1 = session.Get<Customer>(id1); 
            Console.WriteLine("Customer1 data"); 
            Console.WriteLine(customer1);
				
            var customer2 = session.Get<Customer>(id2); 
            Console.WriteLine("Customer2 data"); 
            Console.WriteLine(customer2); 
				
            tx.Commit(); 
         }
			
         Console.WriteLine("Press <ENTER> to exit..."); 
         Console.ReadLine(); 
      }
		
      private static Configuration ConfigureNHibernate() {
		
         NHibernateProfiler.Initialize(); 
         var cfg = new Configuration(); 
         
         cfg.DataBaseIntegration(x => { 
            x.ConnectionStringName = "default"; 
            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect>(); 
            x.IsolationLevel = IsolationLevel.RepeatableRead;
            x.Timeout = 10; 
            x.BatchSize = 10; 
         }); 
         
         cfg.SessionFactory().GenerateStatistics();
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         return cfg; 
      } 
   } 
}

你可以看到我們有兩個 **Guid** ID,第一個是有效的 ID,它是我們知道存在於資料庫中的客戶的 ID。而第二個 ID 不存在於資料庫中。這兩個 ID 作為引數傳遞給 **Get()** 方法,然後結果列印到控制檯。

當上述程式碼編譯並執行時,你將看到以下輸出。

Customer1 data
Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be)
   Points: 74
   HasGoldStatus: True
   MemberSince: 4/4/2009 12:00:00 AM (Utc)
   CreditRating: Neutral
   AverageRating: 0

Orders:
   Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be
   Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be
   Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be
   Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be
	
Customer2 data
Press <ENTER> to exit...

你可以看到 Customer1 資料被打印出來,但 Customer2 資料為空,這是因為 Customer2 記錄在資料庫中不可用。

當你再次執行你的應用程式時,我們可以在提交語句之前設定斷點,然後讓我們在 Watch 視窗中檢視這兩個客戶。

Customer2 Record

你可以看到 Customer1 資料可用,而 Customer2 為 null,並且兩者型別均為 **NHibernateDemo.Customer**。

現在讓我們在同一個示例中使用 Load 方法代替 Get,如下面的程式碼所示。

using System; 
using System.Data; 
using System.Linq; 
using System.Reflection; 

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cfg; 
using NHibernate.Criterion; 
using NHibernate.Dialect; 
using NHibernate.Driver;
using NHibernate.Linq; 

namespace NHibernateDemo { 

   internal class Program { 
	
      private static void Main() { 
		
         var cfg = ConfigureNHibernate(); 
         var sessionFactory = cfg.BuildSessionFactory();
         using(var session = sessionFactory.OpenSession()) 
         
         using(var tx = session.BeginTransaction()) { 
            var id1 = Guid.Parse("4e97c816-6bce-11e1-b095-6cf049ee52be"); 
            var id2 = Guid.Parse("AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE");
				
            var customer1 = session.Load<Customer>(id1); 
            Console.WriteLine("Customer1 data"); 
            Console.WriteLine(customer1);
				
            var customer2 = session.Load<Customer>(id2); 
            Console.WriteLine("Customer2 data"); 
            Console.WriteLine(customer2); 
				
            tx.Commit(); 
         }
			
         Console.WriteLine("Press <ENTER> to exit..."); 
         Console.ReadLine(); 
      }
		
      private static Configuration ConfigureNHibernate() { 
		
         NHibernateProfiler.Initialize(); 
         var cfg = new Configuration(); 
         
         cfg.DataBaseIntegration(x => { 
            x.ConnectionStringName = "default"; 
            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect>(); 
            x.IsolationLevel = IsolationLevel.RepeatableRead; 
            x.Timeout = 10;
            x.BatchSize = 10; 
         }); 
			
         cfg.SessionFactory().GenerateStatistics();
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         return cfg; 
      } 
   } 
}

現在讓我們執行此示例,你將看到丟擲以下異常,如螢幕截圖所示。

Exception Nhibernate Profile

現在如果你檢視 Watch 視窗,你會看到兩個物件的型別都是 customer proxy。你還會在控制檯視窗中看到 Customer1 的相同資料。

Customer1 data
Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be)
   Points: 74
   HasGoldStatus: True
   MemberSince: 4/4/2009 12:00:00 AM (Utc)
   CreditRating: Neutral
   AverageRating: 0

   Orders:
      Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be 
		
Customer2 data

NHibernate - LINQ

在本章中,我們將介紹另一個常見的 API,即 NHibernate LINQ 提供程式。它透過 ISession 上的擴充套件方法訪問,簽名為 **Query <T>**。在使用 LINQ 時,有兩種型別的語法 -

  • 查詢連結語法
  • 查詢理解語法

查詢連結語法

你可以使用方法鏈語法訪問資料庫中的任何記錄,如下面的程式所示。

var customer = session.Query<Customer>() .Where(c => c.FirstName == "Laverne")
  • 你可以看到我們有查詢,還有 WHERE 子句,你可以新增額外的 WHERE 子句,以及類似的 select 子句。

  • 這是你可以在普通 LINQ 中使用的標準方法鏈語法。

  • LINQ to Objects 或 LINQ to SQL,任何你可能熟悉的其他 LINQ 提供程式。

讓我們來看一個簡單的示例,我們將檢索名為 Laverne 的客戶。現在有可能有多個客戶名為 Laverne,因此我們只檢索第一個。

using System; 
using System.Data; 
using System.Linq; 
using System.Reflection; 

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cfg; 
using NHibernate.Criterion; 
using NHibernate.Dialect; 
using NHibernate.Driver; 
using NHibernate.Linq;

namespace NHibernateDemo { 

   internal class Program {
	
      private static void Main() { 
		
         var cfg = ConfigureNHibernate(); 
         var sessionFactory = cfg.BuildSessionFactory();
         using(var session = sessionFactory.OpenSession()) 
         
         using(var tx = session.BeginTransaction()) {
            var customer = session.Query<Customer>() 
               .Where(c => c.FirstName == "Laverne").First(); 
            Console.WriteLine(customer); 
            tx.Commit(); 
         }
			
         Console.WriteLine("Press <ENTER> to exit..."); 
         Console.ReadLine(); 
      }
		
      private static Configuration ConfigureNHibernate() { 
		
         NHibernateProfiler.Initialize(); 
         var cfg = new Configuration(); 
         
         cfg.DataBaseIntegration(x => { 
            x.ConnectionStringName = "default"; 
            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect>(); 
            x.IsolationLevel = IsolationLevel.RepeatableRead; 
            x.Timeout = 10; 
            x.BatchSize = 10; 
         }); 
         
         cfg.SessionFactory().GenerateStatistics();
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         return cfg; 
      } 
   } 
}

現在當上述程式碼編譯並執行時,你將看到以下輸出。

Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be)
   Points: 74
   HasGoldStatus: True
   MemberSince: 4/4/2009 12:00:00 AM (Utc)
   CreditRating: Neutral
   AverageRating: 0

   Orders:
      Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be
		
Press <ENTER> to exit...

查詢理解語法

還有查詢理解語法,它使用 from、where 和 select 關鍵字,看起來更像 SQL。

所以讓我們來看同一個示例,但這次我們使用 LINQ 理解語法,它看起來更像 SQL,如下面的程式所示。

using System; 
using System.Data; 
using System.Linq; 
using System.Reflection; 

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cfg; 
using NHibernate.Criterion; 
using NHibernate.Dialect; 
using NHibernate.Driver; 
using NHibernate.Linq;

namespace NHibernateDemo { 

   internal class Program { 
	
      private static void Main() { 
		
         var cfg = ConfigureNHibernate(); 
         var sessionFactory = cfg.BuildSessionFactory();
         using(var session = sessionFactory.OpenSession()) 
         
         using(var tx = session.BeginTransaction()) {
            var customer = (from c in session.Query<Customer>() 
               where c.FirstName == "Laverne" select c).First();
            Console.WriteLine(customer); 
            tx.Commit();
         }
         
         Console.WriteLine("Press <ENTER> to exit..."); 
         Console.ReadLine(); 
      }
      
      private static Configuration ConfigureNHibernate() {
		
         NHibernateProfiler.Initialize(); 
         var cfg = new Configuration(); 
         
         cfg.DataBaseIntegration(x => { 
            x.ConnectionStringName = "default"; 
            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect>(); 
            x.IsolationLevel = IsolationLevel.RepeatableRead; 
            x.Timeout = 10; 
            x.BatchSize = 10; 
         }); 
         
         cfg.SessionFactory().GenerateStatistics();
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         return cfg; 
      } 
   } 
}

現在讓我們再次執行此應用程式,你將看到以下輸出。

Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be)
   Points: 74
   HasGoldStatus: True
   MemberSince: 4/4/2009 12:00:00 AM (Utc)
   CreditRating: Neutral
   AverageRating: 0

   Orders:
      Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be
		
Press <ENTER> to exit...

讓我們來看另一個示例,我們將檢索所有 FirstName 以字母 H 開頭的客戶。

using System; 
using System.Data; 
using System.Linq; 
using System.Reflection; 

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cfg; 
using NHibernate.Criterion; 
using NHibernate.Dialect; 
using NHibernate.Driver; 
using NHibernate.Linq;

namespace NHibernateDemo { 

   internal class Program { 
	
      private static void Main() { 
		
         var cfg = ConfigureNHibernate(); 
         var sessionFactory = cfg.BuildSessionFactory();
         using(var session = sessionFactory.OpenSession()) 
         
         using(var tx = session.BeginTransaction()) {
            var customers = session.Query<Customer>() .Where(c =<
               c.FirstName.StartsWith("H"));
            
            foreach (var customer in customers.ToList()) { 
               Console.WriteLine(customer); 
            }
				
            tx.Commit(); 
         }
			
         Console.WriteLine("Press <ENTER> to exit..."); 
         Console.ReadLine(); 
      }
		
      private static Configuration ConfigureNHibernate() { 
		
         NHibernateProfiler.Initialize(); 
         var cfg = new Configuration(); 
         
         cfg.DataBaseIntegration(x => { 
            x.ConnectionStringName = "default"; 
            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect>(); 
            x.IsolationLevel = IsolationLevel.RepeatableRead; 
            x.Timeout = 10; 
            x.BatchSize = 10; 
         }); 
			
         cfg.SessionFactory().GenerateStatistics();
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         return cfg; 
      } 
   } 
}

類似地,查詢理解語法將如下面的程式所示。

using System; 
using System.Data; 
using System.Linq; 
using System.Reflection; 

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cfg; 
using NHibernate.Criterion; 
using NHibernate.Dialect; 
using NHibernate.Driver; 
using NHibernate.Linq;

namespace NHibernateDemo { 

   internal class Program { 
	
      private static void Main() { 
		
         var cfg = ConfigureNHibernate(); 
         var sessionFactory = cfg.BuildSessionFactory();
         using(var session = sessionFactory.OpenSession()) 
         
         using(var tx = session.BeginTransaction()) {
            var customers = from c in session.Query<Customer>() 
               where c.FirstName.StartsWith("H") select c;
            
            foreach (var customer in customers.ToList()) { 
               Console.WriteLine(customer); 
            }
				
            tx.Commit(); 
         }
			
         Console.WriteLine("Press <ENTER> to exit..."); 
         Console.ReadLine(); 
      }
		
      private static Configuration ConfigureNHibernate() { 
		
         NHibernateProfiler.Initialize(); 
         var cfg = new Configuration(); 
         
         cfg.DataBaseIntegration(x => { 
            x.ConnectionStringName = "default"; 
            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect>(); 
            x.IsolationLevel = IsolationLevel.RepeatableRead; 
            x.Timeout = 10; 
            x.BatchSize = 10; 
         }); 
			
         cfg.SessionFactory().GenerateStatistics();
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         return cfg; 
      } 
   }  
}

讓我們再次執行此應用程式,你將看到所有 FirstName 以字母 H 開頭的客戶。

Herman Crooks (4ead3480-6bce-11e1-b15c-6cf049ee52be)
   Points: 74
   HasGoldStatus: True
   MemberSince: 12/3/2010 12:00:00 AM (Utc)
   CreditRating: Neutral
   AverageRating: 0

   Orders:
      Order Id: 4ead3480-6bce-11e1-b15d-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b15e-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b15f-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b160-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b161-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b162-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b163-6cf049ee52be

Hudson Bins (4ec03f80-6bce-11e1-b2b7-6cf049ee52be)
   Points: 56
   HasGoldStatus: False
   MemberSince: 10/20/2008 12:00:00 AM (Utc)
   CreditRating: Terrible
   AverageRating: 0

   Orders:
      Order Id: 4ec03f80-6bce-11e1-b2b8-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2b9-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2ba-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2bb-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2bc-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2bd-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2be-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2bf-6cf049ee52be

Hettie Feest (4ec50240-6bce-11e1-b300-6cf049ee52be)
   Points: 82
   HasGoldStatus: False
   MemberSince: 4/10/2009 12:00:00 AM (Utc)
   CreditRating: Neutral
   AverageRating: 0

   Orders:
      Order Id: 4ec50240-6bce-11e1-b301-6cf049ee52be
      Order Id: 4ec50240-6bce-11e1-b302-6cf049ee52be
      Order Id: 4ec50240-6bce-11e1-b303-6cf049ee52be
		
Press <ENTER> to exit...

NHibernate - Hibernate 查詢語言

在本章中,我們將介紹 Hibernate 查詢語言。HQL 在 Java 的 Hibernate 和 NHibernate 中共享。

  • 它是與 **Criteria** 一起實現的最早的查詢機制之一。

  • 它是在很早的時候實現的,並且是一個基於字串的查詢 **API**。

  • 你可以透過 **ISession CreateQuery** 訪問它,它與 SQL 非常相似。

  • 它使用了許多相同的關鍵字,但語法更簡單。

  • 這是最常見的示例之一,如果你正在尋找如何執行查詢,你通常會找到 HQL 示例。

以下是 HQL 的一個簡單示例 -

var customers = session.CreateQuery("select c from Customer c where c.FirstName = 'Laverne'");
  • 所以在這裡你可以看到它們從 customer 中選擇 C,它看起來很像 SQL。就 NHibernate 而言,這是一個不透明的字串,因此你不知道它是否是有效的 HQL,直到執行時,這也是它的缺點之一。

  • LINQ 提供程式的優勢之一是你可以獲得編譯時支援。

  • 但是 HQL 是最靈活的查詢機制之一,經常被使用。據說,如果沒有其他方法可以做到,那麼就有方法可以在 HQL 中做到。

讓我們來看一個簡單的示例,我們將使用 HQL 而不是 LINQ 查詢來重新建立我們的 LINQ 查詢。你可以透過呼叫 **session.CreateQuery** 並使用 HQL 字串作為引數來訪問 HQL。

using System; 
using System.Data; 
using System.Linq; 
using System.Reflection; 

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cfg; 
using NHibernate.Criterion; 
using NHibernate.Dialect; 
using NHibernate.Driver; 
using NHibernate.Linq;

namespace NHibernateDemo {

   internal class Program { 
      
      private static void Main() { 
		
         var cfg = ConfigureNHibernate(); 
         var sessionFactory = cfg.BuildSessionFactory();
         using(var session = sessionFactory.OpenSession()) 
         
         using(var tx = session.BeginTransaction()) { 
            var customers = session.CreateQuery("select c from Customer c 
               where c.FirstName = 'Laverne'"); 
            
            foreach (var customer in customers.List<Customer>()) { 
               Console.WriteLine(customer); 
            }
				
            tx.Commit(); 
         }
			
         Console.WriteLine("Press <ENTER> to exit..."); 
         Console.ReadLine(); 
      }
		
      private static Configuration ConfigureNHibernate() { 
		
         NHibernateProfiler.Initialize(); 
         var cfg = new Configuration(); 
         
         cfg.DataBaseIntegration(x => { 
            x.ConnectionStringName = "default"; 
            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect>(); 
            x.IsolationLevel = IsolationLevel.RepeatableRead; 
            x.Timeout = 10; 
            x.BatchSize = 10; 
         }); 
         
         cfg.SessionFactory().GenerateStatistics();
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         return cfg; 
      } 
   } 
}
  • 此 HQL 字串看起來很像 SQL,主要區別在於 FirstName 是屬性名稱而不是列名。

  • 因此,如果兩者之間存在差異,則使用屬性名稱。同樣,它看起來像表名,但實際上是我們從中選擇的類的名稱。

  • 如果後端表名為 Customers,我們仍然會在 HQL 查詢中使用 Customer。

讓我們執行此應用程式,你將看到以下輸出。

Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be)
   Points: 74
   HasGoldStatus: True
   MemberSince: 4/4/2009 12:00:00 AM (Utc)
   CreditRating: Neutral
   AverageRating: 0

   Orders:
      Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be
		
Press <ENTER> to exit...

讓我們來看另一個簡單的示例,我們將使用 HQL 檢索所有 FirstName 以字母 H 開頭的客戶。

using System; 
using System.Data; 
using System.Linq; 
using System.Reflection; 

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cfg; 
using NHibernate.Criterion; 
using NHibernate.Dialect; 
using NHibernate.Driver;
using NHibernate.Linq; 

namespace NHibernateDemo { 

   internal class Program {
	
      private static void Main() { 
		
         var cfg = ConfigureNHibernate(); 
         var sessionFactory = cfg.BuildSessionFactory();
         using(var session = sessionFactory.OpenSession()) 
         
         using(var tx = session.BeginTransaction()) { 
            var customers = session.CreateQuery("select c from Customer c 
               where c.FirstName like 'H%'"); 
            
            foreach (var customer in customers.List<Customer>()) { 
               Console.WriteLine(customer); 
            }
				
            tx.Commit(); 
         } 
			
         Console.WriteLine("Press <ENTER> to exit..."); 
         Console.ReadLine(); 
      } 
		
      private static Configuration ConfigureNHibernate() {
		
         NHibernateProfiler.Initialize(); 
         var cfg = new Configuration(); 
         
         cfg.DataBaseIntegration(x => { 
            x.ConnectionStringName = "default"; 
            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect>(); 
            x.IsolationLevel = IsolationLevel.RepeatableRead; 
            x.Timeout = 10; 
            x.BatchSize = 10; 
         }); 
			
         cfg.SessionFactory().GenerateStatistics();
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         return cfg; 
      } 
   } 
}

讓我們再次執行你的應用程式,你將看到所有 FirstName 以字母 H 開頭的客戶都從該查詢中返回。

Herman Crooks (4ead3480-6bce-11e1-b15c-6cf049ee52be)
   Points: 74
   HasGoldStatus: True
   MemberSince: 12/3/2010 12:00:00 AM (Utc)
   CreditRating: Neutral
   AverageRating: 0

   Orders:
      Order Id: 4ead3480-6bce-11e1-b15d-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b15e-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b15f-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b160-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b161-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b162-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b163-6cf049ee52be

Hudson Bins (4ec03f80-6bce-11e1-b2b7-6cf049ee52be)
   Points: 56
   HasGoldStatus: False
   MemberSince: 10/20/2008 12:00:00 AM (Utc)
   CreditRating: Terrible
   AverageRating: 0

   Orders:
      Order Id: 4ec03f80-6bce-11e1-b2b8-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2b9-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2ba-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2bb-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2bc-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2bd-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2be-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2bf-6cf049ee52be

Hettie Feest (4ec50240-6bce-11e1-b300-6cf049ee52be)
   Points: 82
   HasGoldStatus: False
   MemberSince: 4/10/2009 12:00:00 AM (Utc)
   CreditRating: Neutral
   AverageRating: 0

   Orders:
      Order Id: 4ec50240-6bce-11e1-b301-6cf049ee52be
      Order Id: 4ec50240-6bce-11e1-b302-6cf049ee52be
      Order Id: 4ec50240-6bce-11e1-b303-6cf049ee52be
		
Press <ENTER> to exit...

我們可以做更復雜的事情,例如想要所有訂單,其中客戶的訂單數量大於 9。以下是相同的 HQL 查詢。

var customers = session.CreateQuery("select c from Customer c 
   where size(c.Orders) > 9"); 
	
foreach (var customer in customers.List<Customer>()) { 
   Console.WriteLine(customer); 
}

我們還需要指出我們需要在這裡使用大小、計數或長度。在 HQL 中,我們可以選擇使用上面顯示的特殊 size 方法。

如果你願意,另一種寫法是 **c.Orders.size**,它具有完全相同的效果。

var customers = session.CreateQuery("select c from Customer c 
   where c.Orders.size > 9"); 
	
foreach (var customer in customers.List<Customer>()) { 
   Console.WriteLine(customer); 
}

讓我們執行此應用程式。

Lindsay Towne (4ea3aef6-6bce-11e1-b0cb-6cf049ee52be)
   Points: 50
   HasGoldStatus: False
   MemberSince: 4/13/2007 12:00:00 AM (Utc)
   CreditRating: VeryGood
   AverageRating: 0

   Orders:
      Order Id: 4ea3aef6-6bce-11e1-b0cc-6cf049ee52be
      Order Id: 4ea3aef6-6bce-11e1-b0cd-6cf049ee52be
      Order Id: 4ea3aef6-6bce-11e1-b0ce-6cf049ee52be
      Order Id: 4ea3aef6-6bce-11e1-b0cf-6cf049ee52be
      Order Id: 4ea3aef6-6bce-11e1-b0d0-6cf049ee52be
      Order Id: 4ea3aef6-6bce-11e1-b0d1-6cf049ee52be
      Order Id: 4ea3aef6-6bce-11e1-b0d2-6cf049ee52be
      Order Id: 4ea3aef6-6bce-11e1-b0d3-6cf049ee52be
      Order Id: 4ea3aef6-6bce-11e1-b0d4-6cf049ee52be
      Order Id: 4ea3aef6-6bce-11e1-b0d5-6cf049ee52be

Wyman Hammes (4ea61056-6bce-11e1-b0e2-6cf049ee52be)
   Points: 32
   HasGoldStatus: False
   MemberSince: 2/5/2011 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 0

   Orders:
      Order Id: 4ea61056-6bce-11e1-b0e3-6cf049ee52be
      Order Id: 4ea61056-6bce-11e1-b0e4-6cf049ee52be
      Order Id: 4ea61056-6bce-11e1-b0e5-6cf049ee52be
      Order Id: 4ea61056-6bce-11e1-b0e6-6cf049ee52be
      Order Id: 4ea61056-6bce-11e1-b0e7-6cf049ee52be
      Order Id: 4ea61056-6bce-11e1-b0e8-6cf049ee52be
      Order Id: 4ea61056-6bce-11e1-b0e9-6cf049ee52be
      Order Id: 4ea61056-6bce-11e1-b0ea-6cf049ee52be
      Order Id: 4ea61056-6bce-11e1-b0eb-6cf049ee52be
      Order Id: 4ea61056-6bce-11e1-b0ec-6cf049ee52be
		
Press <ENTER> to exit...

你可以看到所有訂單數量超過 9 的客戶都從資料庫中檢索出來了。

NHibernate - Criteria 查詢

在本章中,我們將介紹 Criteria 查詢機制。**NHibernate 基於 Criteria 的查詢 API** 允許你透過在執行時操作 Criteria 物件來構建查詢。

  • 這種方法允許你動態指定約束,而無需直接進行字串操作,但它並沒有失去 HQL 的太多靈活性和功能。

  • 另一方面,用 Criteria 表達的查詢通常不如用 HQL 表達的查詢易讀。

  • 經典的 Criteria 語法是一個基於物件的查詢 API,如下面的程式所示。

var customers = session.CreateCriteria<Customer>().Add(Restrictions.Like("FirstName", "H%"));
  • 你可以看到我們正在對 customer 執行 session create criteria,現在我們正在向該查詢新增 restriction 物件。

  • 這對於使用者可以選擇某些選項但不能選擇其他選項的查詢頁面很有用。

  • 構建查詢作為樹狀查詢結構,而不是在 HQL 或 LINQ 中使用 AND 或 OR 在 WHERE 子句中,這樣更容易。

  • 使用這些條件物件新增額外的限制條件會更容易。

讓我們來看一個簡單的例子,在這個例子中,我們將建立一個查詢並透過createCriteria訪問條件API,然後新增一個限制條件,即名字以H開頭。

using System; 
using System.Data; 
using System.Linq; 
using System.Reflection; 

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cfg; 
using NHibernate.Criterion; 
using NHibernate.Dialect; 
using NHibernate.Driver; 
using NHibernate.Linq; 

namespace NHibernateDemo {

   internal class Program { 
      
      private static void Main() { 
		
         var cfg = ConfigureNHibernate(); 
         var sessionFactory = cfg.BuildSessionFactory();
         using(var session = sessionFactory.OpenSession()) 
         
         using(var tx = session.BeginTransaction()) { 
            var customers = session.CreateCriteria<Customer>() 
               .Add(Restrictions.Like("FirstName", "H%")); 
         
            foreach (var customer in customers.List<Customer>()) { 
               Console.WriteLine(customer); 
            } 
				
            tx.Commit(); 
         }
			
         Console.WriteLine("Press <ENTER> to exit..."); 
         Console.ReadLine(); 
      } 
		
      private static Configuration ConfigureNHibernate() {
		
         NHibernateProfiler.Initialize(); 
         var cfg = new Configuration(); 
         
         cfg.DataBaseIntegration(x => { 
            x.ConnectionStringName = "default"; 
            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect>(); 
            x.IsolationLevel = IsolationLevel.RepeatableRead; 
            x.Timeout = 10; 
            x.BatchSize = 10; 
         }); 
			
         cfg.SessionFactory().GenerateStatistics();
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         return cfg; 
      } 
   } 
}

當上述程式碼編譯並執行時,你將看到以下輸出。

Herman Crooks (4ead3480-6bce-11e1-b15c-6cf049ee52be)
   Points: 74
   HasGoldStatus: True
   MemberSince: 12/3/2010 12:00:00 AM (Utc)
   CreditRating: Neutral
   AverageRating: 0

   Orders:
      Order Id: 4ead3480-6bce-11e1-b15d-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b15e-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b15f-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b160-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b161-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b162-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b163-6cf049ee52be

Hudson Bins (4ec03f80-6bce-11e1-b2b7-6cf049ee52be)
   Points: 56
   HasGoldStatus: False
   MemberSince: 10/20/2008 12:00:00 AM (Utc)
   CreditRating: Terrible
   AverageRating: 0

   Orders:
      Order Id: 4ec03f80-6bce-11e1-b2b8-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2b9-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2ba-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2bb-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2bc-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2bd-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2be-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2bf-6cf049ee52be

Hettie Feest (4ec50240-6bce-11e1-b300-6cf049ee52be)
   Points: 82
   HasGoldStatus: False
   MemberSince: 4/10/2009 12:00:00 AM (Utc)
   CreditRating: Neutral
   AverageRating: 0

   Orders:
      Order Id: 4ec50240-6bce-11e1-b301-6cf049ee52be
      Order Id: 4ec50240-6bce-11e1-b302-6cf049ee52be
      Order Id: 4ec50240-6bce-11e1-b303-6cf049ee52be
		
Press <ENTER> to exit…

讓我們來看另一個簡單的例子,在這個例子中,我們將檢索名字等於“Laverne”的客戶。

using System; 
using System.Data; 
using System.Linq; 
using System.Reflection; 

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cfg; 
using NHibernate.Criterion; 
using NHibernate.Dialect; 
using NHibernate.Driver; 
using NHibernate.Linq;

namespace NHibernateDemo { 

   internal class Program { 
      
      private static void Main() { 
		
         var cfg = ConfigureNHibernate(); 
         var sessionFactory = cfg.BuildSessionFactory();
         using(var session = sessionFactory.OpenSession()) 
			
         using(var tx = session.BeginTransaction()) { 
            var customers = session.CreateCriteria<Customer>() 
               .Add(Restrictions.Eq("FirstName", "Laverne")) .List<Customer>(); 
            
            foreach (var customer in customers) {
               Console.WriteLine(customer); 
            } 
				
            tx.Commit(); 
         }
			
         Console.WriteLine("Press <ENTER> to exit..."); 
         Console.ReadLine(); 
      }
		
      private static Configuration ConfigureNHibernate() {
		
         NHibernateProfiler.Initialize(); 
         var cfg = new Configuration(); 
         
         cfg.DataBaseIntegration(x => { 
            x.ConnectionStringName = "default"; 
            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect>(); 
            x.IsolationLevel = IsolationLevel.RepeatableRead; 
            x.Timeout = 10; 
            x.BatchSize = 10; 
         }); 
         
         cfg.SessionFactory().GenerateStatistics();
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         return cfg; 
      } 
   } 
}

讓我們再次執行此應用程式,您將看到以下輸出。

Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be)
   Points: 74
   HasGoldStatus: True
   MemberSince: 4/4/2009 12:00:00 AM (Utc)
   CreditRating: Neutral
   AverageRating: 0

   Orders:
      Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be
		
Press <ENTER> to exit...

現在,條件API的主要缺點之一是屬性名稱中的這些不透明字串。因此,如果名字被重構為其他內容,重構工具不一定能夠識別不透明字串。

NHibernate - QueryOver 查詢

在本章中,我們將介紹QueryOver查詢。這是一種新的語法,更類似於使用方法鏈語法的LINQ,如下面的查詢所示。

var customers = session.QueryOver<Customer>() .Where(x => x.FirstName == "Laverne");
  • 它在底層仍然是條件,但現在我們的查詢是強型別的。

  • 正如我們在條件查詢中看到的,名字只是一個不透明的字串,現在我們實際上使用了x.FirstName,所以如果名字被重構和重新命名,則使用QueryOver的連結風格條件查詢中的更改也會生效。

  • 我們仍然可以做很多類似的事情,但不能在QueryOver中使用查詢理解語法,必須使用方法鏈語法,並且不能混合使用連結和條件。

  • 對於許多查詢,QueryOver API非常有用,並且提供了比直接使用條件更容易理解的物件語法。

讓我們來看一個簡單的例子,在這個例子中,我們將使用QueryOver檢索名字為Laverne的客戶。

using System; 
using System.Data; 
using System.Linq; 
using System.Reflection; 

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cfg; 
using NHibernate.Criterion; 
using NHibernate.Dialect; 
using NHibernate.Driver; 
using NHibernate.Linq;

namespace NHibernateDemo { 

   internal class Program { 
      
      private static void Main() { 
		
         var cfg = ConfigureNHibernate(); 
         var sessionFactory = cfg.BuildSessionFactory();
         using(var session = sessionFactory.OpenSession()) 
         
         using(var tx = session.BeginTransaction()) { 
            var customers = session.QueryOver<Customer>() 
               .Where(x => x.FirstName == "Laverne"); 
            
            foreach (var customer in customers.List()) { 
               Console.WriteLine(customer); 
            } 
				
            tx.Commit(); 
         }
			
         Console.WriteLine("Press <ENTER> to exit..."); 
         Console.ReadLine(); 
      }
      
      private static Configuration ConfigureNHibernate() { 
		
         NHibernateProfiler.Initialize();
         var cfg = new Configuration(); 
         
         cfg.DataBaseIntegration(x => { 
            x.ConnectionStringName = "default"; 
            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect>(); 
            x.IsolationLevel = IsolationLevel.RepeatableRead; 
            x.Timeout = 10; 
            x.BatchSize = 10; 
         });
			
         cfg.SessionFactory().GenerateStatistics();
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         return cfg; 
      } 
   } 
}

正如你所看到的,它在底層仍然是條件,但只是一個更友好的語法。

當以上程式碼編譯並執行時,你將看到以下輸出。

Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be)
   Points: 74
   HasGoldStatus: True
   MemberSince: 4/4/2009 12:00:00 AM (Utc)
   CreditRating: Neutral
   AverageRating: 0

   Orders:
      Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be
		
Press <ENTER> to exit...

其中一個缺點是,假設我們想說FirstName.StartsWith(“A”),如下面的程式所示。

var customers = session.QueryOver<Customer>() .Where(x => x.FirstName.StartsWith("A"));
 
foreach (var customer in customers.List()) { 
   Console.WriteLine(customer); 
} 

tx.Commit();

現在讓我們再次執行應用程式,你將看到這不是一個LINQ提供程式,因為它不知道這個StartsWith方法是什麼,所以你將得到一個執行時異常

Run Time Exception

異常表示無法識別的呼叫方法。在這裡,我們正在做顯而易見的事情,但它不一定能工作。

讓我們嘗試其他方法,比如FirstName等於“A%”,如下面的程式碼所示。

var customers = session.QueryOver<Customer>() .Where(x => x.FirstName == "A%"); 

foreach (var customer in customers.List()) { 
   Console.WriteLine(customer); 
}

讓我們再次執行它,你將看到我們不會得到任何結果,如下所示。

Press <ENTER> to exit...

為了理解為什麼我們沒有得到任何結果,讓我們看看NHibernate Profiler。

Results NHibernate Profile

正如你所看到的,名字等於A%,這不是。A%在SQL中與like運算子一起使用。現在我們需要在WHERE子句中建立一個限制條件,如下面的程式所示。

var customers = session.QueryOver<Customer>() 
   .Where(Restrictions.On<Customer>(c => c.FirstName).IsLike("A%")); 
	
foreach (var customer in customers.List()) { 
   Console.WriteLine(customer); 
}

讓我們再次執行你的應用程式,你將看到所有名字以A開頭的客戶都被檢索到。

Alejandrin Will (4ea3aef6-6bce-11e1-b0b4-6cf049ee52be)
   Points: 24
   HasGoldStatus: False
   MemberSince: 10/1/2011 12:00:00 AM (Utc)
   CreditRating: VeryVeryGood
   AverageRating: 0

   Orders:
      Order Id: 4ea3aef6-6bce-11e1-b0b5-6cf049ee52be

Austyn Nolan (4ea871b6-6bce-11e1-b110-6cf049ee52be)
   Points: 67
   HasGoldStatus: True
   MemberSince: 12/29/2007 12:00:00 AM (Utc)
   CreditRating: Neutral
   AverageRating: 0

   Orders:
      Order Id: 4ea871b6-6bce-11e1-b111-6cf049ee52be

Antonia Murphy (4ea871b6-6bce-11e1-b121-6cf049ee52be)
   Points: 72
   HasGoldStatus: True
   MemberSince: 6/15/2009 12:00:00 AM (Utc)
   CreditRating: Terrible
   AverageRating: 0

   Orders:
      Order Id: 4ea871b6-6bce-11e1-b122-6cf049ee52be
      Order Id: 4ea871b6-6bce-11e1-b123-6cf049ee52be
      Order Id: 4ea871b6-6bce-11e1-b124-6cf049ee52be
      Order Id: 4ea871b6-6bce-11e1-b125-6cf049ee52be
      Order Id: 4ea871b6-6bce-11e1-b126-6cf049ee52be
      Order Id: 4ea871b6-6bce-11e1-b127-6cf049ee52be
      Order Id: 4ea871b6-6bce-11e1-b128-6cf049ee52be
      Order Id: 4ea871b6-6bce-11e1-b129-6cf049ee52be
      Order Id: 4ea871b6-6bce-11e1-b12a-6cf049ee52be

它的工作方式與之前相同,只是使用了新的QueryOver語法。許多開發人員發現LINQ語法更容易理解,並且通常能做出正確的事情。

如果LINQ無法處理它,那麼你將開始檢視HQL或條件,看看是否更合適。

它只是提供了不同的語法,因此條件(create criteria和QueryOver)都為你提供了另一種查詢機制,允許你使用NHibernate從資料庫中提取資料。

NHibernate - 原生 SQL

在本章中,我們將介紹如何在NHibernate中使用原生SQL查詢。如果你使用手寫SQL多年,你可能會擔心ORM會奪走你習慣的一些表達性和靈活性。

  • NHibernate強大的查詢功能允許你做幾乎所有你在SQL中能做的事情,在某些情況下甚至更多。

  • 對於少數你無法讓NHibernate自己的查詢功能完全滿足你的需求的情況。

  • NHibernate允許你使用資料庫的原生SQL方言來檢索物件。

讓我們來看一個NHibernate中原生SQL查詢的簡單示例。

using System; 
using System.Data; 
using System.Linq; 
using System.Reflection; 

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cfg; 
using NHibernate.Criterion; 
using NHibernate.Dialect; 
using NHibernate.Driver; 
using NHibernate.Linq; 
using NHibernate;

namespace NHibernateDemo {

   internal class Program { 
	
      private static void Main() { 
		
         var cfg = ConfigureNHibernate(); 
         var sessionFactory = cfg.BuildSessionFactory();
         using(var session = sessionFactory.OpenSession()) 
         
         using(var tx = session.BeginTransaction()) {
            IQuery sqlQuery = session.CreateSQLQuery("SELECT * FROM
               CUSTOMER").AddEntity(typeof(Customer));
            var customers = sqlQuery.List<Customer>();
				
            foreach (var customer in customers) { 
               Console.WriteLine(customer); 
            } 
				
            tx.Commit(); 
         }
         
         Console.WriteLine("Press <ENTER> to exit..."); 
         Console.ReadLine(); 
      }
		
      private static Configuration ConfigureNHibernate() { 
		
         NHibernateProfiler.Initialize(); 
         var cfg = new Configuration(); 
         
         cfg.DataBaseIntegration(x => { 
            x.ConnectionStringName = "default"; 
            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect>(); 
            x.IsolationLevel = IsolationLevel.RepeatableRead; 
            x.Timeout = 10; 
            x.BatchSize = 10; 
         }); 
         
         cfg.SessionFactory().GenerateStatistics();
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         return cfg; 
      } 
   } 
}

以上示例使用CreateSQLQuery()來獲取物件列表,你還會注意到,你希望查詢返回的根實體型別被指定為Customer。

讓我們執行你的應用程式,你將看到所有客戶都被從資料庫中檢索出來。

Emerson Prosacco (4ec2a0e0-6bce-11e1-b2cf-6cf049ee52be)
   Points: 17
   HasGoldStatus: False
   MemberSince: 6/22/2007 12:00:00 AM (Utc)
   CreditRating: Excellent
   AverageRating: 0

   Orders:
      Order Id: 4ec2a0e0-6bce-11e1-b2d0-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2d1-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2d2-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2d3-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2d4-6cf049ee52be

Kaci Friesen (4ec2a0e0-6bce-11e1-b2d5-6cf049ee52be)
   Points: 30
   HasGoldStatus: True
   MemberSince: 5/25/2007 12:00:00 AM (Utc)
   CreditRating: VeryVeryGood
   AverageRating: 0

   Orders:
      Order Id: 4ec2a0e0-6bce-11e1-b2d6-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2d7-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2d8-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2d9-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2da-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2db-6cf049ee52be

Eveline Waters (4ec2a0e0-6bce-11e1-b2dc-6cf049ee52be)
   Points: 58
   HasGoldStatus: False
   MemberSince: 10/29/2009 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 0

   Orders:
      Order Id: 4ec2a0e0-6bce-11e1-b2dd-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2de-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2df-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2e0-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2e2-6cf049ee52be

Molly Kuhn (4ec2a0e0-6bce-11e1-b2e3-6cf049ee52be)
   Points: 73
   HasGoldStatus: False
   MemberSince: 12/16/2007 12:00:00 AM (Utc)
   CreditRating: VeryGood
   AverageRating: 0

   Orders:
      Order Id: 4ec2a0e0-6bce-11e1-b2e4-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2e5-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2e6-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2e7-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2e8-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2e9-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2ea-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2eb-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2ec-6cf049ee52be

這是另一種編寫原生SQL查詢的方式,如下所示。

IList<Customer> customers = session.CreateSQLQuery("SELECT * FROM CUSTOMER")
   .AddScalar("Id", NHibernateUtil.Guid) 
   .AddScalar("FirstName", NHibernateUtil.String) 
   .AddScalar("LastName", NHibernateUtil.String) .List<Customer>();
  • 正如你所看到的,以上查詢指定了SQL查詢字串以及要返回的列和型別。

  • 這將返回一個IList的Object陣列,其中包含Customer表中每列的標量值。

  • 即使查詢使用*並且可能返回超過三個列,也只會返回這三個列。

讓我們來看另一個簡單的例子。

IList<Customer> customers = session.CreateSQLQuery("SELECT * FROM CUSTOMER WHERE  
   FirstName = 'Laverne'") 
   .AddEntity(typeof(Customer)) .List<Customer>(); 
	
foreach (var customer in customers) { 
   Console.WriteLine(customer); 
}

讓我們再次執行你的應用程式,你將看到以下輸出。

Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be)
   Points: 74
   HasGoldStatus: True
   MemberSince: 4/4/2009 12:00:00 AM (Utc)
   CreditRating: Neutral
   AverageRating: 0

   Orders:
      Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be
		
Press <ENTER> to exit...

類似地,你可以指定任何型別的SQL查詢來從資料庫中檢索資料。

NHibernate - Fluent Hibernate

在本章中,我們將介紹Fluent NHibernate。Fluent NHibernate是另一種對映方式,或者你可以說它是NHibernate標準XML對映檔案的替代方案。而不是編寫XML(.hbm.xml檔案)文件。藉助Fluent NHibernate,你可以在強型別C#程式碼中編寫對映。

  • 在Fluent NHibernate中,對映與應用程式的其餘部分一起編譯。

  • 你可以像應用程式程式碼一樣輕鬆地更改對映,編譯器將在任何型別錯誤時失敗。

  • 它有一個約定配置系統,你可以在其中指定用於覆蓋命名約定和其他許多事物的模式。

  • 你還可以一次設定命名方式,然後Fluent NHibernate完成其餘工作。

讓我們來看一個簡單的例子,建立一個新的控制檯專案。在本章中,我們將使用一個簡單的資料庫,其中包含一個簡單的Customer表,如下面的圖片所示。

Simple Customer Table

安裝Fluent NHibernate

開始使用Fluent NHibernate的第一步是安裝Fluent NHibernate包。因此,開啟NuGet包管理器控制檯並輸入以下命令。

PM> install-package FluentNHibernate

一旦成功安裝,你將看到以下訊息。

Installed Successfully

讓我們新增一個簡單的Customer模型類,下面的程式顯示了Customer類的實現。

using System; 
using System.Collections.Generic; 
using System.Linq;
using System.Text; 
using System.Threading.Tasks;

namespace FluentNHibernateDemo { 
   class Customer { 
      public virtual int Id { get; set; } 
      public virtual string FirstName { get; set; } 
      public virtual string LastName { get; set; } 
   } 
}

現在我們需要使用Fluent NHibernate建立對映,所以在你的專案中再新增一個類CustomerMap。以下是CustomerMap類的實現。

using FluentNHibernate.Mapping; 
using System; 
using System.Collections.Generic; 
using System.Linq; using System.Text; 
using System.Threading.Tasks;

namespace FluentNHibernateDemo { 
   class CustomerMap : ClassMap<Customer> { 
      public CustomerMap() { 
         Id(x => x.Id); 
         Map(x => x.FirstName); 
         Map(x => x.LastName); 
         Table("Customer"); 
      } 
   }
}

讓我們再新增一個類NHibernateHelper,我們將在其中設定不同的配置設定。

using FluentNHibernate.Cfg; 
using FluentNHibernate.Cfg.Db; 
using NHibernate; 
using NHibernate.Tool.hbm2ddl;

namespace FluentNHibernateDemo { 

   public class NHibernateHelper { 
	
      private static ISessionFactory _sessionFactory;
		
      private static ISessionFactory SessionFactory { 
         get { 
            if (_sessionFactory == null)
            InitializeSessionFactory(); return _sessionFactory; 
         } 
      }
   
      private static void InitializeSessionFactory() { 
         _sessionFactory = Fluently.Configure() 
			
         String Data Source = asia13797\\sqlexpress;
         String Initial Catalog = NHibernateDemoDB;
         String Integrated Security = True;
         String Connect Timeout = 15;
         String Encrypt = False;
         String TrustServerCertificate = False;
         String ApplicationIntent = ReadWrite;
         String MultiSubnetFailover = False;
			
         .Database(MsSqlConfiguration.MsSql2008 .ConnectionString( 
            @"Data Source + Initial Catalog + Integrated Security + Connect Timeout
            + Encrypt + TrustServerCertificate + ApplicationIntent + 
            MultiSubnetFailover") .ShowSql() ) 
				
         .Mappings(m => m.FluentMappings
         .AddFromAssemblyOf<Program>()) 
         .ExposeConfiguration(cfg => new SchemaExport(cfg) 
         .Create(true, true)) 
         .BuildSessionFactory(); 
      }
		
      public static ISession OpenSession() { 
         return SessionFactory.OpenSession(); 
      } 
   }
}

現在讓我們轉到Program.cs檔案,我們將在其中啟動一個會話,然後建立一個新的客戶並將其儲存到資料庫中,如下所示。

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks;

namespace FluentNHibernateDemo { 
   class Program { 
	
      static void Main(string[] args) { 
		
         using (var session = NHibernateHelper.OpenSession()) { 
			
            using (var transaction = session.BeginTransaction()) { 
               var customer = new Customer { 
                  FirstName = "Allan", 
                  LastName = "Bomer" 
               }; 
					
               session.Save(customer); 
               transaction.Commit();
               Console.WriteLine("Customer Created: " + customer.FirstName + "\t" +
                  customer.LastName); 
            } 
				
            Console.ReadKey(); 
         } 
      } 
   } 
}

讓我們執行你的應用程式,你將看到以下輸出。

if exists (select * from dbo.sysobjects where id = object_id(N'Customer') and
   OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table Customer

create table Customer (
   Id INT IDENTITY NOT NULL,
   FirstName NVARCHAR(255) null,
   LastName NVARCHAR(255) null,
   primary key (Id)
)

NHibernate: INSERT INTO Customer (FirstName, LastName) VALUES (@p0, @p1); 
   select SCOPE_IDENTITY();@p0 = 'Allan' [Type: String (4000)], 
   @p1 = 'Bomer' [Type: String (4000)]
   Customer Created: Allan Bomer

正如你所看到的,新的客戶已建立。要檢視客戶記錄,讓我們轉到資料庫並檢視資料檢視,你將看到已添加了1個客戶。

Customer Record
廣告

© . All rights reserved.