• “Hello World” с использование NHibernate
  • Разбираемся с архитектурой
  • Написание и мэппинг (mapping) простой сущности (entity)
  • Конфигурирование NHibernate
  • Простые CRUD-операции

Для того, чтобы понять object/relational mapping, рассмотрим его на примере простого приложения, работающего с объектами БД.

1. Установка NHibernate

Скачать NHibernate можно с официального сайта в виде архива. В данном цикле статей, я буду использовать NHibernate 2.1.2.GA.

2. Проект в Visual Studio

Для начала создаем пустой бланк нового решения. Я назвал его “Hello NHibernate!” (неожиданно :) ).

Hello NHibernate

Добавим в решение проект консольное приложение с именем “HelloNHibernate”.

Hello NHibernate

В папке решения “Hello NHibernate!”, я рекомендую создать подпапку Libs для хранения библиотек, которые понадобятся в процессе работы. В этот каталог мы извлекаем и содержимое скачанного ранее архива NHibernate:

  • Required Bins
  • Required For LazyLoading
  • Tests

Теперь добавляем в References (Ссылки) NHibernate.dll. В классе Program.cs так же прописываем using NHibernate, using System.Reflection и NHibernate.Cfg.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
using System;
using NHibernate;
using System.Reflection;
using NHibernate.Cfg;

namespace HelloNHibernate
{
    class Program
    {
        static void Main(string[] args)
        {
        }
    }
}

3. Класс “Служащий” (Employee).

Цель приложения – работа с записями таблицы Employee, хранящей информацию о служащих фирмы. Следовательно, добавляем в проект новый класс и называем его соответственно таблице — “Employee”. Код класса:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
using System;

namespace HelloNHibernate
{
    class Employee
    {
        public int id;
        public string name;
        public Employee manager;
        public string SayHello()
        {
            return string.Format("'Hello World!', said {0}.", name);
        }
    }
}

Как видите, класс имеет три поля: идентификатор, имя служащего, ссылку на объект класса и метод, возвращающий строку типа “’Hello World!’, said Andrey”. Для читателей, имеющих опыт работы с NHibernate, оговорюсь заранее, свойства затрону позже, пока ограничимся только паблик переменными.

Теперь используем описанный выше класс в приложении:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
using System;
using NHibernate;
using System.Reflection;
using NHibernate.Cfg;
namespace HelloNHibernate
{
    class Program
    {
        static void Main(string[] args)
        {
            Employee anry = new Employee();
            anry.name = "Andrey Krisanov";
            Console.WriteLine(anry.SayHello());
            Console.ReadKey();
        }
    }
}
Hello NHibernate

4. База данных

Конечно же, БД! Именно она нужна нам для хранения и выполнения пр. операций с данными в нашем проекте. В качестве СУБД я использую Microsoft SQL Server 2008 Express Edition, однако NHibernate легко уживается и с другими. Приступим…

Запускаем Microsoft SQL Server Management Studio, соединяемся с сервером БД, открываем новое окно запросов и пишем код для создания новой базы:

1
2
CREATE DATABASE HelloNHibernate
GO

Выполняем его. Результат:
Hello NHibernate
Далее переключаемся на нашу БД и создаем таблицу Employee:

1
2
3
4
5
6
7
USE HelloNHibernate
GO
CREATE TABLE Employee (
    id int identity PRIMARY KEY,
    name varchar(50),
    manager int )
GO

Результат:
Hello NHibernate
В итоге, все необходимые каркасы для использования NHibernate мы создали.

5. Создание служащего и сохранение его в БД

Этот функционал описывают две функции: CreateEmployeeAndSaveToDatabase и OpenSession:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
static void CreateEmployeeAndSaveToDatabase()
{
    Employee andrey = new Employee();
    andrey.name = "Andrey Krisanov";

    using (ISession session = OpenSession())
    {
        using (ITransaction transaction = session.BeginTransaction())
        {
            session.Save(andrey);
            transaction.Commit();
        }
        Console.WriteLine("Saved Andrey to the databases");
    }
}

static ISessionFactory factory;

static ISession OpenSession()
{
    if (factory == null)
    {
        Configuration conf = new Configuration();
        conf.AddAssembly(Assembly.GetCallingAssembly());
        factory = conf.BuildSessionFactory();
    }
    return factory.OpenSession();
}

Функции реализуют интерфейсы NHibernate ISession, ITransaction. Так же имеется статическое поле factory, реализующее интерфейс ISessionFactory. Подробнее об этих интерфейсах расскажу позже. Пока достаточно знать, что SessionFactory создается один раз и используется на протяжении всей работы приложения, Session же можно создавать сколько угодно.

Если мы вызовем метод CreateEmployeeAndSaveToDatabase, NHibernate фактически выполнить SQL-запрос:

1
2
INSERT INTO Employees (name, manager)
VALUES ('Andrey Krisanov', NULL)

OpenSession () вызывается всегда, когда необходимо просматривать, сохранять, искать и выполнять прочие действия с объектами в базе данных. В ваших будущих production-проектах я не рекомендую использовать OpenSession, более экономичные способы опишу в следующих постах.

Итак, мы создали класс Служащий (Employee), добавили таблицу для его хранения в БД и написали некоторый код, для сохранения объекта с использованием NHibernate.

6. Загрузка служащих из базы данных

Получим список всех служащих, содержащихся в таблице и заставим сказать их “Привет мир!”.

Добавим код в Program.cs и не забудем подключить System.Collections.Generic для IList:

1
2
3
4
5
6
7
8
9
10
11
static void LoadEmployeesFromDatabase()
{
    using (ISession session = OpenSession())
    {
        IQuery query = session.CreateQuery("from Employee as emp order by emp.name asc");
        IList foundEmployees = query.List();
        Console.WriteLine("\n{0} employees found:", foundEmployees.Count);
        foreach (Employee employee in foundEmployees)
            Console.WriteLine(employee.SayHello());
    }
}

Литерал «from Employee as emp order by emp.name asc» – запрос NHibernate (HQL). Во время вызова query.List () он будет транслирован в SQL:

1
2
3
SELECT e.id, e.name, e.manager
FROM Employee e
ORDER BY e.name ASC

Есть и еще более простой способ получить все записи таблицы, но об этом снова позже :)

Подведем некоторые итоги. После прочтения вышеописанного материала у вас должно было сформироваться ощущение, что чего-то не хватает в структуре нашего проекта, какого-то связующего звена между Employee и NHibernate. Откуда берутся знания о классе для сохранения и извлечения соответствующих объектов? Отвечаю.

7. Создание файла мэппинга (mapping file)

Прежде, чем в полною меру использовать NHibernate, необходимо сообщить информацию о классе Employee. Сделать это можно несколькими способами. Для начала рассмотрим XML-файлы – базовый способ мэппинга.

Для IntelliSense закидываем в папку Microsoft Visual Studio\Xml\Schemas файлы схем NHibernate:

  • nhibernate-configuration.xsd
  • nhibernate-mapping.xsd

Затем добавляем в проект xml-файл, который называем Employee.hbm.xml, имя файла совпадает с именем класса + .hbm.xml.

Hello NHibernate

В свойствах Employee.hbm.xml в Build Action (Действие при построении) выбираем “Embedded Resource” (Внедренный ресурс). Не пропустите данный шаг, иначе NHibernate не сможет найти нужную мэппинг информацию!

Теперь открываем Employee.hbm.xml и прописываем в нем:

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" auto-import="true">
    <class name="HelloNHibernate.Employee, HelloNHibernate" lazy="false">
        <id name="id" access="field">
            <generator class="native" />
        </id>
        <property name="name" access="field" column="name"/>
        <many-to-one access="field" name="manager" column="manager" cascade="all"/>
    </class>
</hibernate-mapping>

При наборе кода, видим подсказки IntelliSense (не зря же мы копировали файлы схем в папку Студии).

Hello NHibernate

Итак, в файле мэппинга мы сообщили информацию о классе Employee, в какие столбцы таблицы будут записываться данные его полей. Как видите, все довольно просто.

Осталось сконфигурировать NHibernate под наш проект.

8. Конфигурирование приложения

Если вы ранее работали с БД в .NET (использовали DataSet, DataReader и пр.), то знаете о необходимости прописывать ConnectionString в файле web.config или app.config. Для NHibernate ситуация аналогичная:

  1. Добавляем в проект конфигурационный файл.
  2. Определяем настройки:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?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="proxyfactory.factory_class">NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle</property>
            <property name="connection.provider">
                NHibernate.Connection.DriverConnectionProvider
            </property>
            <property name="connection.driver_class">
                NHibernate.Driver.SqlClientDriver
            </property>
            <property name="connection.connection_string">
                Data Source=ANRY-NOTEBOOK\SQLEXPRESS;Initial Catalog=HelloNHibernate;Persist Security Info=True;User ID=test;Password=test
            </property>
            <property name="dialect">
                NHibernate.Dialect.MsSql2005Dialect
            </property>
            <property name="show_sql">
                false
            </property>
        </session-factory>
    </hibernate-configuration>
</configuration>

NHibernate очень гибок, и вариантов конфигурации ORM под ваши нужды существуем достаточно много. Обратите внимание, свойство “connection.connection_string” содержит строку подключения к БД, используемой в проекте. При необходимости, замените ее на актуальную для вашего сервера.

9. Обновление записи служащего

Перед тем, как запустить и проверить работу нашего приложения, добавим в него еще одну функцию, которая обновит запись обо мне и добавить новую:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
static void UpdateAndreyAndAssignNikolayAsManager()
{
    using (ISession session = OpenSession())
    {
        using (ITransaction transaction = session.BeginTransaction())
        {
            IQuery q = session.CreateQuery("from Employee where name = 'Andrey Krisanov'");
            Employee andrey = q.List()[0];
            andrey.name = "Andrey Anatolevich Krisanov";
            Employee nikolay = new Employee();
            nikolay.name = "Nikolay Evseev";
            andrey.manager = nikolay;
            transaction.Commit();
            Console.WriteLine("Updated Andrey and added Nikolay Evseev!");
        }
    }
}

При выполнении метода NHibernate переведет описанный выше код в SQL:

1
2
3
4
5
6
7
8
9
10
SELECT e.id, e.name, e.manager
FROM Employee e
WHERE e.id = 1
INSERT INTO Employees (name, manager)
VALUES ('Nikolay Evseev', NULL)
declare @newId int
SELECT @newId = scope_identity()
UPDATE Employees
SET name = 'Andrey Anatolevich Krisanov', manager = @newId
WHERE id = 1

ORM рулит! :)

10. Запуск программы

Ключевой момент данного топика – проверка работы написанного кода. Модифицируем метод Main () класса Program.cs:

1
2
3
4
5
6
7
8
static void Main(string[] args)
{
    CreateEmployeeAndSaveToDatabase();
    UpdateAndreyAndAssignNikolayAsManager();
    LoadEmployeesFromDatabase();
    Console.WriteLine("Press any key to exit...");
    Console.ReadKey();
}

Результат:

Hello NHibernate

Записи в таблице:

Hello NHibernate

P.S. Подключите к проекту следующие сборки:

Hello NHibernate

Исходник проекта