NHibernate
NHibernate is a mature, highly flexible enterprise ORM for .NET. Developed as a C# port of Java Hibernate, it's an open-source OR/M framework with over 15 years of proven track record. It provides precise SQL control, multi-database support, enterprise-grade caching, and rich mapping methodologies, optimized for building complex enterprise systems. Through the Data Mapper pattern, it achieves separation between domain models and database schemas, delivering exceptional flexibility for legacy system integration and complex business logic implementation.
GitHub Overview
Topics
Star History
Library
NHibernate
Overview
NHibernate is a mature, highly flexible enterprise ORM for .NET. Developed as a C# port of Java Hibernate, it's an open-source OR/M framework with over 15 years of proven track record. It provides precise SQL control, multi-database support, enterprise-grade caching, and rich mapping methodologies, optimized for building complex enterprise systems. Through the Data Mapper pattern, it achieves separation between domain models and database schemas, delivering exceptional flexibility for legacy system integration and complex business logic implementation.
Details
NHibernate 2025 edition continues to be utilized on modern platforms as a mature ORM with .NET Core and .NET 5+ support. It provides diverse query description methods through HQL (Hibernate Query Language), Criteria API, and LINQ to NHibernate, addressing complex business requirements. It supports flexible configuration approaches including XML mapping, Fluent NHibernate, and Attribute-based mapping. Comprehensive performance optimization features for enterprise environments include multi-layered caching systems (First-Level Cache, Second-Level Cache, Query Cache), Lazy Loading, Batch Fetching, and Connection Pooling.
Key Features
- Mature Data Mapper Pattern: Complete separation between domain models and database
- Rich Query Methods: Trinity of HQL, Criteria API, and LINQ to NHibernate
- Multi-database Support: 20+ RDBMS including Oracle, SQL Server, MySQL, PostgreSQL
- Flexible Mapping: Diverse configuration methods via XML, Fluent, and Attributes
- Enterprise Caching: Multi-layered caching and query optimization
- Advanced Inheritance Mapping: Comprehensive support for Table per Hierarchy/Class/Subclass
Advantages and Disadvantages
Advantages
- Solid position in enterprise field with over 15 years of stable track record
- Advanced SQL control and flexible handling of complex business logic
- Overwhelming advantage in integration with legacy database schemas
- Rich documentation, books, and community resources
- Transparency and freedom through open source (Apache License 2.0)
- Continued availability on latest platforms with .NET 5/6/7/8 support
Disadvantages
- Higher learning cost and configuration complexity compared to Entity Framework Core
- Mapping configuration tends to be verbose, creating overhead for small projects
- Limited support for modern development practices (Code First, Migrations)
- Community-based support system without official Microsoft support
- LINQ implementation not as complete as Entity Framework in some scenarios
- High acquisition cost for new developers with significant team training burden
Reference Pages
Code Examples
Installation and Project Setup
# Install NuGet packages with .NET CLI
dotnet add package NHibernate
dotnet add package NHibernate.Mapping.Attributes
dotnet add package FluentNHibernate # For Fluent mapping
# SQL Server driver
dotnet add package System.Data.SqlClient
# MySQL driver
dotnet add package MySql.Data
# PostgreSQL driver
dotnet add package Npgsql
# Console application creation example
dotnet new console -n NHibernateDemo
cd NHibernateDemo
<!-- Project file (.csproj) reference example -->
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NHibernate" Version="5.5.2" />
<PackageReference Include="NHibernate.Mapping.Attributes" Version="5.5.2" />
<PackageReference Include="FluentNHibernate" Version="3.3.0" />
<PackageReference Include="System.Data.SqlClient" Version="4.8.6" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Mappings\*.hbm.xml" />
</ItemGroup>
</Project>
Basic Entity Definition and Mapping
// Domain/User.cs - Domain entity
using System;
using System.Collections.Generic;
namespace NHibernateDemo.Domain
{
public class User
{
public virtual int Id { get; set; }
public virtual string Username { get; set; }
public virtual string Email { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual DateTime CreatedAt { get; set; }
public virtual bool IsActive { get; set; }
// One-to-Many relationship
public virtual ISet<Post> Posts { get; set; } = new HashSet<Post>();
// Computed property
public virtual string FullName => $"{FirstName} {LastName}";
// Business logic
public virtual void Activate()
{
IsActive = true;
}
public virtual void Deactivate()
{
IsActive = false;
}
}
public class Post
{
public virtual int Id { get; set; }
public virtual string Title { get; set; }
public virtual string Content { get; set; }
public virtual DateTime CreatedAt { get; set; }
public virtual DateTime? UpdatedAt { get; set; }
public virtual bool Published { get; set; }
// Many-to-One relationship
public virtual User Author { get; set; }
// One-to-Many relationship
public virtual ISet<Comment> Comments { get; set; } = new HashSet<Comment>();
}
public class Comment
{
public virtual int Id { get; set; }
public virtual string Content { get; set; }
public virtual DateTime CreatedAt { get; set; }
// Many-to-One relationships
public virtual User Author { get; set; }
public virtual Post Post { get; set; }
}
}
XML Mapping Configuration
<!-- Mappings/User.hbm.xml -->
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="NHibernateDemo.Domain"
assembly="NHibernateDemo">
<class name="User" table="Users">
<id name="Id" column="UserId" type="int">
<generator class="identity" />
</id>
<property name="Username" column="Username" type="string"
length="50" not-null="true" unique="true" />
<property name="Email" column="Email" type="string"
length="255" not-null="true" unique="true" />
<property name="FirstName" column="FirstName" type="string" length="50" />
<property name="LastName" column="LastName" type="string" length="50" />
<property name="CreatedAt" column="CreatedAt" type="datetime" not-null="true" />
<property name="IsActive" column="IsActive" type="boolean" not-null="true" />
<!-- One-to-Many relationship -->
<set name="Posts" table="Posts" cascade="all-delete-orphan"
lazy="true" inverse="true">
<key column="AuthorId" />
<one-to-many class="Post" />
</set>
</class>
</hibernate-mapping>
<!-- Mappings/Post.hbm.xml -->
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="NHibernateDemo.Domain"
assembly="NHibernateDemo">
<class name="Post" table="Posts">
<id name="Id" column="PostId" type="int">
<generator class="identity" />
</id>
<property name="Title" column="Title" type="string"
length="200" not-null="true" />
<property name="Content" column="Content" type="text" />
<property name="CreatedAt" column="CreatedAt" type="datetime" not-null="true" />
<property name="UpdatedAt" column="UpdatedAt" type="datetime" />
<property name="Published" column="Published" type="boolean" not-null="true" />
<!-- Many-to-One relationship -->
<many-to-one name="Author" class="User" column="AuthorId"
not-null="true" cascade="none" />
<!-- One-to-Many relationship -->
<set name="Comments" table="Comments" cascade="all-delete-orphan"
lazy="true" inverse="true">
<key column="PostId" />
<one-to-many class="Comment" />
</set>
</class>
</hibernate-mapping>
NHibernate Configuration and Session Factory
// Configuration/NHibernateHelper.cs
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;
using System.Reflection;
namespace NHibernateDemo.Configuration
{
public static class NHibernateHelper
{
private static ISessionFactory _sessionFactory;
private static readonly object Lock = new object();
public static ISessionFactory SessionFactory
{
get
{
if (_sessionFactory == null)
{
lock (Lock)
{
if (_sessionFactory == null)
{
_sessionFactory = BuildSessionFactory();
}
}
}
return _sessionFactory;
}
}
private static ISessionFactory BuildSessionFactory()
{
try
{
var configuration = new NHibernate.Cfg.Configuration();
// Database configuration
configuration.SetProperty("connection.driver_class",
"NHibernate.Driver.SqlClientDriver");
configuration.SetProperty("connection.connection_string",
"Server=localhost;Database=NHibernateDemo;Integrated Security=true;TrustServerCertificate=true");
configuration.SetProperty("dialect",
"NHibernate.Dialect.MsSql2012Dialect");
// Basic settings
configuration.SetProperty("show_sql", "true");
configuration.SetProperty("format_sql", "true");
configuration.SetProperty("hbm2ddl.auto", "update"); // create, update, validate
// Cache settings
configuration.SetProperty("cache.use_second_level_cache", "true");
configuration.SetProperty("cache.provider_class",
"NHibernate.Cache.HashtableCacheProvider");
configuration.SetProperty("cache.use_query_cache", "true");
// Batch processing settings
configuration.SetProperty("adonet.batch_size", "20");
// Add mapping files
configuration.AddAssembly(Assembly.GetExecutingAssembly());
return configuration.BuildSessionFactory();
}
catch (Exception ex)
{
throw new Exception("SessionFactory creation error", ex);
}
}
public static ISession OpenSession()
{
return SessionFactory.OpenSession();
}
public static void CloseSessionFactory()
{
if (_sessionFactory != null)
{
_sessionFactory.Close();
}
}
}
}
// appsettings.json configuration management example
{
"ConnectionStrings": {
"DefaultConnection": "Server=localhost;Database=NHibernateDemo;Integrated Security=true;TrustServerCertificate=true"
},
"NHibernate": {
"ShowSql": true,
"FormatSql": true,
"CacheEnabled": true,
"BatchSize": 20
}
}
Basic CRUD Operations and Session Management
// Services/UserService.cs
using NHibernate;
using NHibernateDemo.Configuration;
using NHibernateDemo.Domain;
namespace NHibernateDemo.Services
{
public class UserService
{
// Create - User creation
public int CreateUser(User user)
{
using (var session = NHibernateHelper.OpenSession())
using (var transaction = session.BeginTransaction())
{
try
{
user.CreatedAt = DateTime.Now;
var id = (int)session.Save(user);
transaction.Commit();
return id;
}
catch
{
transaction.Rollback();
throw;
}
}
}
// Read - User retrieval
public User GetUser(int id)
{
using (var session = NHibernateHelper.OpenSession())
{
return session.Get<User>(id);
}
}
public User GetUserByUsername(string username)
{
using (var session = NHibernateHelper.OpenSession())
{
return session.Query<User>()
.FirstOrDefault(u => u.Username == username);
}
}
public IList<User> GetActiveUsers()
{
using (var session = NHibernateHelper.OpenSession())
{
return session.Query<User>()
.Where(u => u.IsActive)
.OrderBy(u => u.Username)
.ToList();
}
}
// Update - User update
public void UpdateUser(User user)
{
using (var session = NHibernateHelper.OpenSession())
using (var transaction = session.BeginTransaction())
{
try
{
session.Update(user);
transaction.Commit();
}
catch
{
transaction.Rollback();
throw;
}
}
}
// Delete - User deletion
public void DeleteUser(int id)
{
using (var session = NHibernateHelper.OpenSession())
using (var transaction = session.BeginTransaction())
{
try
{
var user = session.Get<User>(id);
if (user != null)
{
session.Delete(user);
}
transaction.Commit();
}
catch
{
transaction.Rollback();
throw;
}
}
}
// Complex query example
public IList<User> SearchUsers(string searchTerm, bool includeInactive = false)
{
using (var session = NHibernateHelper.OpenSession())
{
var query = session.Query<User>()
.Where(u => u.Username.Contains(searchTerm) ||
u.Email.Contains(searchTerm) ||
u.FirstName.Contains(searchTerm) ||
u.LastName.Contains(searchTerm));
if (!includeInactive)
{
query = query.Where(u => u.IsActive);
}
return query.OrderBy(u => u.Username).ToList();
}
}
}
}
Advanced Queries with HQL and Criteria API
// Services/PostService.cs
using NHibernate;
using NHibernate.Criterion;
using NHibernateDemo.Configuration;
using NHibernateDemo.Domain;
namespace NHibernateDemo.Services
{
public class PostService
{
// Queries using HQL (Hibernate Query Language)
public IList<Post> GetPostsByAuthorHQL(string username)
{
using (var session = NHibernateHelper.OpenSession())
{
var hql = @"
FROM Post p
INNER JOIN FETCH p.Author a
WHERE a.Username = :username
AND p.Published = true
ORDER BY p.CreatedAt DESC";
return session.CreateQuery(hql)
.SetParameter("username", username)
.List<Post>();
}
}
// Named queries (defined in mapping files)
public IList<Post> GetRecentPosts(int days)
{
using (var session = NHibernateHelper.OpenSession())
{
return session.GetNamedQuery("GetRecentPosts")
.SetParameter("days", days)
.List<Post>();
}
}
// Dynamic queries using Criteria API
public IList<Post> SearchPosts(string title = null, string authorName = null,
DateTime? fromDate = null, DateTime? toDate = null, bool? published = null)
{
using (var session = NHibernateHelper.OpenSession())
{
var criteria = session.CreateCriteria<Post>("p")
.CreateAlias("p.Author", "a");
// Add dynamic conditions
if (!string.IsNullOrEmpty(title))
{
criteria.Add(Restrictions.Like("p.Title", title, MatchMode.Anywhere));
}
if (!string.IsNullOrEmpty(authorName))
{
criteria.Add(Restrictions.Like("a.Username", authorName, MatchMode.Anywhere));
}
if (fromDate.HasValue)
{
criteria.Add(Restrictions.Ge("p.CreatedAt", fromDate.Value));
}
if (toDate.HasValue)
{
criteria.Add(Restrictions.Le("p.CreatedAt", toDate.Value));
}
if (published.HasValue)
{
criteria.Add(Restrictions.Eq("p.Published", published.Value));
}
return criteria
.AddOrder(Order.Desc("p.CreatedAt"))
.List<Post>();
}
}
// Paginated queries
public IList<Post> GetPostsPaged(int pageNumber, int pageSize)
{
using (var session = NHibernateHelper.OpenSession())
{
return session.Query<Post>()
.Where(p => p.Published)
.OrderByDescending(p => p.CreatedAt)
.Skip((pageNumber - 1) * pageSize)
.Take(pageSize)
.ToList();
}
}
// Statistical queries
public object GetPostStatistics()
{
using (var session = NHibernateHelper.OpenSession())
{
var hql = @"
SELECT
COUNT(*) as TotalPosts,
COUNT(CASE WHEN p.Published = true THEN 1 END) as PublishedPosts,
AVG(SIZE(p.Comments)) as AvgCommentsPerPost,
MAX(p.CreatedAt) as LatestPostDate
FROM Post p";
return session.CreateQuery(hql).UniqueResult();
}
}
}
}
Mapping with Fluent NHibernate
// Mappings/UserMap.cs
using FluentNHibernate.Mapping;
using NHibernateDemo.Domain;
namespace NHibernateDemo.Mappings
{
public class UserMap : ClassMap<User>
{
public UserMap()
{
Table("Users");
// Primary Key
Id(x => x.Id, "UserId").GeneratedBy.Identity();
// Properties
Map(x => x.Username).Column("Username").Length(50).Not.Nullable().Unique();
Map(x => x.Email).Column("Email").Length(255).Not.Nullable().Unique();
Map(x => x.FirstName).Column("FirstName").Length(50);
Map(x => x.LastName).Column("LastName").Length(50);
Map(x => x.CreatedAt).Column("CreatedAt").Not.Nullable();
Map(x => x.IsActive).Column("IsActive").Not.Nullable();
// Relationships
HasMany(x => x.Posts)
.KeyColumn("AuthorId")
.Cascade.AllDeleteOrphan()
.Inverse()
.LazyLoad();
}
}
public class PostMap : ClassMap<Post>
{
public PostMap()
{
Table("Posts");
Id(x => x.Id, "PostId").GeneratedBy.Identity();
Map(x => x.Title).Column("Title").Length(200).Not.Nullable();
Map(x => x.Content).Column("Content").CustomType("StringClob");
Map(x => x.CreatedAt).Column("CreatedAt").Not.Nullable();
Map(x => x.UpdatedAt).Column("UpdatedAt");
Map(x => x.Published).Column("Published").Not.Nullable();
References(x => x.Author)
.Column("AuthorId")
.Not.Nullable()
.LazyLoad();
HasMany(x => x.Comments)
.KeyColumn("PostId")
.Cascade.AllDeleteOrphan()
.Inverse()
.LazyLoad();
}
}
}
// Configuration/FluentNHibernateHelper.cs
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using NHibernate;
namespace NHibernateDemo.Configuration
{
public static class FluentNHibernateHelper
{
private static ISessionFactory _sessionFactory;
public static ISessionFactory SessionFactory
{
get
{
if (_sessionFactory == null)
{
_sessionFactory = CreateSessionFactory();
}
return _sessionFactory;
}
}
private static ISessionFactory CreateSessionFactory()
{
return Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2012
.ConnectionString("Server=localhost;Database=NHibernateDemo;Integrated Security=true;TrustServerCertificate=true")
.ShowSql()
.FormatSql())
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<UserMap>())
.Cache(c => c
.UseSecondLevelCache()
.ProviderClass<NHibernate.Cache.HashtableCacheProvider>()
.UseQueryCache())
.BuildSessionFactory();
}
public static ISession OpenSession()
{
return SessionFactory.OpenSession();
}
}
}
Transactions and Batch Processing
// Services/BatchService.cs
using NHibernate;
using NHibernateDemo.Configuration;
using NHibernateDemo.Domain;
namespace NHibernateDemo.Services
{
public class BatchService
{
// Batch insert processing
public void BulkInsertUsers(IEnumerable<User> users)
{
using (var session = NHibernateHelper.OpenSession())
using (var transaction = session.BeginTransaction())
{
try
{
int count = 0;
foreach (var user in users)
{
user.CreatedAt = DateTime.Now;
session.Save(user);
// Flush every batch size
if (++count % 20 == 0)
{
session.Flush();
session.Clear();
}
}
transaction.Commit();
}
catch
{
transaction.Rollback();
throw;
}
}
}
// Batch update processing
public int BulkUpdateUserStatus(bool isActive, DateTime beforeDate)
{
using (var session = NHibernateHelper.OpenSession())
using (var transaction = session.BeginTransaction())
{
try
{
var hql = @"
UPDATE User u
SET u.IsActive = :isActive
WHERE u.CreatedAt < :beforeDate";
var result = session.CreateQuery(hql)
.SetParameter("isActive", isActive)
.SetParameter("beforeDate", beforeDate)
.ExecuteUpdate();
transaction.Commit();
return result;
}
catch
{
transaction.Rollback();
throw;
}
}
}
// Complex transaction processing
public void TransferPostOwnership(int fromUserId, int toUserId)
{
using (var session = NHibernateHelper.OpenSession())
using (var transaction = session.BeginTransaction())
{
try
{
var fromUser = session.Get<User>(fromUserId);
var toUser = session.Get<User>(toUserId);
if (fromUser == null || toUser == null)
{
throw new ArgumentException("Invalid user ID");
}
// Change post ownership
var posts = session.Query<Post>()
.Where(p => p.Author.Id == fromUserId)
.ToList();
foreach (var post in posts)
{
post.Author = toUser;
session.Update(post);
}
// Log recording
var log = new SystemLog
{
Message = $"Transferred {posts.Count} posts from user {fromUserId} to {toUserId}",
CreatedAt = DateTime.Now
};
session.Save(log);
transaction.Commit();
}
catch
{
transaction.Rollback();
throw;
}
}
}
}
}