杰瑞科技汇

NHibernate教程从哪开始学?

NHibernate 全面教程:从入门到精通

目录

  1. 第一部分:NHibernate 简介

    NHibernate教程从哪开始学?-图1
    (图片来源网络,侵删)
    • 什么是 NHibernate?
    • 为什么选择 NHibernate?
    • NHibernate 与 Entity Framework 的对比
  2. 第二部分:环境准备与第一个项目

    • 环境要求
    • 创建控制台应用
    • 安装 NHibernate NuGet 包
    • 配置 NHibernate
  3. 第三部分:核心概念与映射

    • 核心概念:ISession, ISessionFactory, ITransaction
    • 第一个实体类与映射文件
    • 配置文件 (hibernate.cfg.xml)
    • 使用 Fluent NHibernate 进行代码化配置(可选,但推荐)
  4. 第四部分:CRUD 操作详解

    • Create: 保存新实体
    • Read: 查询实体
    • Update: 更新实体
    • Delete: 删除实体
    • 使用 LINQ to NHibernate 进行查询
  5. 第五部分:进阶主题

    NHibernate教程从哪开始学?-图2
    (图片来源网络,侵删)
    • 关系映射(一对一、一对多、多对多)
    • 事务与并发控制
    • 缓存机制
    • 批量操作与性能优化
  6. 第六部分:最佳实践与资源

    • 最佳实践
    • 推荐资源

第一部分:NHibernate 简介

什么是 NHibernate?

NHibernate 是一个成熟、强大、开源的 对象关系映射 框架,专门为 .NET 平台设计,它的灵感来自著名的 Java ORM 框架 Hibernate。

NHibernate 的作用是在 面向对象的领域模型关系型数据库 之间建立一个桥梁,它允许你使用 C# 或 VB.NET 对象来操作数据库,而无需编写大量的 SQL 语句,你操作的是对象,NHibernate 会自动将这些操作翻译成相应的 SQL 并执行。

为什么选择 NHibernate?

  1. 成熟稳定:NHibernate 存在已久,经过了大量项目的检验,非常稳定可靠。
  2. 功能强大:支持几乎所有复杂的数据库操作,如高级查询、继承映射、缓存、事务等。
  3. 数据库无关性:你可以轻松地更换底层数据库(例如从 SQL Server 换到 MySQL 或 PostgreSQL),而几乎不需要修改你的 C# 代码,NHibernate 会自动生成对应数据库的 SQL 方言。
  4. 灵活性高:提供了 XML 映射和 Fluent NHibernate(代码优先)两种方式,对开发者非常友好。
  5. 社区支持:拥有一个活跃的社区,你可以找到大量的文档、教程和解决方案。

NHibernate 与 Entity Framework 的对比

特性 NHibernate Entity Framework (EF Core)
控制反转 ,对 SQL 的生成和执行有更精细的控制。 ,抽象层次更高,有时难以控制生成的 SQL。
成熟度 非常高,十多年的发展,非常稳定。 (EF Core),EF Core 已经非常成熟,但相比 NHibernate 仍属年轻。
学习曲线 较陡,概念较多(如 Session, Cache, HQL)。 较平缓,与 .NET 生态集成紧密,上手更容易。
灵活性 极高,可以处理非常复杂的映射场景。 较高,能满足绝大多数业务场景,但极端复杂场景可能受限。
数据库支持 极广,几乎所有主流数据库。 较广,官方支持主流数据库,社区提供更多支持。
发展方向 稳定维护,是遗留系统维护和需要高度控制场景的首选。 主流,微软主推,是 .NET 新项目的默认选择。

如果你的项目是遗留系统,或者你对数据库性能和 SQL 控制有极高要求,NHibernate 是绝佳选择,对于新项目,尤其是中小型项目,EF Core 可能是更简单、更快速的选择。

NHibernate教程从哪开始学?-图3
(图片来源网络,侵删)

第二部分:环境准备与第一个项目

环境要求

  • .NET SDK (.NET 6.0 或更高版本)
  • 一个 IDE (Visual Studio 2025 或 Rider)
  • 一个数据库 (SQL Server Express, SQLite 用于开发非常方便)

创建控制台应用

  1. 打开 Visual Studio,创建一个新的 "控制台应用" (.NET 6.0 或更高版本)。
  2. 给项目命名,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

  1. 在项目中添加一个 XML 文件,命名为 hibernate.cfg.xml
  2. 将其 "复制到输出目录" 属性设置为 "如果较新则复制"。
  3. 设置其 "生成操作" 为 "内容"。
  4. 编写配置文件内容(以 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),数据库操作应该在事务中执行,以确保数据的原子性(要么全部成功,要么全部失败)。

第一个实体类与映射文件

  1. 创建实体类 (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; }
    }
}
  1. 创建映射文件 (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": 表示该列不能为空。
  1. 更新 hibernate.cfg.xml

<session-factory> 节点中添加我们刚刚创建的映射文件引用。

<!-- 在 hibernate.cfg.xml 中添加 -->
<mapping resource="NHibernateGettingStarted.Models.User.hbm.xml" />

使用 Fluent NHibernate 进行代码化配置(强烈推荐)

使用 XML 文件进行配置和映射虽然直观,但在大型项目中会变得非常繁琐。Fluent NHibernate 允许你使用 C# 代码来配置 NHibernate 和定义映射,这更符合 .NET 开发者的习惯。

  1. 安装 Fluent NHibernate NuGet 包

    Install-Package FluentNHibernate
  2. 创建映射类 (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();
        }
    }
}
  1. 更新代码配置(替代 hibernate.cfg.xml

我们不再需要 hibernate.cfg.xmlUser.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 提供了多种查询方式。

  1. 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 的用户。");
    }
}
  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: 更新实体

更新非常简单,只需修改对象的属性,然后在事务中调用 UpdateSaveOrUpdate,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

  1. 实体类

    // 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; } // 多对一关系
    }
  2. 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();      // 外键不能为空
        }
    }
  3. 使用

    // 创建用户和帖子
    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 有一个强大的多级缓存系统,可以显著提高性能。

  1. 一级缓存 (L1 Cache)

    • 作用域:ISession
    • 生命周期:与 ISession 相同。
    • 特点:默认开启,无法关闭,它缓存了当前 ISession 中加载和保存的对象,当同一个 ISession 多次请求同一个对象时,NHibernate 会直接从缓存返回,不会查询数据库。
  2. 二级缓存 (L2 Cache)

    • 作用域:ISessionFactory
    • 生命周期:与应用程序相同。
    • 特点:需要手动配置和开启,它被所有 ISession 共享,可以缓存实体、集合和查询结果,这极大地减少了数据库的访问次数。
    • 要使用二级缓存,需要安装一个缓存提供程序(如 NHibernate.Caches.SysCacheNHibernate.Caches.Redis)并进行相应配置。

第六部分:最佳实践与资源

最佳实践

  1. 使用依赖注入:不要在代码中硬编码 newISessionFactory,应该使用一个 DI 容器(如 Microsoft.Extensions.DependencyInjection, Autofac, Windsor)来管理 ISessionFactory 的生命周期(单例),并为每个请求(或 UnitOfWork)注入 ISession(作用域或瞬态)。
  2. 仓储模式:将数据访问逻辑封装在仓储类中,你的业务逻辑层调用仓储,而不是直接操作 NHibernate,这可以解耦业务逻辑和数据访问。
  3. 使用 IUnitOfWork 模式:将一个业务操作(如一个 HTTP 请求,一个控制台命令)定义为一个工作单元,在这个工作单元内,共享一个 ISession 和一个 ITransaction
  4. 谨慎使用 hbm2ddl.auto:在生产环境中,绝对不要使用 createupdate,数据库结构变更应该通过数据库迁移脚本(如 DbUp, Flyway)来管理。
  5. 优先选择 LINQ:对于复杂查询,优先使用 LINQ to NHibernate,它比 HQL 更类型安全、更易于编写和维护。

推荐资源

  • 官方文档
  • 社区与教程
    • Ayende Rahien (NHibernate 的主要贡献者) 的博客:里面有大量关于 NHibernate 内部原理和最佳实践的深度文章。
    • CodeProject:搜索 "NHibernate Tutorial",有很多优秀的入门和进阶文章。
    • Stack Overflow:遇到问题时,这里是最好的求助地方。
  • 书籍
    • NHibernate 4.0 Beginner's Guide (虽然版本较旧,但基础概念依然适用)
    • NHibernate in Action (经典书籍,但基于较老版本)

这份教程涵盖了 NHibernate 的核心内容,从简单的 CRUD 到复杂的关系映射和高级特性,希望它能成为你学习 NHibernate 的良好起点,祝你学习愉快!

分享:
扫描分享到社交APP
上一篇
下一篇