Что такое NHibernate и зачем он нужен?
NHibernate — это мощная и зрелая объектно-реляционная библиотека (ORM) для платформы .NET, написанная на C#. Она является портом знаменитой Java-библиотеки Hibernate. NHibernate решает фундаментальную проблему несоответствия между объектно-ориентированной моделью данных в вашем C#-приложении и реляционной моделью данных в базе данных (SQL Server, PostgreSQL, MySQL, Oracle и др.).
Основная задача NHibernate — автоматизировать преобразование данных между объектами C# (POCO — Plain Old CLR Objects) и записями в таблицах базы данных. Вместо того чтобы писать сотни строк однотипного SQL-кода для CRUD-операций (Create, Read, Update, Delete), вы работаете с объектами, а NHibernate берет на себя генерацию и выполнение SQL-запросов.
Зачем использовать NHibernate?
- Повышение производительности разработки: Вы пишете меньше кода, особенно boilerplate-кода для работы с ADO.NET.
- Уменьшение количества ошибок: SQL-запросы генерируются автоматически, что снижает риск синтаксических ошибок и SQL-инъекций (при правильном использовании).
- Независимость от базы данных: Вы можете сменить СУБД, изменив только строку подключения и диалект NHibernate, не переписывая бизнес-логику.
- Управление кэшированием: Встроенные механизмы кэширования первого и второго уровня значительно ускоряют работу приложения.
- Lazy Loading (Отложенная загрузка): Связанные данные загружаются только тогда, когда они действительно нужны, что экономит ресурсы.
- Мощный язык запросов: HQL (Hibernate Query Language) и LINQ to NHibernate позволяют писать типобезопасные запросы.
Установка NHibernate
NHibernate распространяется через NuGet — менеджер пакетов для .NET. Установка очень проста.
Через консоль диспетчера пакетов (Package Manager Console):
Install-Package NHibernate
Через .NET CLI (для проектов .NET Core / .NET 5+):
dotnet add package NHibernate
Дополнительные пакеты (по необходимости):
- NHibernate.Caches.CoreMemoryCache — для кэширования второго уровня.
- System.Data.SqlClient или Microsoft.Data.SqlClient — для работы с SQL Server.
- Npgsql — для работы с PostgreSQL.
- MySql.Data — для работы с MySQL.
После установки вам потребуется настроить конфигурацию (обычно в файле app.config, web.config или appsettings.json) или через Fluent-конфигурацию в коде.
Быстрый старт с примером кода
Рассмотрим простейший пример работы с NHibernate. Допустим, у нас есть таблица Products в базе данных SQL Server.
1. Создаем класс сущности (Entity):
public class Product
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual decimal Price { get; set; }
}
Важно: Все свойства должны быть virtual — это требование NHibernate для реализации механизмов прокси (lazy loading, отслеживание изменений).
2. Создаем файл маппинга (Product.hbm.xml):
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="YourAssemblyName"
namespace="YourNamespace">
<class name="Product" table="Products">
<id name="Id" column="ProductId">
<generator class="native" />
</id>
<property name="Name" column="ProductName" length="100" not-null="true" />
<property name="Price" column="UnitPrice" precision="19" scale="4" />
</class>
</hibernate-mapping>
Этот XML-файл говорит NHibernate, как сопоставить класс Product с таблицей Products и её столбцами.
3. Настраиваем и используем NHibernate:
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Tool.hbm2ddl;
public class NhibernateHelper
{
private static ISessionFactory _sessionFactory;
public static ISessionFactory CreateSessionFactory()
{
if (_sessionFactory == null)
{
var configuration = new Configuration();
configuration.Configure(); // Читает настройки из app.config/web.config
configuration.AddAssembly(typeof(Product).Assembly); // Добавляет файлы .hbm.xml из сборки
_sessionFactory = configuration.BuildSessionFactory();
}
return _sessionFactory;
}
public static void SaveProduct()
{
using (var session = CreateSessionFactory().OpenSession())
using (var transaction = session.BeginTransaction())
{
var newProduct = new Product { Name = "Laptop", Price = 1200.00m };
session.Save(newProduct);
transaction.Commit();
Console.WriteLine($"Product saved with ID: {newProduct.Id}");
}
}
}
4. Конфигурация в app.config:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="hibernate-configuration" type="NHibernate.Cfg.ConfigurationSectionHandler, NHibernate" />
</configSections>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
<property name="connection.connection_string">Server=.;Database=MyDB;Integrated Security=True;</property>
<property name="dialect">NHibernate.Dialect.MsSql2012Dialect</property>
</session-factory>
</hibernate-configuration>
</configuration>
Основные методы и функции NHibernate
Ниже приведены ключевые методы, которые вы будете использовать ежедневно при работе с NHibernate.
1. session.Save(object entity)
Что делает: Сохраняет новый экземпляр сущности в базу данных. После вызова объект становится постоянным (persistent).
Параметры: entity — объект сущности, который нужно сохранить.
Пример:
var customer = new Customer { Name = "John Doe", Email = "john@example.com" };
var savedId = session.Save(customer);
Console.WriteLine($"Customer saved with ID: {savedId}");
2. session.Update(object entity)
Что делает: Обновляет существующую запись в базе данных, соответствующую переданному объекту. Объект должен быть detached (отсоединенным) или persistent.
Параметры: entity — объект с измененными данными.
Пример:
var product = session.Get<Product>(1); // Загружаем объект
product.Price = 1500.00m; // Меняем цену
session.Update(product); // Явно обновляем (можно не вызывать, если сессия открыта)
session.Flush(); // Принудительно синхронизируем с БД
3. session.Delete(object entity)
Что делает: Удаляет запись из базы данных, соответствующую переданному объекту.
Параметры: entity — объект, который нужно удалить.
Пример:
var order = session.Get<Order>(42);
session.Delete(order);
session.Flush();
4. session.Get<T>(object id)
Что делает: Загружает сущность по её первичному ключу. Если запись не найдена, возвращает null. Использует кэш первого уровня.
Параметры: id — значение первичного ключа.
Пример:
var user = session.Get<User>(5);
if (user != null)
{
Console.WriteLine($"User: {user.Username}");
}
5. session.Load<T>(object id)
Что делает: Возвращает прокси-объект для сущности с указанным ID. Реальный запрос к БД не выполняется до первого обращения к свойству, отличному от ID. Если записи не существует, выбросит исключение ObjectNotFoundException при попытке доступа.
Параметры: id — значение первичного ключа.
Пример:
// Получаем прокси, запрос к БД еще не сделан
var categoryProxy = session.Load<Category>(10);
// Только здесь выполняется SELECT
Console.WriteLine($"Category name: {categoryProxy.Name}");
6. session.CreateQuery(string hql)
Что делает: Создает запрос на языке HQL (Hibernate Query Language), который затем можно выполнить. HQL похож на SQL, но оперирует именами классов и свойств, а не таблиц и столбцов.
Параметры: hql — строка HQL-запроса.
Пример:
IQuery query = session.CreateQuery("from Product p where p.Price > :minPrice");
query.SetParameter("minPrice", 100.00m);
IList<Product> expensiveProducts = query.List<Product>();
foreach (var p in expensiveProducts)
{
Console.WriteLine($"{p.Name} - {p.Price}");
}
7. session.Query<T>() (LINQ to NHibernate)
Что делает: Предоставляет возможность писать типобезопасные LINQ-запросы. Это наиболее предпочтительный способ запросов в современном C#.
Параметры: Нет (возвращает IQueryable<T>).
Пример:
var recentOrders = session.Query<Order>()
.Where(o => o.OrderDate >= DateTime.Now.AddDays(-7))
.OrderByDescending(o => o.TotalAmount)
.Take(10)
.ToList();
8. session.CreateCriteria<T>()
Что делает: Создает объект Criteria, который позволяет программно строить запросы. Полезен для динамических запросов.
Параметры: Нет (возвращает ICriteria).
Пример:
var criteria = session.CreateCriteria<Employee>()
.Add(Restrictions.Like("Name", "Smith%"))
.Add(Restrictions.Gt("Salary", 50000))
.AddOrder(Order.Asc("Name"));
IList<Employee> employees = criteria.List<Employee>();
9. session.BeginTransaction()
Что делает: Начинает новую транзакцию базы данных. Все изменения, сделанные после вызова этого метода, будут либо зафиксированы (Commit), либо отменены (Rollback).
Параметры: Нет (возвращает ITransaction).
Пример:
using (var transaction = session.BeginTransaction())
{
try
{
session.Save(new Invoice { Number = "INV-001" });
session.Save(new Payment { Amount = 100 });
transaction Также в библиотеке
Event Bus и паттерн Observer в JavaScript (Node.js): реализация и примеры
Планировщик задач Java: Quartz Scheduler — полное руководство
GuzzleHTTP/Promises для PHP: полное руководство с примерами
GraphQL в C# (.NET): библиотеки Hot Chocolate и GraphQL.NET для создания API