NHibernate 全面教程:从入门到精通
目录
-
(图片来源网络,侵删)- 什么是 NHibernate?
- 为什么选择 NHibernate?
- NHibernate 与 Entity Framework 的对比
-
- 环境要求
- 创建控制台应用
- 安装 NHibernate NuGet 包
- 配置 NHibernate
-
- 核心概念:
ISession,ISessionFactory,ITransaction - 第一个实体类与映射文件
- 配置文件 (
hibernate.cfg.xml) - 使用 Fluent NHibernate 进行代码化配置(可选,但推荐)
- 核心概念:
-
- Create: 保存新实体
- Read: 查询实体
- Update: 更新实体
- Delete: 删除实体
- 使用
LINQ to NHibernate进行查询
-
(图片来源网络,侵删)- 关系映射(一对一、一对多、多对多)
- 事务与并发控制
- 缓存机制
- 批量操作与性能优化
-
- 最佳实践
- 推荐资源
第一部分:NHibernate 简介
什么是 NHibernate?
NHibernate 是一个成熟、强大、开源的 对象关系映射 框架,专门为 .NET 平台设计,它的灵感来自著名的 Java ORM 框架 Hibernate。
NHibernate 的作用是在 面向对象的领域模型 和 关系型数据库 之间建立一个桥梁,它允许你使用 C# 或 VB.NET 对象来操作数据库,而无需编写大量的 SQL 语句,你操作的是对象,NHibernate 会自动将这些操作翻译成相应的 SQL 并执行。
为什么选择 NHibernate?
- 成熟稳定:NHibernate 存在已久,经过了大量项目的检验,非常稳定可靠。
- 功能强大:支持几乎所有复杂的数据库操作,如高级查询、继承映射、缓存、事务等。
- 数据库无关性:你可以轻松地更换底层数据库(例如从 SQL Server 换到 MySQL 或 PostgreSQL),而几乎不需要修改你的 C# 代码,NHibernate 会自动生成对应数据库的 SQL 方言。
- 灵活性高:提供了 XML 映射和 Fluent NHibernate(代码优先)两种方式,对开发者非常友好。
- 社区支持:拥有一个活跃的社区,你可以找到大量的文档、教程和解决方案。
NHibernate 与 Entity Framework 的对比
| 特性 | NHibernate | Entity Framework (EF Core) |
|---|---|---|
| 控制反转 | 高,对 SQL 的生成和执行有更精细的控制。 | 低,抽象层次更高,有时难以控制生成的 SQL。 |
| 成熟度 | 非常高,十多年的发展,非常稳定。 | 高(EF Core),EF Core 已经非常成熟,但相比 NHibernate 仍属年轻。 |
| 学习曲线 | 较陡,概念较多(如 Session, Cache, HQL)。 | 较平缓,与 .NET 生态集成紧密,上手更容易。 |
| 灵活性 | 极高,可以处理非常复杂的映射场景。 | 较高,能满足绝大多数业务场景,但极端复杂场景可能受限。 |
| 数据库支持 | 极广,几乎所有主流数据库。 | 较广,官方支持主流数据库,社区提供更多支持。 |
| 发展方向 | 稳定维护,是遗留系统维护和需要高度控制场景的首选。 | 主流,微软主推,是 .NET 新项目的默认选择。 |
如果你的项目是遗留系统,或者你对数据库性能和 SQL 控制有极高要求,NHibernate 是绝佳选择,对于新项目,尤其是中小型项目,EF Core 可能是更简单、更快速的选择。

第二部分:环境准备与第一个项目
环境要求
- .NET SDK (.NET 6.0 或更高版本)
- 一个 IDE (Visual Studio 2025 或 Rider)
- 一个数据库 (SQL Server Express, SQLite 用于开发非常方便)
创建控制台应用
- 打开 Visual Studio,创建一个新的 "控制台应用" (.NET 6.0 或更高版本)。
- 给项目命名,
NHibernateGettingStarted。
安装 NHibernate NuGet 包
在 Visual Studio 的 "包管理器控制台" (Package Manager Console) 中执行以下命令:
Install-Package NHibernate Install-Package NHibernate.Linq Install-Packet System.Data.SqlClient # 如果你使用 SQL Server
或者,在 NuGet 包管理器中搜索并安装 NHibernate, NHibernate.Linq, 和 System.Data.SqlClient。
配置 NHibernate
NHibernate 需要一个配置文件来告诉它如何连接数据库以及加载哪些映射文件,最简单的方式是使用 hibernate.cfg.xml。
- 在项目中添加一个 XML 文件,命名为
hibernate.cfg.xml。 - 将其 "复制到输出目录" 属性设置为 "如果较新则复制"。
- 设置其 "生成操作" 为 "内容"。
- 编写配置文件内容(以 SQL Server LocalDB 为例):
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-configuration xmlns="http://www.nhibernate.org/schema/hibernate-configuration">
<session-factory>
<!-- 1. 数据库连接信息 -->
<property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
<property name="connection.connection_string">Server=(localdb)\MSSQLLocalDB;Database=NHibernateDemo;Trusted_Connection=True;</property>
<!-- 2. Dialect (数据库方言) -->
<property name="dialect">NHibernate.Dialect.MsSql2012Dialect</property>
<!-- 3. 其他重要配置 -->
<property name="show_sql">true</property> <!-- 在控制台打印生成的SQL -->
<property name="format_sql">true</property> <!-- 格式化打印的SQL -->
<property name="hbm2ddl.auto">update</property> <!-- 自动更新数据库结构 -->
<!-- 4. 映射文件 (稍后会添加) -->
<!-- <mapping resource="YourApp.Mapping.YourEntity.hbm.xml" /> -->
</session-factory>
</hibernate-configuration>
配置项说明:
connection.driver_class: 指定数据库驱动。connection.connection_string: 标准的数据库连接字符串。dialect: 告诉 NHibernate 使用哪个数据库的 SQL 语法。非常重要。show_sql: 开发时非常有用,可以看到 NHibernate 生成的 SQL。hbm2ddl.auto:none: 不创建或更新任何表。update: 如果表结构有变化,会更新表,开发时常用。create: 每次启动都会重新创建表,数据会丢失!仅用于测试。create-drop: 创建表,当ISessionFactory关闭时删除表。
第三部分:核心概念与映射
现在我们来定义一个实体类,并告诉 NHibernate 如何将它映射到数据库表。
核心概念
ISessionFactory: 一个重量级的对象,它是线程安全的,通常在应用程序启动时创建一次,它负责创建ISession对象,并管理所有数据库连接,你可以把它看作是数据库连接池的工厂。ISession: 一个轻量级的对象,相当于 ADO.NET 中的Connection,它是 NHibernate 与数据库交互的主要接口,你用它来执行 CRUD 操作、查询和管理事务。ISession不是线程安全的,通常每个请求(或一个工作单元)创建一个。ITransaction: 代表一个工作单元(Unit of Work),数据库操作应该在事务中执行,以确保数据的原子性(要么全部成功,要么全部失败)。
第一个实体类与映射文件
- 创建实体类 (
User.cs)
// Models/User.cs
namespace NHibernateGettingStarted.Models
{
public class User
{
public virtual int Id { get; set; } // virtual 是必须的,用于 NHibernate 的代理功能
public virtual string Username { get; set; }
public virtual string Email { get; set; }
public virtual DateTime CreatedAt { get; set; }
}
}
- 创建映射文件 (
User.hbm.xml)
这个文件描述了 User 类和数据库表 Users 之间的对应关系。
- 在项目中添加一个 XML 文件,命名为
User.hbm.xml。 - 设置其 "生成操作" 为 "嵌入的资源" (Embedded Resource)。
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="http://www.nhibernate.org/schema/hibernate-mapping"
namespace="NHibernateGettingStarted.Models" assembly="NHibernateGettingStarted">
<class name="User" table="Users">
<id name="Id" column="Id">
<generator class="identity" /> <!-- 主键生成策略,自增 -->
</id>
<property name="Username" column="Username" type="String" not-null="true" length="50" />
<property name="Email" column="Email" type="String" not-null="true" length="100" />
<property name="CreatedAt" column="CreatedAt" type="DateTime" not-null="true" />
</class>
</hibernate-mapping>
映射文件说明:
<class>: 定义一个类到表的映射。name: C# 类的全名(包含命名空间)。table: 对应的数据库表名。
<id>: 定义主键。name: C# 类中的属性名。column: 数据库表中的列名。<generator>: 指定主键生成策略。identity表示自增。
<property>: 定义一个普通属性。name,column: 同上。type: 指定 NHibernate 类型(通常是 C# 类型的映射)。not-null="true": 表示该列不能为空。
- 更新
hibernate.cfg.xml
在 <session-factory> 节点中添加我们刚刚创建的映射文件引用。
<!-- 在 hibernate.cfg.xml 中添加 --> <mapping resource="NHibernateGettingStarted.Models.User.hbm.xml" />
使用 Fluent NHibernate 进行代码化配置(强烈推荐)
使用 XML 文件进行配置和映射虽然直观,但在大型项目中会变得非常繁琐。Fluent NHibernate 允许你使用 C# 代码来配置 NHibernate 和定义映射,这更符合 .NET 开发者的习惯。
-
安装 Fluent NHibernate NuGet 包
Install-Package FluentNHibernate
-
创建映射类 (
UserMap.cs)
创建一个继承自 ClassMap<T> 的类。
// Mappings/UserMap.cs
using FluentNHibernate.Mapping;
using NHibernateGettingStarted.Models;
namespace NHibernateGettingStarted.Mappings
{
public class UserMap : ClassMap<User>
{
public UserMap()
{
Table("Users"); // 对应的表名
Id(x => x.Id) // 主键
.GeneratedBy.Identity(); // 自增策略
Map(x => x.Username) // 普通属性映射
.Length(50)
.Not.Nullable();
Map(x => x.Email)
.Length(100)
.Not.Nullable();
Map(x => x.CreatedAt)
.Not.Nullable();
}
}
}
- 更新代码配置(替代
hibernate.cfg.xml)
我们不再需要 hibernate.cfg.xml 和 User.hbm.xml 文件了,配置将在 C# 代码中完成。
// Program.cs
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using NHibernate;
using NHibernateGettingStarted.Mappings;
using NHibernateGettingStarted.Models;
// ... 其他 using
var sessionFactory = Fluently.Configure()
.Database(
MsSqlConfiguration.MsSql2012
.ConnectionString(c => c.FromConnectionStringWithKey("MyDbConnection")) // 从 appsettings.json 读取连接字符串
// 或者直接写死
// .ConnectionString(c => c.Server("(localdb)\\MSSQLLocalDB").Database("NHibernateDemo").TrustedConnection())
)
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<User>()) // 自动扫描程序集中继承了 ClassMap 的类
.ExposeConfiguration(cfg => cfg.SetProperty("show_sql", "true"))
.ExposeConfiguration(cfg => cfg.SetProperty("format_sql", "true"))
.ExposeConfiguration(cfg => cfg.SetProperty("hbm2ddl.auto", "update"))
.BuildSessionFactory();
// ... 使用 sessionFactory
注意:为了连接字符串管理,建议在 appsettings.json 中添加,并通过 ConfigurationBuilder 读取。
第四部分:CRUD 操作详解
现在我们有了 ISessionFactory,可以开始进行数据库操作了,所有操作都应该在一个 using 语句中创建 ISession,并确保在事务中执行。
Create: 保存新实体
using (var session = sessionFactory.OpenSession())
{
using (var transaction = session.BeginTransaction())
{
try
{
var newUser = new User
{
Username = "john_doe",
Email = "john.doe@example.com",
CreatedAt = DateTime.Now
};
session.Save(newUser); // 保存对象
transaction.Commit(); // 提交事务
Console.WriteLine($"新用户创建成功,ID: {newUser.Id}");
}
catch (Exception ex)
{
transaction.Rollback(); // 出错则回滚
Console.WriteLine($"创建用户失败: {ex.Message}");
}
}
}
Read: 查询实体
NHibernate 提供了多种查询方式。
- Get() - 根据主键查询
using (var session = sessionFactory.OpenSession())
{
// 假设我们知道 ID 为 1 的用户存在
var user = session.Get<User>(1); // 发送 "SELECT * FROM Users WHERE Id = 1"
if (user != null)
{
Console.WriteLine($"找到用户: {user.Username}, 邮箱: {user.Email}");
}
else
{
Console.WriteLine("未找到 ID 为 1 的用户。");
}
}
- LINQ to NHibernate (推荐)
using (var session = sessionFactory.OpenSession())
{
// 查询所有用户
var allUsers = session.Query<User>().ToList();
Console.WriteLine("--- 所有用户 ---");
foreach (var u in allUsers)
{
Console.WriteLine($"{u.Id}: {u.Username}");
}
// 条件查询
var specificUser = session.Query<User>()
.FirstOrDefault(u => u.Username == "john_doe");
if (specificUser != null)
{
Console.WriteLine($"通过 LINQ 找到用户: {specificUser.Email}");
}
}
Update: 更新实体
更新非常简单,只需修改对象的属性,然后在事务中调用 Update 或 SaveOrUpdate,NHibernate 会检测到对象的变化并生成 UPDATE 语句。
using (var session = sessionFactory.OpenSession())
{
using (var transaction = session.BeginTransaction())
{
try
{
var userToUpdate = session.Get<User>(1);
if (userToUpdate != null)
{
userToUpdate.Email = "john.new.doe@example.com"; // 修改属性
session.Update(userToUpdate); // 通知 NHibernate 对象已更改
transaction.Commit();
Console.WriteLine("用户信息已更新。");
}
}
catch (Exception ex)
{
transaction.Rollback();
Console.WriteLine($"更新失败: {ex.Message}");
}
}
}
注意: NHibernate 的 一级缓存 会跟踪
ISession中加载过的对象,如果你在同一个ISession中获取一个对象,修改它,session.SaveOrUpdate(),NHibernate 甚至不会发送UPDATE语句,因为它知道对象已经被“脏”了,这是 NHibernate 性能优化的一部分。
Delete: 删除实体
using (var session = sessionFactory.OpenSession())
{
using (var transaction = session.BeginTransaction())
{
try
{
var userToDelete = session.Get<User>(1);
if (userToDelete != null)
{
session.Delete(userToDelete); // 从 NHibernate 缓存和数据库中删除
transaction.Commit();
Console.WriteLine("用户已删除。");
}
}
catch (Exception ex)
{
transaction.Rollback();
Console.WriteLine($"删除失败: {ex.Message}");
}
}
}
第五部分:进阶主题
关系映射
这是 ORM 的核心威力所在,假设一个 User 可以拥有多个 Post。
-
实体类
// Post.cs public class Post { public virtual int Id { get; set; } public virtual string Title { get; set; } public virtual string Content { get; set; } public virtual User Author { get; set; } // 多对一关系 } -
Fluent NHibernate 映射
// PostMap.cs public class PostMap : ClassMap<Post> { public PostMap() { Table("Posts"); Id(x => x.Id).GeneratedBy.Identity(); Map(x => x.Title).Length(255).Not.Nullable(); Map(x => x.Content).Not.Nullable(); // 多对一关系 References(x => x.Author) // Post 对应一个 User .Column("AuthorId") // 外键列名 .Not.Nullable(); // 外键不能为空 } } -
使用
// 创建用户和帖子 using (var session = sessionFactory.OpenSession()) { using (var tx = session.BeginTransaction()) { var author = session.Query<User>().FirstOrDefault(); if (author != null) { var post = new Post { Title = "我的第一篇博客", Content = "这是关于 NHibernate 的内容。", Author = author }; session.Save(post); tx.Commit(); } } }
NHibernate 还支持一对一、一对多、多对多等复杂关系,配置方式类似,只是使用的 Fluent API 不同(如 HasMany, HasManyToMany)。
事务与并发控制
事务是保证数据一致性的关键,每个数据库操作单元都应该在事务中。
- 并发控制:当多个用户同时修改同一数据时,可能会发生冲突,NHibernate 提供了乐观锁和悲观锁机制。
- 乐观锁:最常用,通过在表中添加一个版本号(
timestamp)或行版本(rowversion)列来实现,当一个事务读取数据时,会同时读取版本号,当要更新时,会检查版本号是否与读取时一致,如果一致,则更新并递增版本号;如果不一致,说明数据已被其他事务修改,更新失败,抛出StaleObjectStateException异常。 - 悲观锁:直接在读取数据时锁定数据库行,直到事务结束,其他事务无法修改该行,NHibernate 可以通过
session.Lock()或 HQL 的with lock mode实现。
- 乐观锁:最常用,通过在表中添加一个版本号(
缓存机制
NHibernate 有一个强大的多级缓存系统,可以显著提高性能。
-
一级缓存 (L1 Cache):
- 作用域:
ISession。 - 生命周期:与
ISession相同。 - 特点:默认开启,无法关闭,它缓存了当前
ISession中加载和保存的对象,当同一个ISession多次请求同一个对象时,NHibernate 会直接从缓存返回,不会查询数据库。
- 作用域:
-
二级缓存 (L2 Cache):
- 作用域:
ISessionFactory。 - 生命周期:与应用程序相同。
- 特点:需要手动配置和开启,它被所有
ISession共享,可以缓存实体、集合和查询结果,这极大地减少了数据库的访问次数。 - 要使用二级缓存,需要安装一个缓存提供程序(如
NHibernate.Caches.SysCache或NHibernate.Caches.Redis)并进行相应配置。
- 作用域:
第六部分:最佳实践与资源
最佳实践
- 使用依赖注入:不要在代码中硬编码
new出ISessionFactory,应该使用一个 DI 容器(如 Microsoft.Extensions.DependencyInjection, Autofac, Windsor)来管理ISessionFactory的生命周期(单例),并为每个请求(或UnitOfWork)注入ISession(作用域或瞬态)。 - 仓储模式:将数据访问逻辑封装在仓储类中,你的业务逻辑层调用仓储,而不是直接操作 NHibernate,这可以解耦业务逻辑和数据访问。
- 使用
IUnitOfWork模式:将一个业务操作(如一个 HTTP 请求,一个控制台命令)定义为一个工作单元,在这个工作单元内,共享一个ISession和一个ITransaction。 - 谨慎使用
hbm2ddl.auto:在生产环境中,绝对不要使用create或update,数据库结构变更应该通过数据库迁移脚本(如 DbUp, Flyway)来管理。 - 优先选择 LINQ:对于复杂查询,优先使用 LINQ to NHibernate,它比 HQL 更类型安全、更易于编写和维护。
推荐资源
- 官方文档:
- NHibernate 官方文档 (虽然有些旧,但仍是权威)
- Fluent NHibernate 官方文档
- 社区与教程:
- Ayende Rahien (NHibernate 的主要贡献者) 的博客:里面有大量关于 NHibernate 内部原理和最佳实践的深度文章。
- CodeProject:搜索 "NHibernate Tutorial",有很多优秀的入门和进阶文章。
- Stack Overflow:遇到问题时,这里是最好的求助地方。
- 书籍:
- NHibernate 4.0 Beginner's Guide (虽然版本较旧,但基础概念依然适用)
- NHibernate in Action (经典书籍,但基于较老版本)
这份教程涵盖了 NHibernate 的核心内容,从简单的 CRUD 到复杂的关系映射和高级特性,希望它能成为你学习 NHibernate 的良好起点,祝你学习愉快!
