NHibernate — Обзор
В этой главе мы обсудим, что такое NHibernate, на каких платформах он может быть реализован, каковы его преимущества и другие аспекты, связанные с ним.
Что такое NHibernate?
NHibernate — это зрелый объектно-реляционный картограф с открытым исходным кодом для платформы .NET. Он активно развивается, полнофункциональный и используется в тысячах успешных проектов. Он построен на основе ADO.NET, и текущей версией является NHibernate 4.0.4.
-
NHibernate — это объектно-реляционный картограф с открытым исходным кодом .NET, распространяемый по лицензии GNU Lesser General Public License .
-
Он основан на Hibernate, популярном объектно-реляционном маппере Java, и имеет очень зрелую и активную базу кода.
-
Он обеспечивает основу для сопоставления объектно-ориентированной модели предметной области с традиционной реляционной базой данных.
-
NHibernate был запущен Томом Барреттом, и этот проект существует с февраля 2003 года, что стало их первым обязательством.
-
Это большой проект и предоставляет много функциональности.
-
Доступен пакет NuGet , который позволяет очень легко добавить в проект.
NHibernate — это объектно-реляционный картограф с открытым исходным кодом .NET, распространяемый по лицензии GNU Lesser General Public License .
Он основан на Hibernate, популярном объектно-реляционном маппере Java, и имеет очень зрелую и активную базу кода.
Он обеспечивает основу для сопоставления объектно-ориентированной модели предметной области с традиционной реляционной базой данных.
NHibernate был запущен Томом Барреттом, и этот проект существует с февраля 2003 года, что стало их первым обязательством.
Это большой проект и предоставляет много функциональности.
Доступен пакет NuGet , который позволяет очень легко добавить в проект.
Почему именно NHibernate?
Теперь вопрос: зачем нам объектно-реляционные картографы? Это потому, что существует разрыв между объектным миром и миром отношений.
-
В объектном мире все основано на объектах ; мы называли объекты теми вещами, которые имеют наши данные.
-
Весь реляционный мир основан на множествах, и мы имеем дело с таблицами и строками, которые отличаются от мира объектов.
-
В объектном мире у нас есть однонаправленные ассоциации . Если у клиента есть указатель на заказ, это не обязательно означает, что у заказа есть указатель на клиента, это может быть или не быть.
-
В реляционном мире все ассоциации являются двунаправленными, и это может быть сделано с помощью внешнего ключа.
-
Все ассоциации по своей природе являются двунаправленными, поэтому, когда мы имеем дело с объектно-реляционным отображением, нам также необходимо иметь дело с этим разъединением.
-
В объектном мире мы работаем с однонаправленными указателями, тогда как в реляционном мире у нас есть внешние ключи, которые по своей природе являются двунаправленными.
-
В объектном мире есть понятие наследования, где транспортное средство может иметь несколько различных подклассов, поэтому автомобиль — это тип транспортного средства, лодка — это тип транспортного средства, а спортивная машина — это тип автомобиля, эти типы наследственные отношения.
-
В реляционном мире нет этого понятия наследования.
В объектном мире все основано на объектах ; мы называли объекты теми вещами, которые имеют наши данные.
Весь реляционный мир основан на множествах, и мы имеем дело с таблицами и строками, которые отличаются от мира объектов.
В объектном мире у нас есть однонаправленные ассоциации . Если у клиента есть указатель на заказ, это не обязательно означает, что у заказа есть указатель на клиента, это может быть или не быть.
В реляционном мире все ассоциации являются двунаправленными, и это может быть сделано с помощью внешнего ключа.
Все ассоциации по своей природе являются двунаправленными, поэтому, когда мы имеем дело с объектно-реляционным отображением, нам также необходимо иметь дело с этим разъединением.
В объектном мире мы работаем с однонаправленными указателями, тогда как в реляционном мире у нас есть внешние ключи, которые по своей природе являются двунаправленными.
В объектном мире есть понятие наследования, где транспортное средство может иметь несколько различных подклассов, поэтому автомобиль — это тип транспортного средства, лодка — это тип транспортного средства, а спортивная машина — это тип автомобиля, эти типы наследственные отношения.
В реляционном мире нет этого понятия наследования.
картографирование
Итак, как мы сопоставляем все эти непересекающиеся отношения? Эта концепция отображения происходит от объектно-реляционного картографа. Есть в основном три вещи, чтобы понять, как показано на следующей диаграмме.
-
В вашем приложении вам понадобятся определения классов, которые обычно представляют собой код C # и его код .NET, представляющий наши классы, такие как класс Employee, класс Customer, класс Order и т. Д.
-
Внизу вы можете увидеть схему базы данных, которая является нашим языком определения данных, в реляционной базе данных, которая определяет, как выглядит таблица клиента, как выглядит таблица сотрудника.
-
Между ними у нас есть метаданные отображения, которые говорят объектно-реляционному преобразователю, как переводить из мира объектов в C # в мир баз данных в терминах строк и столбцов и отношений внешнего ключа.
-
Эти метаданные сопоставления могут быть представлены различными способами, и мы рассмотрим некоторые из этих различных способов, наиболее типичных для приложения NHibernate.
-
Он представлен файлами HBM (Hibernate Mapping) , которые являются файлами XML.
В вашем приложении вам понадобятся определения классов, которые обычно представляют собой код C # и его код .NET, представляющий наши классы, такие как класс Employee, класс Customer, класс Order и т. Д.
Внизу вы можете увидеть схему базы данных, которая является нашим языком определения данных, в реляционной базе данных, которая определяет, как выглядит таблица клиента, как выглядит таблица сотрудника.
Между ними у нас есть метаданные отображения, которые говорят объектно-реляционному преобразователю, как переводить из мира объектов в C # в мир баз данных в терминах строк и столбцов и отношений внешнего ключа.
Эти метаданные сопоставления могут быть представлены различными способами, и мы рассмотрим некоторые из этих различных способов, наиболее типичных для приложения NHibernate.
Он представлен файлами HBM (Hibernate Mapping) , которые являются файлами XML.
База данных поддерживается
NHibernate поддерживает широкий спектр различных баз данных. Любая существующая реляционная база данных может быть доступна в NHibernate.
-
SQL-сервер является основной поддерживаемой базой данных, это то, что большинство разработчиков используют во время разработки, возможно, это самая распространенная.
-
Это также очень хорошо работает с Oracle .
-
Он также поддерживает DB2, Firebird, MySQL, PostgreSQL, SQL Lite
-
Он также имеет драйверы ODBC и OLEDB .
SQL-сервер является основной поддерживаемой базой данных, это то, что большинство разработчиков используют во время разработки, возможно, это самая распространенная.
Это также очень хорошо работает с Oracle .
Он также поддерживает DB2, Firebird, MySQL, PostgreSQL, SQL Lite
Он также имеет драйверы ODBC и OLEDB .
NHibernate — Архитектура
В настоящее время многие системы спроектированы с многоуровневой архитектурой, NHibernate также имеет ее и прекрасно работает с этим дизайном.
Многоуровневая архитектура
Многоуровневая архитектура делит систему на несколько групп, где каждая группа содержит код, адресованный конкретной проблемной области, и эти группы называются слоями. Большинство приложений уровня предприятия используют архитектуру приложений высокого уровня , состоящую из трех уровней:
- Уровень презентации
- Бизнес уровень
- Постоянный слой
Например, уровень пользовательского интерфейса, который также известен как уровень представления, может содержать весь код приложения для создания веб-страниц и обработки пользовательского ввода.
Одним из основных преимуществ многоуровневого подхода является то, что вы часто можете вносить изменения в один уровень без какого-либо значительного нарушения других уровней, что делает системы менее уязвимыми и более удобными в обслуживании .
Уровень представления
-
Это самый верхний слой, который содержит код, отвечающий за отрисовку пользовательского интерфейса, страниц, диалогов или экранов, а также за сбор пользовательского ввода и управление навигацией.
Это самый верхний слой, который содержит код, отвечающий за отрисовку пользовательского интерфейса, страниц, диалогов или экранов, а также за сбор пользовательского ввода и управление навигацией.
Бизнес уровень
-
Бизнес-уровень отвечает за реализацию любых бизнес-правил или системных требований, которые пользователи будут понимать как часть проблемной области.
-
Он также повторно использует модель, определенную постоянным слоем.
Бизнес-уровень отвечает за реализацию любых бизнес-правил или системных требований, которые пользователи будут понимать как часть проблемной области.
Он также повторно использует модель, определенную постоянным слоем.
Постоянный слой
-
Уровень персистентности состоит из классов и компонентов, которые отвечают за сохранение и извлечение данных приложения.
-
Этот слой также определяет соответствие между классом модели и базой данных. NHibernate используется в основном в этом слое.
Уровень персистентности состоит из классов и компонентов, которые отвечают за сохранение и извлечение данных приложения.
Этот слой также определяет соответствие между классом модели и базой данных. NHibernate используется в основном в этом слое.
База данных
- База данных существует вне приложения .NET.
- Это фактическое, постоянное представление состояния системы.
- Если используется база данных SQL, база данных включает в себя реляционную схему и, возможно, хранимые процедуры.
Вспомогательные / служебные классы
-
Каждое приложение имеет набор вспомогательных или служебных классов, которые поддерживают другие уровни: например, виджеты пользовательского интерфейса, классы обмена сообщениями, классы исключений и утилиты ведения журнала.
-
Эти элементы не считаются слоями, потому что они не подчиняются правилам межслойной зависимости в многоуровневой архитектуре.
Каждое приложение имеет набор вспомогательных или служебных классов, которые поддерживают другие уровни: например, виджеты пользовательского интерфейса, классы обмена сообщениями, классы исключений и утилиты ведения журнала.
Эти элементы не считаются слоями, потому что они не подчиняются правилам межслойной зависимости в многоуровневой архитектуре.
Архитектура NHibernate
-
Это высокоуровневое представление приложения NHibernate, и вы также можете увидеть простую архитектуру NHibernate.
Это высокоуровневое представление приложения NHibernate, и вы также можете увидеть простую архитектуру NHibernate.
-
Код приложения использует API-интерфейсы NHibernate ISession и IQuery для постоянных операций и должен управлять транзакциями базы данных только в идеале с использованием API-интерфейса NHibernate ITransaction .
Код приложения использует API-интерфейсы NHibernate ISession и IQuery для постоянных операций и должен управлять транзакциями базы данных только в идеале с использованием API-интерфейса NHibernate ITransaction .
NHibernate — ORM
Прежде чем мы действительно сможем начать использовать NHibernate, нам необходимо понять основы, на которых он построен. NHibernate — это технология персистентности, основанная на идее реляционного сопоставления объектов или ORM.
Что такое ORM?
Объектно-реляционное отображение (ORM) — это метод программирования для преобразования данных между несовместимыми системами типов в объектно-ориентированных языках программирования. Другими словами, это концепция сопоставления бизнес-объектов приложения с таблицами реляционной базы данных, так что данные могут быть легко доступны и полностью обновлены через объектную модель приложения.
-
Как вы уже знаете, реляционные базы данных обеспечивают хорошие средства хранения данных, а объектно-ориентированное программирование — хороший подход к созданию сложных приложений.
-
NHibernate и ORM в целом наиболее актуальны для приложений с нетривиальной бизнес-логикой, моделью предметной области и своего рода базой данных.
-
С ORM очень легко создать слой перевода, который может легко преобразовывать объекты в реляционные данные и обратно.
-
Сокращение ORM также может означать моделирование роли объекта, и этот термин был изобретен до того, как сопоставление объекта / реляции стало актуальным.
-
Описывается метод анализа информации, используемый при моделировании базы данных.
Как вы уже знаете, реляционные базы данных обеспечивают хорошие средства хранения данных, а объектно-ориентированное программирование — хороший подход к созданию сложных приложений.
NHibernate и ORM в целом наиболее актуальны для приложений с нетривиальной бизнес-логикой, моделью предметной области и своего рода базой данных.
С ORM очень легко создать слой перевода, который может легко преобразовывать объекты в реляционные данные и обратно.
Сокращение ORM также может означать моделирование роли объекта, и этот термин был изобретен до того, как сопоставление объекта / реляции стало актуальным.
Описывается метод анализа информации, используемый при моделировании базы данных.
Почему ОРМ?
ORM — это структура, которая позволяет отображать мир объектов, найденных в объектно-ориентированных языках, в строки в реляционных таблицах, найденных в реляционных базах данных.
Чтобы понять эту концепцию, давайте взглянем на следующую диаграмму.
-
На приведенной выше диаграмме вы можете видеть, что у нас есть таблица с именем Employee с правой стороны, которая содержит столбцы с каждым фрагментом данных, связанным с отдельным сотрудником.
-
У нас есть столбец для идентификатора, который уникально идентифицирует каждого сотрудника.
-
Столбец для имени сотрудника, еще один столбец для даты их вступления и, наконец, столбец, в котором указан возраст сотрудника.
-
Если мы хотим написать код для хранения нового сотрудника в таблицах, это не так просто.
-
На приведенной выше диаграмме вы также можете видеть, что у нас есть объект сотрудника, в котором есть поля для идентификатора, имени, даты присоединения и возраста.
-
Без ORM мы должны преобразовать этот объект в несколько различных операторов SQL, которые будут вставлять данные о сотрудниках в таблицу сотрудников.
-
Таким образом, написание кода для создания SQL-кода для выполнения описанного выше сценария не так сложно, но это немного утомительно и легко ошибиться.
-
Используя ORM, такой как NHibernate, мы можем объявить, как определенные классы должны отображаться в реляционные таблицы, и позволить ORM или NHibernate справиться с сложной задачей создания SQL для вставки, обновления, удаления данных запроса в нашей таблице сотрудников.
-
Это позволяет нам сосредоточить наш код на использовании объектов и автоматически переводить эти объекты в реляционные таблицы.
-
Так что на самом деле ORM избавляет нас от необходимости вручную отображать объекты в таблицы.
На приведенной выше диаграмме вы можете видеть, что у нас есть таблица с именем Employee с правой стороны, которая содержит столбцы с каждым фрагментом данных, связанным с отдельным сотрудником.
У нас есть столбец для идентификатора, который уникально идентифицирует каждого сотрудника.
Столбец для имени сотрудника, еще один столбец для даты их вступления и, наконец, столбец, в котором указан возраст сотрудника.
Если мы хотим написать код для хранения нового сотрудника в таблицах, это не так просто.
На приведенной выше диаграмме вы также можете видеть, что у нас есть объект сотрудника, в котором есть поля для идентификатора, имени, даты присоединения и возраста.
Без ORM мы должны преобразовать этот объект в несколько различных операторов SQL, которые будут вставлять данные о сотрудниках в таблицу сотрудников.
Таким образом, написание кода для создания SQL-кода для выполнения описанного выше сценария не так сложно, но это немного утомительно и легко ошибиться.
Используя ORM, такой как NHibernate, мы можем объявить, как определенные классы должны отображаться в реляционные таблицы, и позволить ORM или NHibernate справиться с сложной задачей создания SQL для вставки, обновления, удаления данных запроса в нашей таблице сотрудников.
Это позволяет нам сосредоточить наш код на использовании объектов и автоматически переводить эти объекты в реляционные таблицы.
Так что на самом деле ORM избавляет нас от необходимости вручную отображать объекты в таблицы.
NHibernate — Настройка среды
Чтобы начать работать с NHibernate, нам понадобится Visual Studio и пакет NHibernate.
Visual Studio Установка
Microsoft предоставляет бесплатную версию Visual Studio, которая также содержит SQL Server, и ее можно загрузить с веб-сайта https://www.visualstudio.com. Ниже приведены шаги для установки.
Шаг 1. После завершения загрузки запустите программу установки, и появится следующее диалоговое окно.
Шаг 2 — Нажмите на кнопку Установить, и он начнет процесс установки.
Шаг 3 — После успешного завершения процесса установки вы увидите следующее диалоговое окно.
Шаг 4 — Закройте это диалоговое окно и перезагрузите компьютер, если это необходимо.
Шаг 5 — Теперь откройте Visual Studio из меню «Пуск», которое откроет следующий диалог. Впервые на подготовку потребуется некоторое время.
Шаг 6 — Как только все это будет сделано, вы увидите главное окно Visual Studio.
Установка пакета NHibernate
NHibernate — это зрелый объектно-реляционный картограф с открытым исходным кодом для платформы .NET. Он активно развивается, полнофункциональный и используется в тысячах успешных проектов. Вы можете установить пакет NHibernate следующими способами.
Прямое скачивание
-
Загрузите zip из файла с https://sourceforge.net/, который содержит все необходимые двоичные файлы.
-
Распакуйте этот zip-файл и включите все эти двоичные файлы в ваш проект.
Загрузите zip из файла с https://sourceforge.net/, который содержит все необходимые двоичные файлы.
Распакуйте этот zip-файл и включите все эти двоичные файлы в ваш проект.
Установить с помощью NuGet
-
Другим способом установки NHibernate является использование NuGet для установки пакета NHibernate, что является самым простым способом включения NHibernate в проект.
-
Он собирается загрузить все зависимости NHibernate и создать ссылки на все необходимые сборки.
-
Чтобы установить NHibernate, выполните следующую команду в консоли диспетчера пакетов.
Другим способом установки NHibernate является использование NuGet для установки пакета NHibernate, что является самым простым способом включения NHibernate в проект.
Он собирается загрузить все зависимости NHibernate и создать ссылки на все необходимые сборки.
Чтобы установить NHibernate, выполните следующую команду в консоли диспетчера пакетов.
install-package NHibernate
Теперь вы готовы запустить приложение.
NHibernate — Начало работы
В этой главе мы рассмотрим, как начать простой пример с использованием NHibernate. Мы будем создавать простое консольное приложение . Для создания консольного приложения мы будем использовать Visual Studio 2015, которая содержит все функции, необходимые для создания, протестировать приложение с помощью пакета NHibernate.
Ниже приведены шаги по созданию проекта с использованием шаблонов проектов, доступных в Visual Studio.
Шаг 1 — Откройте Visual Studio и выберите Файл → Создать → Пункт меню Проект.
Шаг 2 — Откроется новое диалоговое окно проекта.
Шаг 3 — На левой панели выберите Шаблоны → Visual C # → Windows.
Шаг 4 — На средней панели выберите Консольное приложение.
Шаг 5 — Введите имя проекта «NHibernateDemoApp» в поле «Имя» и нажмите «ОК» для продолжения.
Шаг 6. После создания проекта в Visual Studio вы увидите несколько файлов, отображаемых в окне обозревателя решений.
Поскольку вы знаете, что мы создали простой проект консольного приложения, теперь нам нужно включить пакет NHibernate в наш консольный проект.
Перейдите в меню Сервис и выберите Диспетчер пакетов NuGet → Консоль диспетчера пакетов, откроется окно Консоль диспетчера пакетов.
Укажите команду, показанную в приведенном выше окне консоли диспетчера пакетов, и нажмите клавишу ВВОД. Она загрузит все зависимости NHibernate и создаст ссылки на все необходимые сборки. После завершения установки вы увидите сообщение, как показано на следующем рисунке.
Теперь, когда мы добавили NHibernate, мы можем начать реализацию. Итак, мы собираемся начать с отображения очень простой таблицы с именем Student , у которой просто есть целочисленный первичный ключ с именем ID и столбцы FirstName и LastName.
Нам нужен класс для представления этого ученика, поэтому давайте создадим новый класс с именем Student, щелкнув правой кнопкой мыши по проекту в обозревателе решений и выбрав Add → Class, который откроет диалоговое окно Add New Item.
Введите Student.cs в поле имени, нажмите кнопку Добавить. В этом классе Student у нас должен быть наш целочисленный первичный ключ с именем ID, и нам нужно создать эту строку, поля FirstName и LastName, как показано в следующей полной реализации класса Student.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace NHibernateDemoApp { class Student { public virtual int ID { get; set; } public virtual string LastName { get; set; } public virtual string FirstMidName { get; set; } } }
При работе с моделями в приложении NHibernate проще всего сделать все поля виртуальными. Так что это наша простая модель NHibernate, которую мы будем использовать и сопоставим ее с внутренней базой данных.
Теперь давайте перейдем к методу Main в классе Program и создадим новый объект конфигурации NHibernate.
Первое, что нам нужно предоставить — это строка подключения . Это строка подключения для конкретной базы данных, и самый простой способ найти строку подключения — это щелкнуть правой кнопкой мыши базу данных в проводнике объектов SQL Server и выбрать «Свойства».
Откроется окно свойств, прокрутите вниз, и вы увидите поле Строка подключения в окне свойств.
Скопируйте строку подключения и укажите в своем коде. Ниже приведена реализация метода Main, в котором нам нужна конфигурация для NHibernate.
using NHibernate.Cfg; using NHibernate.Dialect; using NHibernate.Driver; using System; using System.Linq; using System.Reflection; namespace NHibernateDemoApp { class Program { static void Main(string[] args) { var cfg = new Configuration(); String Data Source = asia13797\\sqlexpress; String Initial Catalog = NHibernateDemoDB; String Integrated Security = True; String Connect Timeout = 15; String Encrypt = False; String TrustServerCertificate = False; String ApplicationIntent = ReadWrite; String MultiSubnetFailover = False; cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source + Initial Catalog + Integrated Security + Connect Timeout + Encrypt + TrustServerCertificate + ApplicationIntent + MultiSubnetFailover"; x.Driver<SqlClientDriver>(); x.Dialect<MsSql2008Dialect>(); }); cfg.AddAssembly(Assembly.GetExecutingAssembly()); var sefact = cfg.BuildSessionFactory(); using (var session = sefact.OpenSession()) { using (var tx = session.BeginTransaction()) { //perform database logic tx.Commit(); } Console.ReadLine(); } } } }
После строки подключения нам нужно предоставить драйвер, который является SQLClientDriver, а затем мы также должны указать ему диалект, какую версию SQL Server мы будем использовать MS SQL 2008.
Теперь NHibernate знает, как подключиться к базе данных. Другая вещь, которую нам нужно сделать, это предоставить список моделей, которые мы будем отображать.
Мы можем сделать это, добавив сборку, указав Assembly.GetExecutingAssembly, и именно здесь программа найдет файлы сопоставления. Файлы сопоставления сообщают NHibernate, как перейти от классов C # к таблицам базы данных.
SessionFactory компилирует все метаданные, необходимые для инициализации NHibernate. SessionFactory может использоваться для создания сеансов, которые примерно аналогичны соединениям с базой данных. Поэтому подходящий способ — использовать его в блоке using. Я могу сказать, что var session равно sessionFactory.OpenSession, и я хочу сделать это внутри своей транзакции.
Как только сеанс открыт, мы можем сказать сеансу начать новую транзакцию, и мы можем выполнить некоторую логику здесь. Так что выполните некоторую логику базы данных и, наконец, совершите эту транзакцию.
NHibernate — базовый ORM
В этой главе мы рассмотрим некоторые основные отображения, и вы знаете, что из последней главы мы имеем таблицу базы данных, а также определение класса C #. Теперь нам нужно сопоставление, которое объясняет, как переводить с C # в базу данных и обратно.
Итак, давайте продолжим и добавим новый XML-файл, щелкнув правой кнопкой мыши по проекту в обозревателе решений и выбрав Добавить → Новый элемент …
Введите Student.hbm.xml в поле имени. Нам нужно указать сборку по умолчанию, которая будет NHibernateDemoApp, а также указать пространство имен по умолчанию. Это просто сокращает многие другие определения типов, которые мы собираемся сделать в этом файле.
Ниже приведена реализация в файле XML:
<?xml version = "1.0" encoding = "utf-8" ?> <hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemoApp" namespace = "NHibernateDemoApp"> <class name = "Student"> <id name = "ID"> <generator class = "native"/> </id> <property name = "LastName"/> <property name = "FirstMidName"/> </class> </hibernate-mapping>
Следующее, что нам нужно определить класс; этот класс станет нашим классом для студентов . Затем нам нужно сообщить NHibernate имя идентификатора, то есть ID, и я также должен сказать NHibernate, как генерировать идентификаторы, так что наш генератор будет иметь тип native.
Генератор собственных типов означает, что в базе данных, такой как SQL Server, он будет использовать столбец идентификаторов, тип идентификатора.
Следующее, что мы должны сделать, это дать имена свойств. Итак, добавьте еще два свойства для FirstName и LastName.
Теперь мы читаем эти файлы сопоставления из сборки. Таким образом, предпочтительный способ сделать это — вставить эти файлы HBM в вашу сборку. Мы можем сделать это, просто установив свойство.
Теперь щелкните правой кнопкой мыши по проекту в обозревателе решений и выберите «Свойства», вы увидите поле « Построить действие», в котором по умолчанию выбран контент.
Выберите встроенный ресурс из выпадающего списка.
Таким образом, это фактически внедряет этот XML-файл в сборку NHibernateDemoApp .
NHibernate — Основные операции CRUD
В этой главе мы рассмотрим основные операции CRUD . Теперь, когда наша система готова к запуску, так как мы успешно внедрили наш класс Student домена, мы также определили файлы сопоставления и настроили NHibernate. Теперь мы можем использовать некоторые запросы для выполнения операций CRUD.
Создать данные
Как видите, у нас нет данных в нашей таблице Student в базе данных NHibernateDemoDB .
Поэтому для добавления некоторых данных нам нужно выполнить операцию добавления / создания, как показано ниже.
using (var session = sefact.OpenSession()) { using (var tx = session.BeginTransaction()) { var student1 = new Student { ID = 1, FirstMidName = "Allan", LastName = "Bommer" }; var student2 = new Student { ID = 2, FirstMidName = "Jerry", LastName = "Lewis" }; session.Save(student1); session.Save(student2); tx.Commit(); } Console.ReadLine(); }
Как вы можете видеть, мы создали двух студентов и затем вызываем метод Save () OpenSession, а затем вызываем Commit () BeginTransaction . Вот полная реализация в файле Program.cs
using NHibernate.Cfg; using NHibernate.Dialect; using NHibernate.Driver; using System; using System.Linq; using System.Reflection; namespace NHibernateDemoApp { class Program { static void Main(string[] args) { var cfg = new Configuration(); String Data Source = asia13797\\sqlexpress; String Initial Catalog = NHibernateDemoDB; String Integrated Security = True; String Connect Timeout = 15; String Encrypt = False; String TrustServerCertificate = False; String ApplicationIntent = ReadWrite; String MultiSubnetFailover = False; cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source + Initial Catalog + Integrated Security + Connect Timeout + Encrypt + TrustServerCertificate + ApplicationIntent + MultiSubnetFailover"; x.Driver<SqlClientDriver>(); x.Dialect<MsSql2008Dialect>(); }); cfg.AddAssembly(Assembly.GetExecutingAssembly()); var sefact = cfg.BuildSessionFactory(); using (var session = sefact.OpenSession()) { using (var tx = session.BeginTransaction()) { var student1 = new Student { ID = 1, FirstMidName = "Allan", LastName = "Bommer" }; var student2 = new Student { ID = 2, FirstMidName = "Jerry", LastName = "Lewis" }; session.Save(student1); session.Save(student2); tx.Commit(); } Console.ReadLine(); } } } }
Теперь давайте запустим это приложение, а затем перейдем в Обозреватель объектов SQL Server и обновим вашу базу данных. Вы увидите, что два вышеупомянутых студента теперь добавлены в таблицу Student в базе данных NHibernateDemoDB.
Чтение данных из таблицы учеников
Вы можете видеть, что теперь у нас есть две записи в нашей таблице учеников. Чтобы прочитать эти записи из таблицы, нам нужно вызвать CreateCriteria () из OpenSession, как показано в следующем коде.
using (var session = sefact.OpenSession()) { using (var tx = session.BeginTransaction()) { var students = session.CreateCriteria<Student>().List<Student>(); foreach (var student in students) { Console.WriteLine("{0} \t{1} \t{2}", student.ID,student.FirstMidName, student.LastName); } tx.Commit(); } Console.ReadLine(); }
Итак, если вам нужен список записей, тогда мы можем просто сказать список типа Student.
Теперь используйте foreach для всех учеников и скажите «ID», « FirstMidName» и « LastName» на консоли. Теперь давайте снова запустим это приложение, и вы увидите следующий вывод в окне консоли.
1 Allan Bommer 2 Jerry Lewis
Вы также можете получить любую запись, указав идентификатор в методе Get () OpenSession, используя следующий код.
using (var session = sefact.OpenSession()) { using (var tx = session.BeginTransaction()) { var students = session.CreateCriteria<Student>().List<Student>(); foreach (var student in students) { Console.WriteLine("{0} \t{1} \t{2}", student.ID, student.FirstMidName, student.LastName); } var stdnt = session.Get<Student>(1); Console.WriteLine("Retrieved by ID"); Console.WriteLine("{0} \t{1} \t{2}", stdnt.ID, stdnt.FirstMidName, stdnt.LastName); tx.Commit(); } Console.ReadLine(); }
Теперь, когда вы запустите ваше приложение, вы увидите следующий вывод.
1 Allan Bommer 2 Jerry Lewis Retrieved by ID 1 Allan Bommer
Обновить запись
Чтобы обновить запись в таблице, нам нужно сначала извлечь эту конкретную запись, а затем обновить эту запись, вызвав метод Update () OpenSession, как показано в следующем коде.
using (var session = sefact.OpenSession()) { using (var tx = session.BeginTransaction()) { var students = session.CreateCriteria<Student>().List<Student>(); foreach (var student in students) { Console.WriteLine("{0} \t{1} \t{2}", student.ID, student.FirstMidName, student.LastName); } var stdnt = session.Get<Student>(1); Console.WriteLine("Retrieved by ID"); Console.WriteLine("{0} \t{1} \t{2}", stdnt.ID, stdnt.FirstMidName, stdnt.LastName); Console.WriteLine("Update the last name of ID = {0}", stdnt.ID); stdnt.LastName = "Donald"; session.Update(stdnt); Console.WriteLine("\nFetch the complete list again\n"); foreach (var student in students) { Console.WriteLine("{0} \t{1} \t{2}", student.ID, student.FirstMidName, student.LastName); } tx.Commit(); } Console.ReadLine(); }
Теперь, когда вы запустите ваше приложение, вы увидите следующий вывод.
1 Allan Bommer 2 Jerry Lewis Retrieved by ID 1 Allan Bommer Update the last name of ID = 1 Fetch the complete list again 1 Allan Donald 2 Jerry Lewis
Как видите, LastName с идентификатором, равным 1, обновляется с Bommer до Donald.
Удалить запись
Чтобы удалить любую запись из таблицы, нам нужно сначала извлечь эту конкретную запись, а затем удалить эту запись, вызвав метод Delete () OpenSession, как показано в следующем коде.
using (var session = sefact.OpenSession()) { using (var tx = session.BeginTransaction()) { var students = session.CreateCriteria<Student>().List<Student>(); foreach (var student in students) { Console.WriteLine("{0} \t{1} \t{2}", student.ID, student.FirstMidName, student.LastName); } var stdnt = session.Get<Student>(1); Console.WriteLine("Retrieved by ID"); Console.WriteLine("{0} \t{1} \t{2}", stdnt.ID, stdnt.FirstMidName, stdnt.LastName); Console.WriteLine("Delete the record which has ID = {0}", stdnt.ID); session.Delete(stdnt); Console.WriteLine("\nFetch the complete list again\n"); foreach (var student in students) { Console.WriteLine("{0} \t{1} \t{2}", student.ID, student.FirstMidName, student.LastName); } tx.Commit(); } Console.ReadLine(); }
Теперь, когда вы запустите ваше приложение, вы увидите следующий вывод.
1 Allan Donald 2 Jerry Lewis Retrieved by ID 1 Allan Bommer Delete the record which has ID = 1 Fetch the complete list again 2 Jerry Lewis
Как видите, запись с идентификатором, равным 1, больше не доступна в базе данных. Вы также можете просмотреть базу данных в обозревателе объектов SQL Server.
NHibernate — Профилировщик
В этой главе мы поймем, как все записи из базы данных извлекаются, обновляются, создаются и удаляются, и как именно эти запросы выполняются?
Чтобы понять все это, мы можем просто добавить опцию в нашу конфигурацию, которая регистрирует SQL в консоли. Вот простое утверждение, которое будет регистрировать запрос SQL —
x.LogSqlInConsole = true;
Теперь у нас есть две записи в нашей таблице учеников в базе данных NHibernateDemoDB. Давайте извлечем все записи из базы данных, как показано в следующем коде.
using NHibernate.Cfg; using NHibernate.Dialect; using NHibernate.Driver; using System; using System.Linq; using System.Reflection; namespace NHibernateDemoApp { class Program { static void Main(string[] args) { var cfg = new Configuration(); String Data Source = asia13797\\sqlexpress; String Initial Catalog = NHibernateDemoDB; String Integrated Security = True; String Connect Timeout = 15; String Encrypt = False; String TrustServerCertificate = False; String ApplicationIntent = ReadWrite; String MultiSubnetFailover = False; cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source + Initial Catalog + Integrated Security + Connect Timeout + Encrypt + TrustServerCertificate + ApplicationIntent + MultiSubnetFailover"; x.Driver<SqlClientDriver>(); x.Dialect<MsSql2008Dialect>(); x.LogSqlInConsole = true; }); cfg.AddAssembly(Assembly.GetExecutingAssembly()); var sefact = cfg.BuildSessionFactory(); using (var session = sefact.OpenSession()) { using (var tx = session.BeginTransaction()) { Console.WriteLine("\nFetch the complete list again\n"); var students = session.CreateCriteria<Student>().List<Student>(); foreach (var student in students) { Console.WriteLine("{0} \t{1} \t{2}", student.ID, student.FirstMidName, student.LastName); } tx.Commit(); } Console.ReadLine(); } } } }
Итак, давайте продолжим и снова запустим это приложение, и вы увидите следующий результат:
NHibernate: SELECT this_.ID as ID0_0_, this_.LastName as LastName0_0_, this_.FirstMidName as FirstMid3_0_0_ FROM Student this_ Fetch the complete list again 3 Allan Bommer 4 Jerry Lewis
Как видите, предложение select , отправляемое в базу данных, фактически похоже на предложение, которое будет извлекать ID, FirstMidName и LastName. Таким образом, все это отправляется в базу данных и обрабатывается там, вместо того, чтобы много записей возвращалось на ваш сервер и обрабатывалось на стороне сервера.
NHibernate Profiler
Еще один способ взглянуть на эти результаты — использовать NHibernate Profiler. NHibernate Profiler — это коммерческий инструмент, но он очень полезен для работы с приложениями NHibernate. Вы можете легко установить NHibernate Profiler в свое приложение из NuGet.
Давайте перейдем к консоли диспетчера NuGet из меню Сервис, выбрав Диспетчер пакетов NuGet → Консоль диспетчера пакетов. Откроется окно консоли диспетчера пакетов. Введите следующую команду и нажмите ввод.
PM> install-package NHibernateProfiler
Он установит все необходимые двоичные файлы для NHibernate Profiler, после его успешной установки вы увидите следующее сообщение.
Вы также увидите, что NHibernate Profiler запускается после его установки. Для его использования потребуется лицензия, но для демонстрационных целей мы можем использовать 30-дневную пробную версию NHibernate Profiler.
Теперь NHibernate Profiler оптимизирован для работы с веб-приложениями, и вы увидите, что он добавил папку App_Start в обозревателе решений. Для простоты удалите папку App_Start, и вы также увидите, что один оператор добавляется в начале метода Main в классе Program.
App_Start.NHibernateProfilerBootstrapper.PreStart();
Также удалите этот оператор и просто добавьте простой вызов NHibernateProfiler.Initialize, как показано в следующем коде.
using HibernatingRhinos.Profiler.Appender.NHibernate; using NHibernate.Cfg; using NHibernate.Dialect; using NHibernate.Driver; using System; using System.Linq; using System.Reflection; namespace NHibernateDemoApp { class Program { static void Main(string[] args) { NHibernateProfiler.Initialize(); var cfg = new Configuration(); String Data Source = asia13797\\sqlexpress; String Initial Catalog = NHibernateDemoDB; String Integrated Security = True; String Connect Timeout = 15; String Encrypt = False; String TrustServerCertificate = False; String ApplicationIntent = ReadWrite; String MultiSubnetFailover = False; cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source + Initial Catalog + Integrated Security + Connect Timeout + Encrypt + TrustServerCertificate + ApplicationIntent + MultiSubnetFailover"; x.Driver<SqlClientDriver>(); x.Dialect<MsSql2008Dialect>(); x.LogSqlInConsole = true; }); cfg.AddAssembly(Assembly.GetExecutingAssembly()); var sefact = cfg.BuildSessionFactory(); using (var session = sefact.OpenSession()) { using (var tx = session.BeginTransaction()){ var students = session.CreateCriteria<Student>().List<Student>(); Console.WriteLine("\nFetch the complete list again\n"); foreach (var student in students) { Console.WriteLine("{0} \t{1} \t{2}", student.ID, student.FirstMidName, student.LastName); } tx.Commit(); } Console.ReadLine(); } } } }
Теперь, когда вы запустите приложение, оно отправит данные в приложение NHibernate Profiler.
Вы можете видеть здесь, у нас есть хороший дисплей, который показывает, что мы начали транзакцию, что SQL делает с базой данных в хорошем формате.
Так что это очень полезно для определения того, что именно происходит внутри вашего приложения NHibernate. Это становится невероятно полезным, когда приложение достигает определенного уровня сложности, когда вам нужно нечто более похожее на SQL Profiler, но со знанием NHibernate.
Добавить Intelliesnse в файл сопоставления
В этой главе мы добавим IntelliSense в наши файлы сопоставления NHibernate ( файлы * .hbm.xml) . Как вы заметили при сопоставлении класса ученика домена, в настоящее время у нас нет IntelliSense. Очень полезно иметь в наличии схемы XML . Таким образом, в этой главе вы поймете, как добавить IntelliSense в Visual Studio для этих файлов NHibernate XML.
Откройте файл сопоставления, и вы увидите, что пункт меню XML появляется в главном меню.
Выберите пункт меню XML → Схемы…, и отобразится диалоговое окно Схемы XML.
Нажмите кнопку «Добавить…», которая находится в правом верхнем углу диалогового окна, которое открывает диалоговое окно файла. Теперь перейдите в папку пакетов , которая находится в папке Solution вашего проекта, и вы увидите различные пакеты, включенные в ваш проект.
Теперь дважды щелкните папку NHibernate.4. ***, и вы увидите файлы двух схем (* .xsd) или файлы определения схемы XML, которые определяют конфигурацию и отображение NHibernate.
Выберите эти два файла схемы и нажмите кнопку «Открыть».
Вы можете видеть, что схемы NHibernate добавляются в диалог схем XML. Нажмите кнопку ОК. Теперь давайте начнем новый тег свойства, и вы увидите, что у нас есть полный IntelliSense здесь.
IntelliSense теперь доступен для вас, что экономит много времени при объектно-реляционном отображении.
NHibernate — Отображение типов данных
В этой главе мы рассмотрим типы данных отображения. Сопоставление сущностей является простым, классы сущностей всегда сопоставляются с таблицами базы данных с использованием элементов сопоставления <class>, <subclass> и <join-subclass> . Типам значений нужно нечто большее, именно здесь требуются типы отображения.
NHibernate может отображать самые разные типы данных. Вот список наиболее распространенных типов данных, которые поддерживаются.
Тип отображения | Тип .NET | System.Data.DbType |
---|---|---|
Int16 | System.Int16 | DbType.Int16 |
Int32 | System.Int32 | DbType.Int32 |
Int64 | System.Int64 | DbType.Int64 |
не замужем | System.Single | DbType.Single |
двойной | System.Double | DbType.Double |
Десятичный | System.Decimal | DbType.Decimal |
строка | System.String | DbType.String |
AnsiString | System.String | DbType.AnsiString |
Байт | System.Byte | DbType.Byte |
голец | System.Char | DbType.StringFixedLength — один символ |
AnsiChar | System.Char | DbType.AnsiStringFixedLength — один символ |
логический | System.Boolean | DbType.Boolean |
Guid | System.Guid | DbType.Guid |
PersistentEnum | System.Enum (перечисление) | DbType для базового значения |
TrueFalse | System.Boolean | DbType.AnsiStringFixedLength — либо «T», либо «F» |
Да нет | System.Boolean | DbType.AnsiStringFixedLength — либо «Y», либо «N» |
DateTime | DateTime | DbType.DateTime — игнорирует миллисекунды |
Клещи | System.DateTime | DbType.Int64 |
Промежуток времени | System.TimeSpan | DbType.Int64 |
Отметка | System.DateTime | DbType.DateTime — настолько, насколько это поддерживает база данных |
двоичный | System.Byte [] | DbType.Binary |
блоб | System.Byte [] | DbType.Binary |
StringClob | System.String | DbType.String |
Сериализуемый | Любой объект System.Object, помеченный атрибутом SerializableAttribute. | DbType.Binary |
CultureInfo | System.Globalization.CultureInfo | DbType.String — пять символов для культуры |
Тип | Тип системы | DbType.String, содержащий квалифицированное имя сборки |
Приведенная выше таблица подробно объясняет нижеуказанные указатели.
-
Все, начиная от простых числовых типов и заканчивая строками, которые можно отображать различными способами, используя varchars, chars и т. Д., А также строковые объекты и все разнообразие типов, поддерживаемых базами данных.
-
Он также может отображать логические поля, как поля, использующие нули, так и поля, символьные поля, которые содержат истину, ложь или T и F.
-
Существует множество способов определения того, как это отображается на бэкэнд, логические значения в базе данных.
-
Мы можем обрабатывать отображение DateTime , включая и исключая смещения часового пояса, летнее время и т. Д.
-
Мы также можем отобразить перечисления ; мы можем сопоставить их со строками или их базовыми числовыми значениями.
Все, начиная от простых числовых типов и заканчивая строками, которые можно отображать различными способами, используя varchars, chars и т. Д., А также строковые объекты и все разнообразие типов, поддерживаемых базами данных.
Он также может отображать логические поля, как поля, использующие нули, так и поля, символьные поля, которые содержат истину, ложь или T и F.
Существует множество способов определения того, как это отображается на бэкэнд, логические значения в базе данных.
Мы можем обрабатывать отображение DateTime , включая и исключая смещения часового пояса, летнее время и т. Д.
Мы также можем отобразить перечисления ; мы можем сопоставить их со строками или их базовыми числовыми значениями.
Давайте рассмотрим простой пример, в котором у нас есть одинаковые имена свойств как в базе данных, так и в классе Student.
Теперь давайте изменим FirstMidName на FirstName в классе ученика, где мы не будем изменять столбец FirstMidName, но мы увидим, как сообщить NHibernate, что нужно выполнить это преобразование. Следующее — обновленный студенческий класс.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace NHibernateDemoApp { class Student { public virtual int ID { get; set; } public virtual string LastName { get; set; } public virtual string FirstName { get; set; } } }
Вот реализация файла сопоставления NHibernate.
<?xml version = "1.0" encoding = "utf-8" ?> <hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemoApp" namespace = "NHibernateDemoApp"> <class name = "Student"> <id name = "ID"> <generator class = "native"/> </id> <property name = "LastName"/> <property name = "FirstName" column = "FirstMidName" type = "String"/> </class> </hibernate-mapping>
В этом примере предположим, что поле FirstName является строкой .NET, а столбец FirstMidName является SQL nvarchar . Теперь, чтобы сообщить NHibernate, как выполнить это преобразование, установите имя, равное FirstName, и столбец, равный FirstMidName, и укажите тип отображения, равный String, который подходит для этого конкретного преобразования.
Ниже приведена реализация файла Program.cs .
using HibernatingRhinos.Profiler.Appender.NHibernate; using NHibernate.Cfg; using NHibernate.Dialect; using NHibernate.Driver; using System; using System.Linq; using System.Reflection; namespace NHibernateDemoApp { class Program { static void Main(string[] args) { NHibernateProfiler.Initialize(); var cfg = new Configuration(); String Data Source = asia13797\\sqlexpress; String Initial Catalog = NHibernateDemoDB; String Integrated Security = True; String Connect Timeout = 15; String Encrypt = False; String TrustServerCertificate = False; String ApplicationIntent = ReadWrite; String MultiSubnetFailover = False; cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source + Initial Catalog + Integrated Security + Connect Timeout + Encrypt + TrustServerCertificate + ApplicationIntent + MultiSubnetFailover"; x.Driver<SqlClientDriver>(); x.Dialect<MsSql2008Dialect>(); x.LogSqlInConsole = true; }); cfg.AddAssembly(Assembly.GetExecutingAssembly()); var sefact = cfg.BuildSessionFactory(); using (var session = sefact.OpenSession()) { using (var tx = session.BeginTransaction()) { var students = session.CreateCriteria<Student>().List<Student>(); Console.WriteLine("\nFetch the complete list again\n"); foreach (var student in students) { Console.WriteLine("{0} \t{1} \t{2}", student.ID, student.FirstName, student.LastName); } tx.Commit(); } Console.ReadLine(); } } } }
Теперь, когда вы запустите ваше приложение, вы увидите следующий вывод.
NHibernate: SELECT this_.ID as ID0_0_, this_.LastName as LastName0_0_, this_.FirstMidName as FirstMid3_0_0_ FROM Student this_ Fetch the complete list again 3 Allan Bommer 4 Jerry Lewis
Как вы можете видеть, он сопоставил другое имя свойства с именем столбца в базе данных.
Давайте рассмотрим другой пример, в котором мы добавим еще одно свойство в класс Student типа enum . Вот реализация студенческого класса.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace NHibernateDemoApp { class Student { public virtual int ID { get; set; } public virtual string LastName { get; set; } public virtual string FirstName { get; set; } public virtual StudentAcademicStanding AcademicStanding { get; set; } } public enum StudentAcademicStanding { Excellent, Good, Fair, Poor, Terrible } }
Как вы можете видеть, перечисление имеет множество различных значений, которые оно может иметь, например, Отлично, Хорошо, Удовлетворительно, Плохо и Ужасно.
Перейдя к файлу сопоставления, вы увидите, что каждое из этих свойств перечислено в файле сопоставления, включая недавно добавленное свойство AcademicStanding .
<?xml version = "1.0" encoding = "utf-8" ?> <hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemoApp" namespace = "NHibernateDemoApp"> <class name = "Student"> <id name = "ID"> <generator class = "native"/> </id> <property name = "LastName"/> <property name = "FirstName" column = "FirstMidName" type = "String"/> <property name = "AcademicStanding"/> </class> </hibernate-mapping>
Теперь нам также нужно изменить базу данных, поэтому перейдите в проводник объектов SQL Server, щелкните правой кнопкой мыши базу данных и выберите опцию Новый запрос….
Он откроет редактор запросов и затем укажет следующий запрос.
DROP TABLE [dbo].[Student] CREATE TABLE [dbo].[Student] ( [ID] INT IDENTITY (1, 1) NOT NULL, [LastName] NVARCHAR (MAX) NULL, [FirstMidName] NVARCHAR (MAX) NULL, [AcademicStanding] NCHAR(10) NULL, CONSTRAINT [PK_dbo.Student] PRIMARY KEY CLUSTERED ([ID] ASC) );
Этот запрос сначала удалит существующую таблицу учеников, а затем создаст новую таблицу.
Нажмите на значок Выполнить, как показано выше. После успешного выполнения запроса вы увидите сообщение.
Разверните базу данных и раскрывающийся список Таблица, затем щелкните правой кнопкой мыши таблицу Student и выберите View Designer.
Теперь вы увидите вновь созданную таблицу, которая также имеет новое свойство AcademicStanding.
Давайте добавим две записи, как показано в следующем файле Program.cs .
using HibernatingRhinos.Profiler.Appender.NHibernate; using NHibernate.Cfg; using NHibernate.Dialect; using NHibernate.Driver; using System; using System.Linq; using System.Reflection; namespace NHibernateDemoApp { class Program { static void Main(string[] args) { NHibernateProfiler.Initialize(); var cfg = new Configuration(); String Data Source = asia13797\\sqlexpress; String Initial Catalog = NHibernateDemoDB; String Integrated Security = True; String Connect Timeout = 15; String Encrypt = False; String TrustServerCertificate = False; String ApplicationIntent = ReadWrite; String MultiSubnetFailover = False; cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source + Initial Catalog + Integrated Security + Connect Timeout + Encrypt + TrustServerCertificate + ApplicationIntent + MultiSubnetFailover"; x.Driver<SqlClientDriver>(); x.Dialect<MsSql2008Dialect>(); }); cfg.AddAssembly(Assembly.GetExecutingAssembly()); var sefact = cfg.BuildSessionFactory(); using (var session = sefact.OpenSession()) { using (var tx = session.BeginTransaction()) { var student1 = new Student { ID = 1, FirstName = "Allan", LastName = "Bommer", AcademicStanding = StudentAcademicStanding.Excellent }; var student2 = new Student { ID = 2, FirstName = "Jerry", LastName = "Lewis", AcademicStanding = StudentAcademicStanding.Good }; session.Save(student1); session.Save(student2); var students = session.CreateCriteria<Student>().List<Student>(); Console.WriteLine("\nFetch the complete list again\n"); foreach (var student in students) { Console.WriteLine("{0} \t{1} \t{2} \t{3}", student.ID, student.FirstName, student.LastName, student.AcademicStanding); } tx.Commit(); } Console.ReadLine(); } } } }
Теперь давайте запустим ваше приложение, и вы увидите следующий вывод в окне консоли.
Fetch the complete list again 1 Allan Bommer Excellent 2 Jerry Lewis Good
Теперь давайте посмотрим на базу данных, щелкнув правой кнопкой мыши на Студенческий стол.
Выберите Просмотр данных, и вы увидите две записи в таблице учеников, как показано на следующем снимке экрана.
Вы видите, что добавлены две записи, и у Аллана есть AcademicStanding 0, а у Джерри есть AcademicStanding 1. Это потому, что в .Net первое значение перечисления по умолчанию имеет 0, что отлично, если вы посмотрите на StudentAcademicStanding . Принимая во внимание, что в файле Student.cs «Good» является вторым, поэтому он имеет значение 1.
NHibernate — Конфигурация
В этой главе мы рассмотрим конфигурацию NHibernate. У нас есть разные способы настройки NHibernate. Он делится на две основные группы
- Конфигурация на основе XML
- Конфигурация на основе кода
Конфигурация на основе кода
Конфигурация на основе кода встроена в NHibernate. Он был представлен в NHibernate 3, и до сих пор мы использовали конфигурацию баз кода.
String Data Source = asia13797\\sqlexpress; String Initial Catalog = NHibernateDemoDB; String Integrated Security = True; String Connect Timeout = 15; String Encrypt = False; String TrustServerCertificate = False; String ApplicationIntent = ReadWrite; String MultiSubnetFailover = False; cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source + Initial Catalog + Integrated Security + Connect Timeout + Encrypt + TrustServerCertificate + ApplicationIntent + MultiSubnetFailover"; x.Driver<SqlClientDriver>(); x.Dialect<MsSql2008Dialect>(); x.LogSqlInConsole = true; }); cfg.AddAssembly(Assembly.GetExecutingAssembly());
Все конфигурации указаны в коде C #. Здесь вы можете видеть, что у нас есть наш новый объект конфигурации, а затем мы используем простую конфигурацию, которая была представлена в NHibernate 3.1 для настройки базы данных. Какую строку подключения мы используем, к какой базе данных мы подключаемся и используемому диалекту. Мы также добавляем нашу картографическую сборку прямо сюда.
Конфигурация на основе XML
Если вы используете конфигурацию на основе XML, вы можете использовать файл hibernate.cfg.xml , который представляет собой просто отдельный xml-файл с использованием схемы NHibernate, или вы можете встроить эту конкретную конфигурацию NHibernate в свое приложение или web.cfg . Имя hibernate.cfg.xml по умолчанию, но мы также можем использовать произвольное имя для этого XML-файла.
Давайте посмотрим на конфигурацию на основе XML, добавив новый xml-файл в проект NHibernateDemoApp и назовем его hibernate.cfg.xml.
Введите следующую информацию в файл hibernate.cfg.xml.
<?xml version = "1.0" encoding = "utf-8" ?> <hibernate-configuration xmlns = "urn:nhibernate-configuration-2.2"> <session-factory> <property name = "connection.connection_string"> Data Source = asia13797\\sqlexpress; Initial Catalog = NHibernateDemoDB; Integrated Security = True; Connect Timeout = 15; Encrypt = False; TrustServerCertificate = False; ApplicationIntent = ReadWrite; MultiSubnetFailover = False; </property> <property name = "connection.driver_class"> NHibernate.Driver.SqlClientDriver </property> <property name = "dialect"> NHibernate.Dialect.MsSql2008Dialect </property> <mapping assembly = "NHibernateDemoApp"/> </session-factory> </hibernate-configuration>
Как вы можете видеть в приведенном выше XML-файле, мы указали ту же конфигурацию, что и в C #.
Теперь давайте прокомментируем эту конфигурацию из файла Program.cs и просто вызовем метод Configure () , который загрузит файл hibernate.cfg.xml, как показано ниже.
using HibernatingRhinos.Profiler.Appender.NHibernate; using NHibernate.Cfg; using NHibernate.Dialect; using NHibernate.Driver; using System; using System.Linq; using System.Reflection; namespace NHibernateDemoApp { class Program { static void Main(string[] args) { NHibernateProfiler.Initialize(); var cfg = new Configuration(); //cfg.DataBaseIntegration(x => //{ // x.ConnectionString = "Data Source = asia13797;\\sqlexpress Initial Catalog = NHibernateDemoDB; Integrated Security = True; Connect Timeout = 15; Encrypt =False; TrustServerCertificate = False; ApplicationIntent = ReadWrite; MultiSubnetFailover = False"; // x.Driver<SqlClientDriver>(); // x.Dialect<MsSql2008Dialect>(); // x.LogSqlInConsole = true; //}); //cfg.AddAssembly(Assembly.GetExecutingAssembly()); cfg.Configure(); var sefact = cfg.BuildSessionFactory(); using (var session = sefact.OpenSession()) { using (var tx = session.BeginTransaction()) { var students = session.CreateCriteria<Student>().List<Student>(); Console.WriteLine("\nFetch the complete list again\n"); foreach (var student in students) { Console.WriteLine("{0} \t{1} \t{2} \t{3}", student.ID, student.FirstName, student.LastName, student.AcademicStanding); } tx.Commit(); } Console.ReadLine(); } } } }
Давайте снова запустим ваше приложение, и вы увидите тот же результат.
Fetch the complete list again 1 Allan Bommer Excellent 2 Jerry Lewis Good
NHibernate — переопределить конфигурацию
В этой главе мы рассмотрим, как переопределить конфигурацию NHibernate. Есть только несколько вещей, которые вы должны иметь в виду.
-
Прежде всего, конфигурация в NHibernate является аддитивной.
-
Таким образом, вам не нужно просто использовать один XML-файл, или вам не нужно просто использовать конфигурацию на основе кода или свободный NHibernate.
-
Вы можете смешивать и сочетать все эти методы в зависимости от того, как вы хотите настроить приложение.
-
Важно помнить, что, наконец, конфигурация выигрывает.
Прежде всего, конфигурация в NHibernate является аддитивной.
Таким образом, вам не нужно просто использовать один XML-файл, или вам не нужно просто использовать конфигурацию на основе кода или свободный NHibernate.
Вы можете смешивать и сочетать все эти методы в зависимости от того, как вы хотите настроить приложение.
Важно помнить, что, наконец, конфигурация выигрывает.
В следующем примере вы видите, что мы создаем наш объект конфигурации, настраиваем его с помощью конфигурации на основе кода и, наконец, вызываем метод cfg.configure () , который загружает файл hibernate.cfg.xml.
String Data Source = asia13797\\sqlexpress; String Initial Catalog = NHibernateDemoDB; String Integrated Security = True; String Connect Timeout = 15; String Encrypt = False; String TrustServerCertificate = False; String ApplicationIntent = ReadWrite; String MultiSubnetFailover = False; cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source + Initial Catalog + Integrated Security + Connect Timeout + Encrypt + TrustServerCertificate + ApplicationIntent + MultiSubnetFailover"; x.Driver<SqlClientDriver>(); x.Dialect<MsSql2008Dialect>(); x.LogSqlInConsole = true; }); cfg.Configure();
-
Таким образом, все, что находится внутри файла hibernate.cfg.xml, переопределяет параметры, установленные конфигурацией на основе кода.
-
Путем изменения этих двух процессов мы можем получить значения по умолчанию внутри hibernate.cfg.xml, а затем выполнить наши переопределения внутри конфигурации на основе кода.
-
Ничто не исключает, если вы используете конфигурацию на основе кода, а также ничто не мешает вам использовать файл hibernate.cfg.xml.
Таким образом, все, что находится внутри файла hibernate.cfg.xml, переопределяет параметры, установленные конфигурацией на основе кода.
Путем изменения этих двух процессов мы можем получить значения по умолчанию внутри hibernate.cfg.xml, а затем выполнить наши переопределения внутри конфигурации на основе кода.
Ничто не исключает, если вы используете конфигурацию на основе кода, а также ничто не мешает вам использовать файл hibernate.cfg.xml.
Давайте рассмотрим простой пример, в котором мы переопределим конфигурацию, используя комбинацию конфигурации на основе xml и кода.
Давайте также переместим строку подключения в файл app.config, используя следующий код.
<?xml version = "1.0" encoding = "utf-8" ?> <configuration> <startup> <supportedRuntime version = "v4.0" sku = ".NETFramework,Version = v4.5" /> </startup> <connectionStrings> <add name = "default" connectionString = "Data Source = asia13797\\sqlexpress; Initial Catalog = NHibernateDemoDB; Integrated Security = True; Connect Timeout = 15; Encrypt = False; TrustServerCertificate = False; ApplicationIntent = ReadWrite; MultiSubnetFailover = False"/> </connectionStrings> </configuration>
Строка подключения находится в каком-то файле app.config с именем по умолчанию. Теперь нам нужно упомянуть имя по умолчанию в файле hibernate.cfg.xml вместо строки подключения.
<?xml version = "1.0" encoding = "utf-8" ?> <hibernate-configuration xmlns = "urn:nhibernate-configuration-2.2"> <session-factory> <property name = "connection.connection_string">default</property> <property name = "connection.driver_class"> NHibernate.Driver.SqlClientDriver </property> <property name = "dialect"> NHibernate.Dialect.MsSql2008Dialect </property> <mapping assembly = "NHibernateDemoApp"/> </session-factory> </hibernate-configuration>
Давайте прокомментируем часть строки подключения, драйвер и часть диалекта из конфигурации на основе кода, поскольку программа будет считывать ее из файла hibernate.cfg.xml, а часть LogSqlInConsole останется в конфигурации на основе кода.
using HibernatingRhinos.Profiler.Appender.NHibernate; using NHibernate.Cfg; using NHibernate.Dialect; using NHibernate.Driver; using System; using System.Linq; using System.Reflection; namespace NHibernateDemoApp { class Program { static void Main(string[] args) { NHibernateProfiler.Initialize(); var cfg = new Configuration(); String Data Source = asia13797\\sqlexpress; String Initial Catalog = NHibernateDemoDB; String Integrated Security = True; String Connect Timeout = 15; String Encrypt = False; String TrustServerCertificate = False; String ApplicationIntent = ReadWrite; String MultiSubnetFailover = False; cfg.DataBaseIntegration(x = > { //x.ConnectionString = "Data Source + Initial Catalog + Integrated Security + Connect Timeout + Encrypt + TrustServerCertificate + ApplicationIntent + MultiSubnetFailover"; //x.Driver<SqlClientDriver>(); //x.Dialect<MsSql2008Dialect>(); x.LogSqlInConsole = true; }); cfg.Configure(); cfg.AddAssembly(Assembly.GetExecutingAssembly()); var sefact = cfg.BuildSessionFactory(); using (var session = sefact.OpenSession()) { using (var tx = session.BeginTransaction()) { var students = session.CreateCriteria<Student>().List<Student>(); Console.WriteLine("\nFetch the complete list again\n"); foreach (var student in students) { Console.WriteLine("{0} \t{1} \t{2} \t{3}", student.ID, student.FirstName, student.LastName, student.AcademicStanding); } tx.Commit(); } Console.ReadLine(); } } } }
Теперь, когда вы запустите приложение, вы увидите, что программа прочитала журнал из конфигурации на основе кода и другую конфигурацию из файла hibernate.cfg.xml.
NHibernate: SELECT this_.ID as ID0_0_, this_.LastName as LastName0_0_, this_.FirstMidName as FirstMid3_0_0_, this_.AcademicStanding as Academic4_0_0_ FROM Student this_ Fetch the complete list again 1 Allan Bommer Excellent 2 Jerry Lewis Good
Итак, теперь у нас есть некоторые наши настройки внутри нашего файла hibernate.cfg.xml , некоторые из них находятся внутри конфигурации на основе кода, и в зависимости от порядка вызова на основе кода в сравнении с configure () , мы можем изменить, какой из они перекрывают другие.
NHibernate — размер партии
В этой главе мы рассмотрим обновление размера партии. Размер пакета позволяет вам контролировать количество обновлений, которые выходят за одну поездку в вашу базу данных для поддерживаемых баз данных.
-
Размер пакета обновления по умолчанию установлен на NHibernate 3.2.
-
Но если вы используете более раннюю версию или вам нужно настроить приложение NHibernate, вы должны посмотреть на размер пакета обновления, который является очень полезным параметром, который можно использовать для настройки производительности NHibernate.
-
На самом деле размер пакета определяет, сколько вставок нужно вставить в группу в базу данных.
-
На данный момент только SQL Server и Oracle поддерживают эту опцию, поскольку базовый поставщик баз данных должен поддерживать пакетирование запросов.
Размер пакета обновления по умолчанию установлен на NHibernate 3.2.
Но если вы используете более раннюю версию или вам нужно настроить приложение NHibernate, вы должны посмотреть на размер пакета обновления, который является очень полезным параметром, который можно использовать для настройки производительности NHibernate.
На самом деле размер пакета определяет, сколько вставок нужно вставить в группу в базу данных.
На данный момент только SQL Server и Oracle поддерживают эту опцию, поскольку базовый поставщик баз данных должен поддерживать пакетирование запросов.
Давайте рассмотрим простой пример, в котором мы установили размер пакета равным 10, который будет вставлять 10 записей в набор.
cfg.DataBaseIntegration(x => { x.ConnectionString = "default"; x.Driver<SqlClientDriver>(); x.Dialect<MsSql2008Dialect>(); x.LogSqlInConsole = true; x.BatchSize = 10; });
Вот полная реализация, в которой 25 записей будут добавлены в базу данных.
using HibernatingRhinos.Profiler.Appender.NHibernate; using NHibernate.Cfg; using NHibernate.Dialect; using NHibernate.Driver; using System; using System.Linq; using System.Reflection; namespace NHibernateDemoApp { class Program { static void Main(string[] args) { NHibernateProfiler.Initialize(); var cfg = new Configuration(); String Data Source = asia13797\\sqlexpress; String Initial Catalog = NHibernateDemoDB; String Integrated Security = True; String Connect Timeout = 15; String Encrypt = False; String TrustServerCertificate = False; String ApplicationIntent = ReadWrite; String MultiSubnetFailover = False; cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source + Initial Catalog + Integrated Security + Connect Timeout + Encrypt + TrustServerCertificate + ApplicationIntent + MultiSubnetFailover"; x.Driver>SqlClientDriver<(); x.Dialect>MsSql2008Dialect>(); x.LogSqlInConsole = true; x.BatchSize = 10; }); //cfg.Configure(); cfg.AddAssembly(Assembly.GetExecutingAssembly()); var sefact = cfg.BuildSessionFactory(); using (var session = sefact.OpenSession()) { using (var tx = session.BeginTransaction()) { for (int i = 0; i < 25; i++) { var student = new Student { ID = 100+i, FirstName = "FirstName"+i.ToString(), LastName = "LastName" + i.ToString(), AcademicStanding = StudentAcademicStanding.Good }; session.Save(student); } tx.Commit(); var students = session.CreateCriteria<Student>().List<Student>(); Console.WriteLine("\nFetch the complete list again\n"); foreach (var student in students) { Console.WriteLine("{0} \t{1} \t{2} \t{3}", student.ID,student.FirstName, student.LastName, student.AcademicStanding); } } Console.ReadLine(); } } } }
Теперь давайте запустим ваше приложение, и вы увидите, что все эти обновления переходят на профилировщик NHibernate. У нас есть 26 индивидуальных поездок в базу данных 25 для вставки и один поиск списка студентов.
Теперь, почему это? Причина в том, что NHibernate должен выполнить выборку идентификатора области, поскольку мы используем стратегию генерирования собственного идентификатора в файле сопоставления для идентификатора, как показано в следующем коде.
<?xml version = "1.0" encoding = "utf-8" ?> <hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemoApp" namespace = "NHibernateDemoApp"> <class name = "Student"> <id name = "ID"> <generator class = "native"/> </id> <property name = "LastName"/> <property name = "FirstName" column = "FirstMidName" type = "String"/> <property name = "AcademicStanding"/> </class> </hibernate-mapping>
Поэтому нам нужно использовать другой метод, такой как метод guid.comb . Если мы собираемся перейти на guid.comb, нам нужно перейти к нашему клиенту и изменить его на guid . Так что это будет работать нормально. Теперь давайте перейдем с нативного на guid.comb, используя следующий код.
<?xml version = "1.0" encoding = "utf-8" ?> <hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemoApp" namespace = "NHibernateDemoApp"> <class name = "Student"> <id name = "ID"> <generator class = "guid.comb"/> </id> <property name = "LastName"/> <property name = "FirstName" column = "FirstMidName" type = "String"/> <property name = "AcademicStanding"/> </class> </hibernate-mapping>
Так что именно база данных отвечает за генерацию этих идентификаторов. Единственный способ, которым NHibernate может узнать, какой идентификатор был сгенерирован, — это сразу же выбрать его. Или же, если мы создали группу студентов, она не сможет соответствовать идентификатору студента, который был создан.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace NHibernateDemoApp { class Student { public virtual Guid ID { get; set; } public virtual string LastName { get; set; } public virtual string FirstName { get; set; } public virtual StudentAcademicStanding AcademicStanding { get; set; } } public enum StudentAcademicStanding { Excellent, Good, Fair, Poor, Terrible } }
Нам просто нужно обновить нашу базу данных. Давайте отбросим таблицу учеников и создадим новую таблицу, указав следующий запрос, поэтому перейдите в обозреватель объектов SQL Server, щелкните правой кнопкой мыши базу данных и выберите параметр « Новый запрос …».
Он откроет редактор запросов и затем укажет следующий запрос.
DROP TABLE [dbo].[Student] CREATE TABLE [dbo].[Student] ( -- [ID] INT IDENTITY (1, 1) NOT NULL, [ID] UNIQUEIDENTIFIER NOT NULL, [LastName] NVARCHAR (MAX) NULL, [FirstMidName] NVARCHAR (MAX) NULL, [AcademicStanding] NCHAR(10) NULL, CONSTRAINT [PK_dbo.Student] PRIMARY KEY CLUSTERED ([ID] ASC) );
Этот запрос сначала удалит существующую таблицу учеников, а затем создаст новую таблицу. Как видите, мы использовали UNIQUEIDENTIFIER, а не целочисленный первичный ключ в качестве идентификатора.
Выполните этот запрос, а затем перейдите в представление Designer, и вы увидите, что теперь идентификатор создается с уникальным идентификатором, как показано на следующем рисунке.
Теперь нам нужно удалить идентификатор из файла program.cs при вставке данных, потому что теперь он автоматически сгенерирует для него направляющие .
using HibernatingRhinos.Profiler.Appender.NHibernate; using NHibernate.Cfg; using NHibernate.Dialect; using NHibernate.Driver; using System; using System.Linq; using System.Reflection; namespace NHibernateDemoApp { class Program { static void Main(string[] args) { NHibernateProfiler.Initialize(); var cfg = new Configuration(); String Data Source = asia13797\\sqlexpress; String Initial Catalog = NHibernateDemoDB; String Integrated Security = True; String Connect Timeout = 15; String Encrypt = False; String TrustServerCertificate = False; String ApplicationIntent = ReadWrite; String MultiSubnetFailover = False; cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source + Initial Catalog + Integrated Security + Connect Timeout + Encrypt + TrustServerCertificate + ApplicationIntent + MultiSubnetFailover"; x.Driver<SqlClientDriver>(); x.Dialect<MsSql2008Dialect>(); x.LogSqlInConsole = true; x.BatchSize = 10; }); //cfg.Configure(); cfg.AddAssembly(Assembly.GetExecutingAssembly()); var sefact = cfg.BuildSessionFactory(); using (var session = sefact.OpenSession()) { using (var tx = session.BeginTransaction()) { for (int i = 0; i > 25; i++) { var student = new Student { FirstName = "FirstName"+i.ToString(), LastName = "LastName" + i.ToString(), AcademicStanding = StudentAcademicStanding.Good }; session.Save(student); } tx.Commit(); var students = session.CreateCriteria<Student>().List<Student>(); Console.WriteLine("\nFetch the complete list again\n"); foreach (var student in students) { Console.WriteLine("{0} \t{1} \t{2} \t{3}", student.ID, student.FirstName,student.LastName, student.AcademicStanding); } } Console.ReadLine(); } } } }
Теперь снова запустите приложение и посмотрите на профилировщик NHibernate. Теперь профилировщик NHibernate вместо 26 поездок туда и обратно сделает только четыре.
Он вставляет десять строк в таблицу, затем еще десять строк, а затем оставшиеся пять. И после фиксации он вставил еще одну для извлечения всех записей.
-
Таким образом, это разделено на группы по десять, насколько это возможно.
-
Поэтому, если вы делаете много вставок, это может значительно улучшить производительность вставки в вашем приложении, потому что вы можете пакетировать ее.
-
Это связано с тем, что NHibernate сам назначает эти направляющие с помощью алгоритма guid.comb , и для этого ему не нужно полагаться на базу данных.
-
Таким образом, использование размера пакета — отличный способ настроить его.
Таким образом, это разделено на группы по десять, насколько это возможно.
Поэтому, если вы делаете много вставок, это может значительно улучшить производительность вставки в вашем приложении, потому что вы можете пакетировать ее.
Это связано с тем, что NHibernate сам назначает эти направляющие с помощью алгоритма guid.comb , и для этого ему не нужно полагаться на базу данных.
Таким образом, использование размера пакета — отличный способ настроить его.
NHibernate — Кэширование
В этой главе мы рассмотрим, как работает кэширование в приложениях NHibernate. Имеет встроенную поддержку кеширования. Это выглядит как простая функция, но на самом деле это одна из самых сложных функций. Начнем с кэша первого уровня.
Кэш первого уровня
Этот механизм кеширования по умолчанию включен в NHibernate, и нам не нужно ничего делать для работы с кешем. Чтобы понять это, давайте рассмотрим простой пример, поскольку вы можете видеть, что у нас есть две записи в нашей базе данных.
Теперь в этом примере мы извлечем учащегося, чей ID равен 1, и дважды будем использовать один и тот же запрос сеанса, как показано в следующем коде.
using HibernatingRhinos.Profiler.Appender.NHibernate; using NHibernate.Cache; using NHibernate.Cfg; using NHibernate.Dialect; using NHibernate.Driver; using NHibernate.Linq; using System; using System.Linq; using System.Reflection; namespace NHibernateDemoApp { class Program { static void Main(string[] args) { NHibernateProfiler.Initialize(); var cfg = new Configuration(); String Data Source = asia13797\\sqlexpress; String Initial Catalog = NHibernateDemoDB; String Integrated Security = True; String Connect Timeout = 15; String Encrypt = False; String TrustServerCertificate = False; String ApplicationIntent = ReadWrite; String MultiSubnetFailover = False; cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source + Initial Catalog + Integrated Security + Connect Timeout + Encrypt + TrustServerCertificate + ApplicationIntent + MultiSubnetFailover"; x.Driver<SqlClientDriver>(); x.Dialect<MsSql2008Dialect>(); x.LogSqlInConsole = true; x.BatchSize = 10; }); //cfg.Configure(); cfg.Cache(c => { c.UseMinimalPuts = true; c.UseQueryCache = true; }); cfg.SessionFactory().Caching .Through<HashtableCacheProvider>() .WithDefaultExpiration(1440); cfg.AddAssembly(Assembly.GetExecutingAssembly()); var sefact = cfg.BuildSessionFactory(); using (var session = sefact.OpenSession()){ using (var tx = session.BeginTransaction()) { var studentUsingTheFirstQuery = session.Get<Student>(1); var studentUsingTheSecondQuery = session.Get<Student>(1); } Console.ReadLine(); } } } }
Теперь давайте запустим это приложение и увидим результат в NHibernate Profiler.
Вы будете удивлены, увидев, что NHibernate запускает только один запрос. Вот как NHibernate использует кэш первого уровня. Когда выполняется первый запрос, NHibernate кэширует Student с ID = 1 в своем кеше первого уровня.
Итак, когда второй запрос выполняется, тогда NHibernate сначала ищет сущность кеша первого уровня с идентификатором ID = 1, если он находит эту сущность, то NHibernate знает, что нет необходимости запускать другой запрос, чтобы снова получить тот же объект сотрудника. ,
NHibernate — Компонент отображения
В этой главе мы будем говорить о сопоставлении компонентов. В NHibernate компонент представляет собой объект значения . У него нет собственной идентичности.
-
Примером этого может быть денежный объект, в кошельке или кошельке могут быть деньги, но точная идентификация этих денег не имеет значения.
-
У него нет собственного первичного ключа, но сами компоненты хранятся в той же таблице, что и объект-владелец.
Примером этого может быть денежный объект, в кошельке или кошельке могут быть деньги, но точная идентификация этих денег не имеет значения.
У него нет собственного первичного ключа, но сами компоненты хранятся в той же таблице, что и объект-владелец.
Давайте рассмотрим простой пример, в котором у студента есть адрес, который является объектом класса Location, связанного с ним.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace NHibernateDemoApp { class Student { public virtual int ID { get; set; } public virtual string LastName { get; set; } public virtual string FirstName { get; set; } public virtual StudentAcademicStanding AcademicStanding { get; set; } public virtual Location Address { get; set; } } public class Location { public virtual string Street { get; set; } public virtual string City { get; set; } public virtual string Province { get; set; } public virtual string Country { get; set; } } public enum StudentAcademicStanding { Excellent, Good, Fair, Poor, Terrible } }
Теперь нам также необходимо обновить базу данных, выполнив следующий запрос, который сначала удалит таблицу Student, а затем создаст новую таблицу, которая также будет содержать столбец для класса Location.
DROP TABLE [dbo].[Student] CREATE TABLE [dbo].[Student] ( [ID] INT IDENTITY (1, 1) NOT NULL, [LastName] NVARCHAR (MAX) NULL, [FirstMidName] NVARCHAR (MAX) NULL, [AcademicStanding] NCHAR(10) NULL, [Street] NVARCHAR (100) NULL, [City] NVARCHAR (100) NULL, [Province] NVARCHAR (100) NULL, [Country] NVARCHAR (100) NULL, CONSTRAINT [PK_dbo.Student] PRIMARY KEY CLUSTERED ([ID] ASC) );
Теперь для сопоставления тех столбцов, которые не являются непосредственно частью класса Student, но они являются свойствами класса Location и объекта класса Location, определяется в классе ученика. Нам нужен компонент, чтобы правильно отобразить его. Давайте создадим компонент в файле student.hbm.xml, как показано в следующем коде.
<?xml version = "1.0" encoding = "utf-8" ?> <hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemoApp" namespace = "NHibernateDemoApp"> <class name = "Student"> <id name = "ID"> <generator class = "native"/> </id> <property name = "LastName"/> <property name = "FirstName" column = "FirstMidName" type = "String"/> <property name = "AcademicStanding"/> <component name = "Address"> <property name = "Street"/> <property name = "City"/> <property name = "Province"/> <property name = "Country"/> </component> </class> </hibernate-mapping>
Этот компонент является адресом, и он имеет эти различные свойства. С этой информацией NHibernate теперь достаточно, чтобы он мог отобразить это.
Теперь вот файл Program.cs, в котором новый объект студента создается и инициализируется, а затем сохраняется в базе данных. Затем он получит список из базы данных.
using HibernatingRhinos.Profiler.Appender.NHibernate; using NHibernate.Cache; using NHibernate.Caches.SysCache; using NHibernate.Cfg; using NHibernate.Dialect; using NHibernate.Driver; using NHibernate.Linq; using System; using System.Linq; using System.Reflection; namespace NHibernateDemoApp { class Program { static void Main(string[] args) { NHibernateProfiler.Initialize(); var cfg = new Configuration(); String Data Source = asia13797\\sqlexpress; String Initial Catalog = NHibernateDemoDB; String Integrated Security = True; String Connect Timeout = 15; String Encrypt = False; String TrustServerCertificate = False; String ApplicationIntent = ReadWrite; String MultiSubnetFailover = False; cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source + Initial Catalog + Integrated Security + Connect Timeout + Encrypt + TrustServerCertificate + ApplicationIntent + MultiSubnetFailover"; x.Driver<SqlClientDriver>(); x.Dialect<MsSql2008Dialect>(); }); cfg.AddAssembly(Assembly.GetExecutingAssembly()); var sefact = cfg.BuildSessionFactory(); using (var session = sefact.OpenSession()) { using (var tx = session.BeginTransaction()) { var student1 = new Student { ID = 1, FirstName = "Allan", LastName = "Bommer", AcademicStanding = StudentAcademicStanding.Poor, Address = new Location { Street = "123 Street", City = "Lahore", Province = "Punjab", Country = "Pakistan" } }; session.Save(student1); tx.Commit(); var students = session.Query<Student>().ToList<Student>(); Console.WriteLine("\nFetch the complete list again\n"); foreach (var student in students) { Console.WriteLine("{0} \t{1} \t{2} \t{3} \t{4} \t{5} \t{6} \t{7}", student.ID, student.FirstName, student.LastName, student.AcademicStanding, student.Address.Street, student.Address.City, student.Address.Province, student.Address.Country ); } } Console.ReadLine(); } } } }
Теперь мы можем запустить это приложение, а NHibernate может сохранить эти значения в базе данных. Когда вы запустите приложение, вы увидите следующий вывод.
Fetch the complete list again 2 Allan Bommer Poor 123 Street Lahore Punjab Pakistan
Вот значения в базе данных.
Компоненты позволяют нам разделить столбцы в таблице базы данных на отдельный класс.
-
Другая вещь, на которую следует обратить внимание, это то, что Location является классом, а не сущностью.
-
Это объект типа значения, и у него нет собственного первичного ключа.
-
Он сохраняется в той же таблице, что и учащийся, в котором он содержится.
-
Вот почему мы используем компонент здесь.
-
Это дает большую гибкость, чтобы изменить наш уровень классов, как наши классы определяются по сравнению с тем, как устроена наша база данных.
Другая вещь, на которую следует обратить внимание, это то, что Location является классом, а не сущностью.
Это объект типа значения, и у него нет собственного первичного ключа.
Он сохраняется в той же таблице, что и учащийся, в котором он содержится.
Вот почему мы используем компонент здесь.
Это дает большую гибкость, чтобы изменить наш уровень классов, как наши классы определяются по сравнению с тем, как устроена наша база данных.
NHibernate — Отношения
В этой главе мы рассмотрим отношения в NHibernate. Давайте обратим наше внимание на то, как мы можем понять отношения в NHibernate. Самый простой способ — подумать об отношениях с точки зрения базы данных.
-
Сначала мы создадим новое приложение, в котором мы создадим некоторые отношения между клиентами и заказчиками.
-
Первые отношения, которые мы собираемся рассмотреть, это классические отношения коллекции.
-
У нас есть клиент с коллекцией заказов.
-
Это отношение «один ко многим», оно представлено в базе данных двумя таблицами, в таблице заказов есть идентификатор клиента, и у нас есть отношение внешнего ключа к клиенту.
Сначала мы создадим новое приложение, в котором мы создадим некоторые отношения между клиентами и заказчиками.
Первые отношения, которые мы собираемся рассмотреть, это классические отношения коллекции.
У нас есть клиент с коллекцией заказов.
Это отношение «один ко многим», оно представлено в базе данных двумя таблицами, в таблице заказов есть идентификатор клиента, и у нас есть отношение внешнего ключа к клиенту.
Для начала нам нужно создать базу данных и две таблицы Customer и Order. Вы можете создать это, указав следующий запрос в SQL Server Explorer.
USE [master] GO CREATE DATABASE [NHibernateDemo] GO USE [NHibernateDemo] GO CREATE TABLE [dbo].[Customer]( [Id] [uniqueidentifier] NOT NULL, [FirstName] [nvarchar](100) NOT NULL, [LastName] [nvarchar](100) NOT NULL, [Points] [int] NULL, [HasGoldStatus] [bit] NULL, [MemberSince] [date] NULL, [CreditRating] [nchar](20) NULL, [AverageRating] [decimal](18, 4) NULL, [Street] [nvarchar](100) NULL, [City] [nvarchar](100) NULL, [Province] [nvarchar](100) NULL, [Country] [nvarchar](100) NULL, PRIMARY KEY CLUSTERED ([Id] ASC) ) GO CREATE TABLE [dbo].[Order]( [Id] [uniqueidentifier] NOT NULL, [CustomerId] [uniqueidentifier] NULL, [Ordered] [datetime] NULL, [Shipped] [datetime] NULL, [Street] [nvarchar](100) NULL, [City] [nvarchar](100) NULL, [Province] [nvarchar](100) NULL, [Country] [nvarchar](100) NULL, PRIMARY KEY CLUSTERED ([Id] ASC) ) GO
Это создаст две таблицы в базе данных. На следующем рисунке показана таблица клиентов.
На следующем рисунке показана таблица заказов, в которой вы можете увидеть связь внешнего ключа с клиентом.
Нам нужно определить строку подключения в файле app.config , здесь приведена реализация файла app.config.
<?xml version = "1.0" encoding = "utf-8" ?> <configuration> <connectionStrings> <add name = "default" connectionString = "Data Source = (localdb)\MSSQLLocalDB;Initial Catalog = NHibernateDemo;Integrated Security = True;Connect Timeout = 30;Encrypt = False;TrustServerCertificate = False; ApplicationIntent = ReadWrite;MultiSubnetFailover = False"/> </connectionStrings> </configuration>
Чтобы установить NHibernate в вашем приложении, выполните следующую команду в окне консоли NuGet Manager.
install-package NHibernate
Чтобы настроить конфигурацию NHibernate, нам нужно определить конфигурацию в файле hibernate.cfg.xml, как показано в следующем коде.
<xml version = "1.0" encoding = "utf-8" ?> <hibernate-configuration xmlns = "urn:nhibernate-configuration-2.2"> <session-factory> <property name = "connection.connection_string_name">default</property> <property name = "connection.driver_class"> NHibernate.Driver.SqlClientDriver </property> <property name = "dialect"> NHibernate.Dialect.MsSql2008Dialect </property> <property name = "show_sql">true</property> </session-factory> </hibernate-configuration>
В этом примере мы будем работать с двумя классами доменов, Customer и Order.
Вот реализация файла Customer.cs, в которой у нас есть два класса, один — класс Customer, а другой — класс Location, в котором объект используется в качестве адреса в классе Customer.
using System; using System.Text; using Iesi.Collections.Generic; namespace NHibernateDemo { public class Customer { public Customer() { MemberSince = DateTime.UtcNow; Orders = new HashedSet<Order>(); } public virtual Guid Id { get; set; } public virtual string FirstName { get; set; } public virtual string LastName { get; set; } public virtual double AverageRating { get; set; } public virtual int Points { get; set; } public virtual bool HasGoldStatus { get; set; } public virtual DateTime MemberSince { get; set; } public virtual CustomerCreditRating CreditRating { get; set; } public virtual Location Address { get; set; } public virtual ISet<Order> Orders { get; set; } public virtual void AddOrder(Order order) { Orders.Add(order); order.Customer = this; } public override string ToString() { var result = new StringBuilder(); result.AppendFormat("{1} {2} ({0})\r\n\tPoints: {3}\r\n\tHasGoldStatus: {4}\r\n\tMemberSince: {5} ({7})\r\n\tCreditRating: {6}\r\n\tAverageRating: {8}\r\n", Id, FirstName, LastName, Points, HasGoldStatus, MemberSince, CreditRating, MemberSince.Kind, AverageRating); result.AppendLine("\tOrders:"); foreach(var order in Orders) { result.AppendLine("\t\t" + order); } return result.ToString(); } } public class Location { public virtual string Street { get; set; } public virtual string City { get; set; } public virtual string Province { get; set; } public virtual string Country { get; set; } } public enum CustomerCreditRating { Excellent, VeryVeryGood, VeryGood, Good, Neutral, Poor, Terrible } }
Вот файл сопоставления Customer.hbm.xml, в котором класс Customer сопоставлен с таблицей Customer.
<?xml version = "1.0" encoding = "utf-8" ?> <hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo" namespace = "NHibernateDemo"> <class name = "Customer"> <id name = "Id"> <generator class = "guid.comb"/> </id> <property name = "FirstName"/> <property name = "LastName"/> <property name = "AverageRating"/> <property name = "Points"/> <property name = "HasGoldStatus"/> <property name = "MemberSince" type = "UtcDateTime"/> <property name = "CreditRating" type = "CustomerCreditRatingType"/> <component name = "Address"> <property name = "Street"/> <property name = "City"/> <property name = "Province"/> <property name = "Country"/> </component> </class> </hibernate-mapping>
У нас также есть класс Order, и здесь есть реализация файла Order.cs .
using System; using Iesi.Collections.Generic; namespace NHibernateDemo { public class Order { public virtual Guid Id { get; set; } public virtual DateTime Ordered { get; set; } public virtual DateTime? Shipped { get; set; } public virtual Location ShipTo { get; set; } public virtual Customer Customer { get; set; } public override string ToString() { return string.Format("Order Id: {0}", Id); } } }
Отношения многие-к-одному
Нам также необходимо сопоставить класс Order с таблицей Order в базе данных, поэтому здесь приведена реализация файла Order.hbm.xml .
<?xml version = "1.0" encoding = "utf-8" ?> <hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo" namespace = "NHibernateDemo"> <class name = "Order" table = "`Order`"> <id name = "Id"> <generator class = "guid.comb"/> </id> <property name = "Ordered"/> <property name = "Shipped"/> <component name = "ShipTo"> <property name = "Street"/> <property name = "City"/> <property name = "Province"/> <property name = "Country"/> </component> <!--<many-to-one name = "Customer" column = "CustomerId" cascade = "save-update"/>--> </class> </hibernate-mapping>
Отношения один-ко-многим
Здесь мы рассмотрим взаимоотношения один-ко-многим, в данном случае между клиентом и заказами. У нас есть наш клиент, мы создаем нового, и вы можете видеть, что коллекция инициализируется следующей парой заказов.
private static Customer CreateCustomer() { var customer = new Customer { FirstName = "John", LastName = "Doe", Points = 100, HasGoldStatus = true, MemberSince = new DateTime(2012, 1, 1), CreditRating = CustomerCreditRating.Good, AverageRating = 42.42424242, Address = CreateLocation() }; var order1 = new Order { Ordered = DateTime.Now }; customer.AddOrder(order1); var order2 = new Order { Ordered = DateTime.Now.AddDays(-1), Shipped = DateTime.Now, ShipTo = CreateLocation() }; customer.AddOrder(order2); return customer; }
Таким образом, мы создадим нового клиента и затем сохраним его, после сохранения мы найдем идентификатор и затем перезагрузим его в другом сеансе в методе Main, как показано в следующей программе.
private static void Main() { var cfg = ConfigureNHibernate(); var sessionFactory = cfg.BuildSessionFactory(); Guid id; using(var session = sessionFactory.OpenSession()) using(var tx = session.BeginTransaction()) { var newCustomer = CreateCustomer(); Console.WriteLine("New Customer:"); Console.WriteLine(newCustomer); session.Save(newCustomer); id = newCustomer.Id; tx.Commit(); } using(var session = sessionFactory.OpenSession()) using(var tx = session.BeginTransaction()) { var reloaded = session.Load<Customer>(id); Console.WriteLine("Reloaded:"); Console.WriteLine(reloaded); tx.Commit(); } Console.WriteLine("Press <ENTER> to exit..."); Console.ReadLine(); }
Вот полная реализация файла Program.cs .
using System; using System.Data; using System.Linq; using System.Reflection; using HibernatingRhinos.Profiler.Appender.NHibernate; using NHibernate.Cfg; using NHibernate.Dialect; using NHibernate.Driver; using NHibernate.Linq; namespace NHibernateDemo { internal class Program { private static void Main() { var cfg = ConfigureNHibernate(); var sessionFactory = cfg.BuildSessionFactory(); Guid id; using(var session = sessionFactory.OpenSession()) using(var tx = session.BeginTransaction()) { var newCustomer = CreateCustomer(); Console.WriteLine("New Customer:"); Console.WriteLine(newCustomer); session.Save(newCustomer); id = newCustomer.Id; tx.Commit(); } using(var session = sessionFactory.OpenSession()) using(var tx = session.BeginTransaction()) { var reloaded = session.Load<Customer>(id); Console.WriteLine("Reloaded:"); Console.WriteLine(reloaded); tx.Commit(); } Console.WriteLine("Press <ENTER> to exit..."); Console.ReadLine(); } private static Customer CreateCustomer() { var customer = new Customer { FirstName = "John", LastName = "Doe", Points = 100, HasGoldStatus = true, MemberSince = new DateTime(2012, 1, 1), CreditRating = CustomerCreditRating.Good, AverageRating = 42.42424242, Address = CreateLocation() }; var order1 = new Order { Ordered = DateTime.Now }; customer.AddOrder(order1); var order2 = new Order { Ordered = DateTime.Now.AddDays(-1), Shipped = DateTime.Now, ShipTo = CreateLocation() }; customer.AddOrder(order2); return customer; } private static Location CreateLocation() { return new Location { Street = "123 Somewhere Avenue", City = "Nowhere", Province = "Alberta", Country = "Canada" }; } private static Configuration ConfigureNHibernate() { NHibernateProfiler.Initialize(); var cfg = new Configuration(); cfg.DataBaseIntegration(x =&ht; { x.ConnectionStringName = "default"; x.Driver<SqlClientDriver>(); x.Dialect<MsSql2008Dialect>(); x.IsolationLevel = IsolationLevel.RepeatableRead; x.Timeout = 10; x.BatchSize = 10; }); cfg.SessionFactory().GenerateStatistics(); cfg.AddAssembly(Assembly.GetExecutingAssembly()); return cfg; } } }
Когда вы запустите это приложение, вы увидите следующий вывод.
New Customer: John Doe (00000000-0000-0000-0000-000000000000) Points: 100 HasGoldStatus: True MemberSince: 1/1/2012 12:00:00 AM (Unspecified) CreditRating: Good AverageRating: 42.42424242 Orders: Order Id: 00000000-0000-0000-0000-000000000000 Order Id: 00000000-0000-0000-0000-000000000000 Reloaded: John Doe (9b0fcf10-83f6-4f39-bda5-a5b800ede2ba) Points: 100 HasGoldStatus: True MemberSince: 1/1/2012 12:00:00 AM (Utc) CreditRating: Good AverageRating: 42.4242 Orders: Press <ENTER> to exit...
Как вы можете видеть, изначально у клиента было 2 заказа, но когда мы его перезагружаем, заказов не видно. Если вы посмотрите на файл customer.hbm.xml , то увидите, что мы не сопоставляем фактический сбор заказов. Так что NHibernate ничего не знает об этом. Давайте продолжим и добавим это.
<?xml version = "1.0" encoding = "utf-8" ?> <hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo" namespace = "NHibernateDemo"> <class name = "Customer"> <id name = "Id"> <generator class = "guid.comb"/> </id> <property name = "FirstName"/> <property name = "LastName"/> <property name = "AverageRating"/> <property name = "Points"/> <property name = "HasGoldStatus"/> <property name = "MemberSince" type = "UtcDateTime"/> <property name = "CreditRating" type = "CustomerCreditRatingType"/> <component name = "Address"> <property name = "Street"/> <property name = "City"/> <property name = "Province"/> <property name = "Country"/> </component> <set name = "Orders" table = "`Order`"> <key column = "CustomerId"/> <one-to-many class = "Order"/> </set> </class> </hibernate-mapping>
Это набор, и название этой коллекции — «Заказы», которые хранятся в таблице, называемой заказом. Нам нужно указать ключ, который является названием внешнего ключа или для поиска заказов. Эти заказы идентифицируются или принадлежат клиенту через идентификатор клиента. И затем я должен отметить, что это отношение один ко многим, и оно относится к классу заказов.
Нам также нужно немного изменить метод Main, сохранив новые заказы клиентов в базе данных, как показано в следующей программе.
private static void Main() { var cfg = ConfigureNHibernate(); var sessionFactory = cfg.BuildSessionFactory(); Guid id; using(var session = sessionFactory.OpenSession()) using(var tx = session.BeginTransaction()) { var newCustomer = CreateCustomer(); Console.WriteLine("New Customer:"); Console.WriteLine(newCustomer); session.Save(newCustomer); foreach (var order in newCustomer.Orders) { session.Save(order); } id = newCustomer.Id; tx.Commit(); } using(var session = sessionFactory.OpenSession()) using(var tx = session.BeginTransaction()) { var reloaded = session.Load<Customer>(id); Console.WriteLine("The orders were ordered by: "); foreach (var order in reloaded.Orders) { Console.WriteLine(order.Customer); } tx.Commit(); } Console.WriteLine("Press <ENTER> to exit..."); Console.ReadLine(); }
Мы также указали, какой клиент заказал этот конкретный продукт. Таким образом, нам нужно создать отношения «многие к одному», чтобы вернуть этот заказ этому клиенту.
Итак, давайте перейдем в файл Order.hbm.xml и добавим «многие к одному», а затем назовем поле клиента и столбец с идентификатором клиента.
<?xml version = "1.0" encoding = "utf-8" ?> <hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo" namespace = "NHibernateDemo"> <class name = "Order" table = "`Order`"> <id name = "Id"> <generator class = "guid.comb"/> </id> <property name = "Ordered"/> <property name = "Shipped"/> <component name = "ShipTo"> <property name = "Street"/> <property name = "City"/> <property name = "Province"/> <property name = "Country"/> </component> <many-to-one name = "Customer" column = "CustomerId"/> </class> </hibernate-mapping>
Давайте снова запустим это приложение, и теперь вы увидите следующий вывод.
New Customer: John Doe (00000000-0000-0000-0000-000000000000) Points: 100 HasGoldStatus: True MemberSince: 1/1/2012 12:00:00 AM (Unspecified) CreditRating: Good AverageRating: 42.42424242 Orders: Order Id: 00000000-0000-0000-0000-000000000000 Order Id: 00000000-0000-0000-0000-000000000000 Reloaded: John Doe (660a6f29-650e-4380-99e0-a5b800febbde) Points: 100 HasGoldStatus: True MemberSince: 1/1/2012 12:00:00 AM (Utc) CreditRating: Good AverageRating: 42.4242 Orders: Order Id: 57314deb-e023-4e55-ac1e-a5b800febbe3 Order Id: fc065683-d5f5-484b-ae42-a5b800febbe3 The orders were ordered by: John Doe (660a6f29-650e-4380-99e0-a5b800febbde) Points: 100 HasGoldStatus: True MemberSince: 1/1/2012 12:00:00 AM (Utc) CreditRating: Good AverageRating: 42.4242 Orders: Order Id: 57314deb-e023-4e55-ac1e-a5b800febbe3 Order Id: fc065683-d5f5-484b-ae42-a5b800febbe3 John Doe (660a6f29-650e-4380-99e0-a5b800febbde) Points: 100 HasGoldStatus: True MemberSince: 1/1/2012 12:00:00 AM (Utc) CreditRating: Good AverageRating: 42.4242 Orders: Order Id: 57314deb-e023-4e55-ac1e-a5b800febbe3 Order Id: fc065683-d5f5-484b-ae42-a5b800febbe3 Press <ENTER> to exit...
NHibernate — Отображение коллекции
В этой главе мы рассмотрим, как представлять коллекции. Существуют различные типы коллекций, которые мы можем использовать в NHibernate, такие как —
- Списки
- наборы
- мешки
Теперь, с точки зрения .NET, мы обычно имеем дело со списками или как с очень простыми структурами данных, списками, словарями. .NET не имеет большого разнообразия типов коллекций. Так зачем NHibernate все эти разные типы? Это действительно возвращается в базу данных.
Список
-
Список — это упорядоченная коллекция элементов, которые не обязательно являются уникальными.
-
Мы можем отобразить это, используя IList <T> .
-
Таким образом, хотя у нас может быть обычный список адресов, и с точки зрения приложения мы знаем, что элементы уникальны, ничто в списке не мешает нам вставлять повторяющиеся элементы в этот список.
Список — это упорядоченная коллекция элементов, которые не обязательно являются уникальными.
Мы можем отобразить это, используя IList <T> .
Таким образом, хотя у нас может быть обычный список адресов, и с точки зрения приложения мы знаем, что элементы уникальны, ничто в списке не мешает нам вставлять повторяющиеся элементы в этот список.
Задавать
-
Набор — это неупорядоченная коллекция уникальных элементов. Если вы попытаетесь вставить 2 повторяющихся элемента в набор, это вызовет исключение.
-
В NHibernate нет ничего конкретного.
-
Это просто удобный способ иметь реализацию общего набора. Если вы используете .NET 4, вы можете использовать новый HashSet <T> для их представления, но в большинстве приложений NHibernate мы представляем, что это ISet.
-
Это неупорядоченный, если вы извлекаете список адресов из базы данных или список заказов, вы не знаете, в каком порядке они поступают, если вы не добавите конкретный пункт Order by.
-
В общем, данные, которые вы извлекаете из базы данных, являются наборами.
-
Это уникальные коллекции неупорядоченных элементов.
Набор — это неупорядоченная коллекция уникальных элементов. Если вы попытаетесь вставить 2 повторяющихся элемента в набор, это вызовет исключение.
В NHibernate нет ничего конкретного.
Это просто удобный способ иметь реализацию общего набора. Если вы используете .NET 4, вы можете использовать новый HashSet <T> для их представления, но в большинстве приложений NHibernate мы представляем, что это ISet.
Это неупорядоченный, если вы извлекаете список адресов из базы данных или список заказов, вы не знаете, в каком порядке они поступают, если вы не добавите конкретный пункт Order by.
В общем, данные, которые вы извлекаете из базы данных, являются наборами.
Это уникальные коллекции неупорядоченных элементов.
Мешок
-
Еще одна распространенная коллекция, которую мы увидим в мире баз данных, — это сумка, которая похожа на набор, за исключением того, что в ней могут быть повторяющиеся элементы.
-
В мире .NET мы представляем это как IList.
Еще одна распространенная коллекция, которую мы увидим в мире баз данных, — это сумка, которая похожа на набор, за исключением того, что в ней могут быть повторяющиеся элементы.
В мире .NET мы представляем это как IList.
Наборы, вероятно, самые распространенные, но вы также увидите списки и пакеты в зависимости от вашего приложения. Давайте посмотрим на файл customer.hbm.xml, приведенный ниже, из последней главы, в которой определены заказы Set.
<?xml version = "1.0" encoding = "utf-8" ?> <hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo" namespace = "NHibernateDemo"> <class name = "Customer"> <id name = "Id"> <generator class = "guid.comb"/> </id> <property name = "FirstName"/> <property name = "LastName"/> <property name = "AverageRating"/> <property name = "Points"/> <property name = "HasGoldStatus"/> <property name = "MemberSince" type = "UtcDateTime"/> <property name = "CreditRating" type = "CustomerCreditRatingType"/> <component name = "Address"> <property name = "Street"/> <property name = "City"/> <property name = "Province"/> <property name = "Country"/> </component> <set name = "Orders" table = "`Order`"> <key column = "CustomerId"/> <one-to-many class = "Order"/> </set> </class> </hibernate-mapping>
Как видите, мы сопоставили коллекцию заказов как набор. Помните, что набор — это неупорядоченная коллекция уникальных элементов.
Теперь, если вы посмотрите на класс Customer, вы увидите, что свойство Orders определяется ISet, как показано в следующей программе.
public virtual ISet<Order> Orders { get; set; }
Теперь, когда это приложение запущено, вы увидите следующий вывод.
New Customer: John Doe (00000000-0000-0000-0000-000000000000) Points: 100 HasGoldStatus: True MemberSince: 1/1/2012 12:00:00 AM (Unspecified) CreditRating: Good AverageRating: 42.42424242 Orders: Order Id: 00000000-0000-0000-0000-000000000000 Order Id: 00000000-0000-0000-0000-000000000000 Reloaded: John Doe (1f248133-b50a-4ad7-9915-a5b8017d0ff1) Points: 100 HasGoldStatus: True MemberSince: 1/1/2012 12:00:00 AM (Utc) CreditRating: Good AverageRating: 42.4242 Orders: Order Id: c41af8f2-7124-42a7-91c5-a5b8017d0ff6 Order Id: 657f6bb0-1f42-45fc-8fc7-a5b8017d0ff7 The orders were ordered by: John Doe (1f248133-b50a-4ad7-9915-a5b8017d0ff1) Points: 100 HasGoldStatus: True MemberSince: 1/1/2012 12:00:00 AM (Utc) CreditRating: Good AverageRating: 42.4242 Orders: Order Id: c41af8f2-7124-42a7-91c5-a5b8017d0ff6 Order Id: 657f6bb0-1f42-45fc-8fc7-a5b8017d0ff7 John Doe (1f248133-b50a-4ad7-9915-a5b8017d0ff1) Points: 100 HasGoldStatus: True MemberSince: 1/1/2012 12:00:00 AM (Utc) CreditRating: Good AverageRating: 42.4242 Orders: Order Id: c41af8f2-7124-42a7-91c5-a5b8017d0ff6 Order Id: 657f6bb0-1f42-45fc-8fc7-a5b8017d0ff7 Press <ENTER> to exit...
Если элементы в коллекции не должны быть уникальными, если у вас может быть несколько заказов с одним и тем же первичным ключом, встречающимся несколько раз в этой коллекции, тогда это будет лучше сопоставлено как пакет, как показано в следующей программе.
<bag name = "Orders" table = "`Order`"> <key column = "CustomerId"/> <one-to-many class = "Order"/> </bag>
Теперь, если вы запустите это приложение, вы получите исключение, потому что если мы посмотрим на класс клиента, вы заметите, что заказы помечены как ISet в коде C #.
Таким образом, нам также нужно будет изменить это значение на IList, а затем нам нужно будет перейти от HashSet к списку в конструкторе.
public class Customer { public Customer() { MemberSince = DateTime.UtcNow; Orders = new List<Order>(); } public virtual Guid Id { get; set; } public virtual string FirstName { get; set; } public virtual string LastName { get; set; } public virtual double AverageRating { get; set; } public virtual int Points { get; set; } public virtual bool HasGoldStatus { get; set; } public virtual DateTime MemberSince { get; set; } public virtual CustomerCreditRating CreditRating { get; set; } public virtual Location Address { get; set; } public virtual IList<Order> Orders { get; set; } public virtual void AddOrder(Order order) { Orders.Add(order); order.Customer = this; } public override string ToString() { var result = new StringBuilder(); result.AppendFormat("{1} {2} ({0})\r\n\tPoints: {3}\r\n\tHasGoldStatus: {4}\r\n\tMemberSince: {5} ({7})\r\n\tCreditRating: {6}\r\n\tAverageRating: {8}\r\n", Id, FirstName, LastName, Points, HasGoldStatus, MemberSince, CreditRating, MemberSince.Kind, AverageRating); result.AppendLine("\tOrders:"); foreach(var order in Orders) { result.AppendLine("\t\t" + order); } return result.ToString(); } }
Когда вы запустите приложение, вы увидите то же поведение. Но теперь у нас может быть порядок, встречающийся несколько раз в одной коллекции.
John Doe (00000000-0000-0000-0000-000000000000) Points: 100 HasGoldStatus: True MemberSince: 1/1/2012 12:00:00 AM (Unspecified) CreditRating: Good AverageRating: 42.42424242 Orders: Order Id: 00000000-0000-0000-0000-000000000000 Order Id: 00000000-0000-0000-0000-000000000000 Reloaded: John Doe (fbde48f5-d620-4d1c-9a7f-a5b8017c3280) Points: 100 HasGoldStatus: True MemberSince: 1/1/2012 12:00:00 AM (Utc) CreditRating: Good AverageRating: 42.4242 Orders: Order Id: 6dd7dbdb-354f-4c82-9c39-a5b8017c3286 Order Id: 9b3e2441-a81b-404d-9aed-a5b8017c3287 The orders were ordered by: John Doe (fbde48f5-d620-4d1c-9a7f-a5b8017c3280) Points: 100 HasGoldStatus: True MemberSince: 1/1/2012 12:00:00 AM (Utc) CreditRating: Good AverageRating: 42.4242 Orders: Order Id: 6dd7dbdb-354f-4c82-9c39-a5b8017c3286 Order Id: 9b3e2441-a81b-404d-9aed-a5b8017c3287 John Doe (fbde48f5-d620-4d1c-9a7f-a5b8017c3280) Points: 100 HasGoldStatus: True MemberSince: 1/1/2012 12:00:00 AM (Utc) CreditRating: Good AverageRating: 42.4242 Orders: Order Id: 6dd7dbdb-354f-4c82-9c39-a5b8017c3286 Order Id: 9b3e2441-a81b-404d-9aed-a5b8017c3287 Press <ENTER> to exit...
NHibernate — Каскады
В этой главе мы расскажем, как использовать функцию Cascade. Если у вас есть набор или коллекция предметов или отношения между двумя классами, такими как наш клиент и заказ, и у вас есть связь с внешним ключом. Если мы удаляем клиента по умолчанию, NHibernate ничего не делает с дочерними объектами, поэтому те, которые принадлежат этому клиенту, и мы могли бы быть заказами-сиротами.
-
Мы также могли бы нарушать ограничения внешнего ключа, поэтому мы можем использовать понятие каскадов.
-
По умолчанию NHibernate не связывает операции с дочерними объектами.
-
Причина этого заключается в том, что у вас могут быть такие отношения, как клиент, имеющий адрес доставки по умолчанию, и этот адрес доставки используется многими разными клиентами.
-
Таким образом, вы не хотели бы каскадно связывать эти отношения, потому что другие клиенты все еще ссылаются на них.
-
Таким образом, вся идея каскадов состоит в том, чтобы рассказать NHibernate, как обращаться с дочерними объектами.
Мы также могли бы нарушать ограничения внешнего ключа, поэтому мы можем использовать понятие каскадов.
По умолчанию NHibernate не связывает операции с дочерними объектами.
Причина этого заключается в том, что у вас могут быть такие отношения, как клиент, имеющий адрес доставки по умолчанию, и этот адрес доставки используется многими разными клиентами.
Таким образом, вы не хотели бы каскадно связывать эти отношения, потому что другие клиенты все еще ссылаются на них.
Таким образом, вся идея каскадов состоит в том, чтобы рассказать NHibernate, как обращаться с дочерними объектами.
Существуют разные варианты каскадирования, а именно:
-
none — это значение по умолчанию и это означает отсутствие каскадирования.
-
все, что собирается каскадом, сохраняет, обновляет и удаляет.
-
save-update — это будет каскад, сохранения и обновления.
-
удалить — это будет каскадное удаление.
-
all-delete-orphan — это специальный файл, который довольно часто используется и такой же, как All Except, если он находит строки Delete-orphan, он также удаляет их.
none — это значение по умолчанию и это означает отсутствие каскадирования.
все, что собирается каскадом, сохраняет, обновляет и удаляет.
save-update — это будет каскад, сохранения и обновления.
удалить — это будет каскадное удаление.
all-delete-orphan — это специальный файл, который довольно часто используется и такой же, как All Except, если он находит строки Delete-orphan, он также удаляет их.
Вы можете указать значение по умолчанию в своем файле hbm.xml , чтобы вы могли предоставить каскад по умолчанию для этого элемента отображения Hibernate или вы также можете указать его для определенных коллекций и отношений, таких как многие-к-одному.
Давайте рассмотрим простые примеры каскадов, давайте исправим проблему в программе, где мы должны вручную каскадировать сохранение заказов, как показано в следующем коде.
using(var session = sessionFactory.OpenSession()) using(var tx = session.BeginTransaction()) { var newCustomer = CreateCustomer(); Console.WriteLine("New Customer:"); Console.WriteLine(newCustomer); session.Save(newCustomer); foreach (var order in newCustomer.Orders) { session.Save(order); } id = newCustomer.Id; tx.Commit(); }
В приведенном фрагменте кода видно, что мы вручную сохраняем все заказы для клиента. Теперь давайте удалим ручной каскадный код, в котором сохранены все заказы.
using(var session = sessionFactory.OpenSession()) using(var tx = session.BeginTransaction()) { var newCustomer = CreateCustomer(); Console.WriteLine("New Customer:"); Console.WriteLine(newCustomer); session.Save(newCustomer); id = newCustomer.Id; tx.Commit(); }
Нам нужно указать каскадную опцию в customer.hbm.xml .
<?xml version = "1.0" encoding = "utf-8" ?> <hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo" namespace = "NHibernateDemo"> <class name = "Customer"> <id name = "Id"> <generator class = "guid.comb"/> </id> <property name = "FirstName"/> <property name = "LastName"/> <property name = "AverageRating"/> <property name = "Points"/> <property name = "HasGoldStatus"/> <property name = "MemberSince" type = "UtcDateTime"/> <property name = "CreditRating" type = "CustomerCreditRatingType"/> <component name = "Address"> <property name = "Street"/> <property name = "City"/> <property name = "Province"/> <property name = "Country"/> </component> <set name = "Orders" table = "`Order`" cascade = "all-delete-orphan"> <key column = "CustomerId"/> <one-to-many class = "Order"/> </set> </class> </hibernate-mapping>
-
Теперь заказы полностью принадлежат заказчику. Таким образом, если клиенты были удалены из базы данных, наше приложение хотело бы удалить все эти заказы, включая любые, которые могли быть осиротевшими.
-
Это в конечном итоге делает удаление. При этом будет указано удаление из таблицы заказов, где идентификатор клиента равен клиенту, которого вы удаляете.
-
Таким образом, вы можете каскадировать эти удаления. Так что с All , он будет сохранять, обновлять и удалять.
Теперь заказы полностью принадлежат заказчику. Таким образом, если клиенты были удалены из базы данных, наше приложение хотело бы удалить все эти заказы, включая любые, которые могли быть осиротевшими.
Это в конечном итоге делает удаление. При этом будет указано удаление из таблицы заказов, где идентификатор клиента равен клиенту, которого вы удаляете.
Таким образом, вы можете каскадировать эти удаления. Так что с All , он будет сохранять, обновлять и удалять.
Теперь, когда вы запустите это приложение, вы увидите следующий вывод.
New Customer: John Doe (00000000-0000-0000-0000-000000000000) Points: 100 HasGoldStatus: True MemberSince: 1/1/2012 12:00:00 AM (Unspecified) CreditRating: Good AverageRating: 42.42424242 Orders: Order Id: 00000000-0000-0000-0000-000000000000 Order Id: 00000000-0000-0000-0000-000000000000 Reloaded: John Doe (10b2a3d7-7fcf-483c-b1da-a5bb00b8512e) Points: 100 HasGoldStatus: True MemberSince: 1/1/2012 12:00:00 AM (Utc) CreditRating: Good AverageRating: 42.4242 Orders: Order Id: e6680e30-5b3b-4efa-b017-a5bb00b85133 Order Id: b03858e7-8c36-4555-8878-a5bb00b85134 The orders were ordered by: John Doe (10b2a3d7-7fcf-483c-b1da-a5bb00b8512e) Points: 100 HasGoldStatus: True MemberSince: 1/1/2012 12:00:00 AM (Utc) CreditRating: Good AverageRating: 42.4242 Orders: Order Id: e6680e30-5b3b-4efa-b017-a5bb00b85133 Order Id: b03858e7-8c36-4555-8878-a5bb00b85134 John Doe (10b2a3d7-7fcf-483c-b1da-a5bb00b8512e) Points: 100 HasGoldStatus: True MemberSince: 1/1/2012 12:00:00 AM (Utc) CreditRating: Good AverageRating: 42.4242 Orders: Order Id: e6680e30-5b3b-4efa-b017-a5bb00b85133 Order Id: b03858e7-8c36-4555-8878-a5bb00b85134 Press <ENTER> to exit...
Как вы можете видеть, мы удалили код из программы, которая вручную каскадировалась, и наше приложение все еще работает.
Поэтому, в зависимости от ваших отношений, вы можете каскадировать их. Теперь давайте посмотрим на другие каскадные отношения. Давайте перейдем к файлу Order.hbm.xml, и мы сможем связать это соотношение многие-к-одному.
<?xml version = "1.0" encoding = "utf-8" ?> <hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo" namespace = "NHibernateDemo"> <class name = "Order" table = "`Order`"> <id name = "Id"> <generator class = "guid.comb"/> </id> <property name = "Ordered"/> <property name = "Shipped"/> <component name = "ShipTo"> <property name = "Street"/> <property name = "City"/> <property name = "Province"/> <property name = "Country"/> </component> <many-to-one name = "Customer" column = "CustomerId" cascade = "save-update"/> </class> </hibernate-mapping>
Поэтому, если мы создаем новый заказ, к нему присоединяется новый клиент и мы говорим: сохраните этот заказ, возможно, мы захотим это сделать каскадно. Но одна вещь, которую мы, вероятно, не хотели бы делать, — это удаление заказа для удаления соответствующего клиента.
Итак, здесь мы хотели бы сделать обновление для сохранения, поэтому с помощью сохранения-обновления оно будет каскадно сохранять или сохранять любые обновления для этого клиента. Так что, если мы получим нового клиента или поменяем клиента, это будет каскадным. Если это удаление, оно не удалит это из базы данных.
Итак, запустив наше приложение снова, все по-прежнему работает, как и ожидалось.
New Customer: John Doe (00000000-0000-0000-0000-000000000000) Points: 100 HasGoldStatus: True MemberSince: 1/1/2012 12:00:00 AM (Unspecified) CreditRating: Good AverageRating: 42.42424242 Orders: Id: 00000000-0000-0000-0000-000000000000 Order Id: 00000000-0000-0000-0000-000000000000 Reloaded: John Doe (10b2a3d7-7fcf-483c-b1da-a5bb00b8512e) Points: 100 HasGoldStatus: True MemberSince: 1/1/2012 12:00:00 AM (Utc) CreditRating: Good AverageRating: 42.4242 Orders: Order Id: e6680e30-5b3b-4efa-b017-a5bb00b85133 Order Id: b03858e7-8c36-4555-8878-a5bb00b85134 The orders were ordered by: John Doe (10b2a3d7-7fcf-483c-b1da-a5bb00b8512e) Points: 100 HasGoldStatus: True MemberSince: 1/1/2012 12:00:00 AM (Utc) CreditRating: Good AverageRating: 42.4242 Orders: Order Id: e6680e30-5b3b-4efa-b017-a5bb00b85133 Order Id: b03858e7-8c36-4555-8878-a5bb00b85134 John Doe (10b2a3d7-7fcf-483c-b1da-a5bb00b8512e) Points: 100 HasGoldStatus: True MemberSince: 1/1/2012 12:00:00 AM (Utc) CreditRating: Good AverageRating: 42.4242 Orders: Order Id: e6680e30-5b3b-4efa-b017-a5bb00b85133 Order Id: b03858e7-8c36-4555-8878-a5bb00b85134 Press <ENTER> to exit...
Теперь вы должны взглянуть на свое приложение, помните, что по умолчанию установлено значение «Нет», и вам нужно подумать о ваших сущностях и ваших отношениях между ними, чтобы определить соответствующие каскады для каждой из ваших сущностей, а также для каждого из ваших отношений в этой базе данных.
NHibernate — Ленивая Загрузка
В этой главе мы рассмотрим функцию отложенной загрузки. По умолчанию это совершенно другая концепция, и NHibernate не требует отложенной загрузки, например, если вы загружаете клиента, он не собирается загружать все заказы.
-
Коллекция заказов будет загружена по запросу.
-
Любая ассоциация, будь то много-к-одному или коллекция, загружается по умолчанию лениво, требует Open ISession .
-
Если вы закрыли сеанс или зафиксировали транзакцию, вы можете получить исключение для отложенной загрузки, которое не сможет добавить эти дополнительные объекты.
-
Вы должны быть осторожны с отложенной загрузкой и объемом данных, которые вам действительно нужны.
-
Вы можете отключить ленивую загрузку для всей ассоциации, или вы можете поставить ленивый равным false, или вы также можете указать стратегию выборки.
Коллекция заказов будет загружена по запросу.
Любая ассоциация, будь то много-к-одному или коллекция, загружается по умолчанию лениво, требует Open ISession .
Если вы закрыли сеанс или зафиксировали транзакцию, вы можете получить исключение для отложенной загрузки, которое не сможет добавить эти дополнительные объекты.
Вы должны быть осторожны с отложенной загрузкой и объемом данных, которые вам действительно нужны.
Вы можете отключить ленивую загрузку для всей ассоциации, или вы можете поставить ленивый равным false, или вы также можете указать стратегию выборки.
Вот реализация файла Program.cs .
using System; using System.Data; using System.Linq; using System.Reflection; using HibernatingRhinos.Profiler.Appender.NHibernate; using NHibernate.Cfg; using NHibernate.Dialect; using NHibernate.Driver; using NHibernate.Linq; namespace NHibernateDemo { internal class Program { private static void Main() { var cfg = ConfigureNHibernate(); var sessionFactory = cfg.BuildSessionFactory(); Guid id; using(var session = sessionFactory.OpenSession()) using(var tx = session.BeginTransaction()) { var newCustomer = CreateCustomer(); Console.WriteLine("New Customer:"); Console.WriteLine(newCustomer); session.Save(newCustomer); id = newCustomer.Id; tx.Commit(); } using(var session = sessionFactory.OpenSession()) using(var tx = session.BeginTransaction()) { var reloaded = session.Load<Customer>(id); Console.WriteLine("Reloaded:"); Console.WriteLine(reloaded); Console.WriteLine("The orders were ordered by: "); foreach (var order in reloaded.Orders) { Console.WriteLine(order.Customer); } tx.Commit(); } Console.WriteLine("Press <ENTER> to exit..."); Console.ReadLine(); } private static Customer CreateCustomer() { var customer = new Customer { FirstName = "John", LastName = "Doe", Points =100, HasGoldStatus = true, MemberSince = new DateTime(2012, 1, 1), CreditRating = CustomerCreditRating.Good, AverageRating = 42.42424242, Address = CreateLocation() }; var order1 = new Order { Ordered = DateTime.Now }; customer.AddOrder(order1); var order2 = new Order { Ordered = DateTime.Now.AddDays(-1), Shipped = DateTime.Now, ShipTo = CreateLocation() }; customer.AddOrder(order2); return customer; } private static Location CreateLocation() { return new Location { Street = "123 Somewhere Avenue", City = "Nowhere", Province = "Alberta", Country = "Canada" }; } private static Configuration ConfigureNHibernate() { NHibernateProfiler.Initialize(); var cfg = new Configuration(); cfg.DataBaseIntegration(x => { x.ConnectionStringName = "default"; x.Driver<SqlClientDriver>(); x.Dialect<MsSql2008Dialect<(); x.IsolationLevel = IsolationLevel.RepeatableRead; x.Timeout = 10; x.BatchSize = 10; }); cfg.SessionFactory().GenerateStatistics(); cfg.AddAssembly(Assembly.GetExecutingAssembly()); return cfg; } } }
Чтобы понять это, давайте запустим приложение и взглянем на NHibernate Profiler.
Как вы можете видеть, у нас есть «Выбор из клиента» с заданным идентификатором клиента, а затем у нас есть еще одна таблица «Выбор из заказов», когда она фактически обращается к коллекции этого клиента.
Итак, у нас есть 2 обращения к базе данных. Теперь иногда нам хотелось бы оптимизировать это. Для этого перейдем к файлу customer.hbm.xml, добавим стратегию выборки и попросим его выполнить выборку соединения.
<?xml version = "1.0" encoding = "utf-8" ?> <hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo" namespace = "NHibernateDemo"> <class name = "Customer"> <id name = "Id"> <generator class = "guid.comb"/> </id> <property name = "FirstName"/> <property name = "LastName"/> <property name = "AverageRating"/> <property name = "Points"/> <property name = "HasGoldStatus"/> <property name = "MemberSince" type = "UtcDateTime"/> <property name = "CreditRating" type = "CustomerCreditRatingType"/> <component name = "Address"> <property name = "Street"/> <property name = "City"/> <property name = "Province"/> <property name = "Country"/> </component> <set name = "Orders" table = "`Order`" cascade = "all-delete-orphan" fetch = "join"> <key column = "CustomerId"/> <one-to-many class = "Order"/> </set> </class> </hibernate-mapping>
Как видите, мы не изменили ни одного кода в нашем приложении, мы просто добавили стратегию извлечения в customer.hbm.xml . Давайте снова запустим это приложение, оно по-прежнему ведет себя точно так же. Давайте посмотрим на NHibernate Profiler.
-
Раньше у программы было два обхода базы данных, теперь у нее есть только одна, и это потому, что здесь выполняется левое внешнее соединение.
-
Мы видим, что он выполняет левое внешнее соединение между таблицей клиентов и таблицей заказов на основе идентификатора клиента и, следовательно, может загружать всю эту информацию одновременно.
-
Мы сохранили 1 поездку туда и обратно в базу данных.
-
Недостатком является то, что информация о клиенте будет дублироваться в обеих строках, и так будет работать левое внешнее соединение SQL.
-
Таким образом, с помощью стратегии извлечения мы собираем немного больше данных и сохраняем туда-обратно.
Раньше у программы было два обхода базы данных, теперь у нее есть только одна, и это потому, что здесь выполняется левое внешнее соединение.
Мы видим, что он выполняет левое внешнее соединение между таблицей клиентов и таблицей заказов на основе идентификатора клиента и, следовательно, может загружать всю эту информацию одновременно.
Мы сохранили 1 поездку туда и обратно в базу данных.
Недостатком является то, что информация о клиенте будет дублироваться в обеих строках, и так будет работать левое внешнее соединение SQL.
Таким образом, с помощью стратегии извлечения мы собираем немного больше данных и сохраняем туда-обратно.
Вы также можете сделать это на уровне запроса, поэтому давайте перейдем к файлу Program.cs и рассмотрим более простой перезагруженный пример.
using(var session = sessionFactory.OpenSession()) using(var tx = session.BeginTransaction()) { //var query = from customer in session.Query<Customer>() // select customer; //var reloaded = query.Fetch(x => x.Orders).ToList(); var reloaded = session.Load<Customer>(id); Console.WriteLine("Reloaded:"); Console.WriteLine(reloaded); Console.WriteLine("The orders were ordered by: "); foreach (var order in reloaded.Orders) { Console.WriteLine(order.Customer); } tx.Commit(); }
Здесь мы делаем нагрузку на клиента. Теперь давайте изменим его на запрос, и мы будем использовать запрос ссылки, как показано в следующем коде.
using(var session = sessionFactory.OpenSession()) using(var tx = session.BeginTransaction()) { var query = from customer in session.Query<Customer>() where customer.Id == id select customer; var reloaded = query.Fetch(x => x.Orders).ToList().First(); Console.WriteLine("Reloaded:"); Console.WriteLine(reloaded); tx.Commit(); }
Давайте также удалим стратегию выборки из файла customer.hbm.xml .
<?xml version = "1.0" encoding = "utf-8" ?> <hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo" namespace = "NHibernateDemo"> <class name = "Customer"> <id name = "Id"> <generator class = "guid.comb"/> </id> <property name = "FirstName"/> <property name = "LastName"/> <property name = "AverageRating"/> <property name = "Points"/> <property name = "HasGoldStatus"/> <property name = "MemberSince" type = "UtcDateTime"/> <property name = "CreditRating" type = "CustomerCreditRatingType"/> <component name = "Address"> <property name = "Street"/> <property name = "City"/> <property name = "Province"/> <property name = "Country"/> </component> <set name = "Orders" table = "`Order`" cascade = "all-delete-orphan"> <key column = "CustomerId"/> <one-to-many class = "Order"/> </set> </class> </hibernate-mapping>
Давайте снова запустим это приложение, и вы увидите следующий результат.
New Customer: John Doe (00000000-0000-0000-0000-000000000000) Points: 100 HasGoldStatus: True MemberSince: 1/1/2012 12:00:00 AM (Unspecified) CreditRating: Good AverageRating: 42.42424242 Orders: Order Id: 00000000-0000-0000-0000-000000000000 Order Id: 00000000-0000-0000-0000-000000000000 Reloaded: John Doe (6ebacd17-f9ba-4ad8-9817-a5bb01112a5a) Points: 100 HasGoldStatus: True MemberSince: 1/1/2012 12:00:00 AM (Utc) CreditRating: Good AverageRating: 42.4242 Orders: Order Id: 16a6596b-d56e-41c7-9681-a5bb01112a60 Order Id: d41d615b-0f21-4032-81db-a5bb01112a61 Press <ENTER> to exit...
Теперь давайте посмотрим на NHibernate Profiler, вы можете видеть, что у нас снова происходит эта нетерпеливая выборка соединения, но на этот раз она основана на запросе.
NHibernate — обратные отношения
В этой главе мы рассмотрим еще одну особенность — Обратные отношения. Это забавная опция, которую вы увидите в коллекции, которая обратно равна true, и это также сбивает с толку многих разработчиков. Итак, давайте поговорим об этом варианте. Чтобы понять это, вам действительно нужно подумать о реляционной модели. Допустим, у вас есть двунаправленные ассоциации, использующие один внешний ключ.
-
С точки зрения отношений у вас есть один внешний ключ, и он представляет собой как заказчика, так и заказы заказчику.
-
От модели ОО, у вас есть однонаправленные ассоциации, использующие эти ссылки.
-
Ничто не говорит о том, что две однонаправленные ассоциации представляют одну и ту же двунаправленную ассоциацию в базе данных.
-
Проблема в том, что NHibernate не имеет достаточно информации, чтобы знать, что customer.orders и order.customer представляют одинаковые отношения в базе данных.
-
Нам нужно предоставить нам обратное значение true в качестве подсказки, поскольку однонаправленные ассоциации используют одни и те же данные.
-
Если мы попытаемся сохранить эти отношения, которые имеют 2 ссылки на них, NHibernate попытается обновить эту ссылку дважды.
-
На самом деле он сделает дополнительный обход к базе данных, а также будет иметь два обновления этого внешнего ключа.
-
Обратное значение true указывает NHibernate, какую сторону отношений игнорировать.
-
Когда вы применяете его на стороне коллекции, NHibernate всегда обновляет внешний ключ с другой стороны, со стороны дочернего объекта.
-
Тогда у нас есть только одно обновление для этого внешнего ключа, и у нас нет дополнительных обновлений для этих данных.
-
Это позволяет нам предотвращать дублирование обновлений внешнего ключа, а также предотвращает нарушения внешнего ключа.
С точки зрения отношений у вас есть один внешний ключ, и он представляет собой как заказчика, так и заказы заказчику.
От модели ОО, у вас есть однонаправленные ассоциации, использующие эти ссылки.
Ничто не говорит о том, что две однонаправленные ассоциации представляют одну и ту же двунаправленную ассоциацию в базе данных.
Проблема в том, что NHibernate не имеет достаточно информации, чтобы знать, что customer.orders и order.customer представляют одинаковые отношения в базе данных.
Нам нужно предоставить нам обратное значение true в качестве подсказки, поскольку однонаправленные ассоциации используют одни и те же данные.
Если мы попытаемся сохранить эти отношения, которые имеют 2 ссылки на них, NHibernate попытается обновить эту ссылку дважды.
На самом деле он сделает дополнительный обход к базе данных, а также будет иметь два обновления этого внешнего ключа.
Обратное значение true указывает NHibernate, какую сторону отношений игнорировать.
Когда вы применяете его на стороне коллекции, NHibernate всегда обновляет внешний ключ с другой стороны, со стороны дочернего объекта.
Тогда у нас есть только одно обновление для этого внешнего ключа, и у нас нет дополнительных обновлений для этих данных.
Это позволяет нам предотвращать дублирование обновлений внешнего ключа, а также предотвращает нарушения внешнего ключа.
Давайте посмотрим на файл customer.cs, в котором вы увидите метод AddOrder, и идея заключается в том, что теперь у нас есть этот обратный указатель от заказа к клиенту, и его нужно установить. Поэтому, когда заказ добавляется к клиенту, указатель возврата этого клиента устанавливается, в противном случае он будет нулевым, поэтому нам нужно это, чтобы правильно связать это вместе в графе объектов.
using System; using System.Text; using Iesi.Collections.Generic; namespace NHibernateDemo { public class Customer { public Customer() { MemberSince = DateTime.UtcNow; Orders = new HashedSet<Order>(); } public virtual Guid Id { get; set; } public virtual string FirstName { get; set; } public virtual string LastName { get; set; } public virtual double AverageRating { get; set; } public virtual int Points { get; set; } public virtual bool HasGoldStatus { get; set; } public virtual DateTime MemberSince { get; set; } public virtual CustomerCreditRating CreditRating { get; set; } public virtual Location Address { get; set; } public virtual ISet<Order> Orders { get; set; } public virtual void AddOrder(Order order) { Orders.Add(order); order.Customer = this; } public override string ToString() { var result = new StringBuilder(); result.AppendFormat("{1} {2} ({0})\r\n\tPoints: {3}\r\n\tHasGoldStatus: {4}\r\n\tMemberSince: {5} ({7})\r\n\tCreditRating: {6}\r\n\tAverageRating: {8}\r\n", Id, FirstName, LastName, Points, HasGoldStatus, MemberSince, CreditRating, MemberSince.Kind, AverageRating); result.AppendLine("\tOrders:"); foreach(var order in Orders) { result.AppendLine("\t\t" + order); } return result.ToString(); } } public class Location { public virtual string Street { get; set; } public virtual string City { get; set; } public virtual string Province { get; set; } public virtual string Country { get; set; } } public enum CustomerCreditRating { Excellent, VeryVeryGood, VeryGood, Good, Neutral, Poor, Terrible } }
Вот реализация файла Program.cs .
using System; using System.Data; using System.Linq; using System.Reflection; using HibernatingRhinos.Profiler.Appender.NHibernate; using NHibernate.Cfg; using NHibernate.Dialect; using NHibernate.Driver; using NHibernate.Linq; namespace NHibernateDemo { internal class Program { private static void Main() { var cfg = ConfigureNHibernate(); var sessionFactory = cfg.BuildSessionFactory(); Guid id; using(var session = sessionFactory.OpenSession()) using(var tx = session.BeginTransaction()) { var newCustomer = CreateCustomer(); Console.WriteLine("New Customer:"); Console.WriteLine(newCustomer); session.Save(newCustomer); id = newCustomer.Id; tx.Commit(); } using(var session = sessionFactory.OpenSession()) using(var tx = session.BeginTransaction()) { var query = from customer in session.Query<Customer>() where customer.Id == id select customer; var reloaded = query.Fetch(x => x.Orders).ToList().First(); Console.WriteLine("Reloaded:"); Console.WriteLine(reloaded); tx.Commit(); } Console.WriteLine("Press <ENTER> to exit..."); Console.ReadLine(); } private static Customer CreateCustomer() { var customer = new Customer { FirstName = "John", LastName = "Doe", Points = 100, HasGoldStatus = true, MemberSince = new DateTime(2012, 1, 1), CreditRating = CustomerCreditRating.Good, AverageRating = 42.42424242, Address = CreateLocation() }; var order1 = new Order { Ordered = DateTime.Now }; customer.AddOrder(order1); var order2 = new Order { Ordered = DateTime.Now.AddDays(-1), Shipped = DateTime.Now, ShipTo = CreateLocation() }; customer.AddOrder(order2); return customer; } private static Location CreateLocation() { return new Location { Street = "123 Somewhere Avenue", City = "Nowhere", Province = "Alberta", Country = "Canada" }; } private static Configuration ConfigureNHibernate() { NHibernateProfiler.Initialize(); var cfg = new Configuration(); cfg.DataBaseIntegration(x => { x.ConnectionStringName = "default"; x.Driver<SqlClientDriver>(); x.Dialect<MsSql2008Dialect>(); x.IsolationLevel = IsolationLevel.RepeatableRead; x.Timeout = 10; x.BatchSize = 10; }); cfg.SessionFactory().GenerateStatistics(); cfg.AddAssembly(Assembly.GetExecutingAssembly()); return cfg; } } }
Он собирается сохранить это в базе данных, а затем перезагрузить его. Теперь давайте запустим ваше приложение и откроем NHibernate Profiler и посмотрим, как оно его сохранило.
Вы заметите, что у нас есть 3 группы заявлений. Первый вставит клиента, а его идентификатором будет Guid, который выделен. Второе утверждение вставляется в таблицу заказов.
Вы заметите, что там установлен тот же Идентификатор клиента, так что установите этот внешний ключ. Последний оператор — это обновление, которое обновит внешний ключ с тем же идентификатором клиента еще раз.
Теперь проблема в том, что у клиента есть заказы, а у заказов есть клиент, и мы никоим образом не сказали NHibernate, что это на самом деле те же отношения. То, как мы это делаем, с обратным равен true.
Итак, давайте перейдем к нашему файлу сопоставления customer.hbm.xml и установим обратное значение равным true, как показано в следующем коде.
<?xml version = "1.0" encoding = "utf-8" ?> <hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo" namespace = "NHibernateDemo"> <class name = "Customer"> <id name = "Id"> <generator class = "guid.comb"/> </id> <property name = "FirstName"/> <property name = "LastName"/> <property name = "AverageRating"/> <property name = "Points"/> <property name = "HasGoldStatus"/> <property name = "MemberSince" type = "UtcDateTime"/> <property name = "CreditRating" type = "CustomerCreditRatingType"/> <component name = "Address"> <property name = "Street"/> <property name = "City"/> <property name = "Province"/> <property name = "Country"/> </component> <set name = "Orders" table = "`Order`" cascade = "all-delete-orphan" inverse = "true"> <key column = "CustomerId"/> <one-to-many class = "Order"/> </set> </class> </hibernate-mapping>
При сохранении заказов он устанавливает этот внешний ключ со стороны заказа. Теперь давайте снова запустим это приложение и откроем профилировщик NHibernate.
Если мы посмотрим, как они вставляются, мы получим вставку в клиенте и вставку в заказы, но у нас нет этого дублирующего обновления внешнего ключа, потому что он обновляется при сохранении заказов.
-
Теперь, вы должны заметить, что если у вас есть только однонаправленная ассоциация, и это множество поддерживает эти отношения, то если вы установите обратное значение равным true, этот внешний ключ никогда не будет установлен, и эти элементы никогда не будут иметь своих внешние ключи, установленные в базе данных.
-
Если вы посмотрите на отношение «многие к одному» в файле Order.hbm.xml и ищите обратное, у него фактически нет обратного атрибута.
-
Он всегда устанавливается из дочернего элемента, но если у вас есть коллекция «многие ко многим», вы можете установить ее с любой стороны.
Теперь, вы должны заметить, что если у вас есть только однонаправленная ассоциация, и это множество поддерживает эти отношения, то если вы установите обратное значение равным true, этот внешний ключ никогда не будет установлен, и эти элементы никогда не будут иметь своих внешние ключи, установленные в базе данных.
Если вы посмотрите на отношение «многие к одному» в файле Order.hbm.xml и ищите обратное, у него фактически нет обратного атрибута.
Он всегда устанавливается из дочернего элемента, но если у вас есть коллекция «многие ко многим», вы можете установить ее с любой стороны.
NHibernate — загрузить / получить
В этой главе мы рассмотрим, как работают функции загрузки и получения и как мы можем их использовать. Это два очень похожих API, предоставляемых ISession для загрузки объекта по первичному ключу.
-
Get — вернет объект или ноль.
-
Load — он вернет объект или сгенерирует исключение ObjectNotFoundException .
Get — вернет объект или ноль.
Load — он вернет объект или сгенерирует исключение ObjectNotFoundException .
Теперь, почему у нас есть эти два разных API?
нагрузка
-
Это потому, что Load может оптимизировать обходы базы данных намного эффективнее.
-
На самом деле Load возвращает прокси-объект и не нуждается в доступе к базе данных прямо при вызове этого вызова Load.
-
Когда вы обращаетесь к этому прокси, объект не попадает в базу данных, он может вызвать исключение ObjectNotFoundException в этой точке.
Это потому, что Load может оптимизировать обходы базы данных намного эффективнее.
На самом деле Load возвращает прокси-объект и не нуждается в доступе к базе данных прямо при вызове этого вызова Load.
Когда вы обращаетесь к этому прокси, объект не попадает в базу данных, он может вызвать исключение ObjectNotFoundException в этой точке.
Получить
-
И наоборот, с Get из-за ограничений CLR или Common Language Runtime и NHibernate должен немедленно перейти в базу данных, проверить, есть ли объекты, и вернуть ноль, если его нет.
-
У него нет опции объекта для отсрочки этого извлечения, обратного обращения к базе данных на более позднее время, потому что он не может вернуть прокси-объект, и это поменял этот прокси-объект на ноль, когда пользователь фактически обращается к нему.
И наоборот, с Get из-за ограничений CLR или Common Language Runtime и NHibernate должен немедленно перейти в базу данных, проверить, есть ли объекты, и вернуть ноль, если его нет.
У него нет опции объекта для отсрочки этого извлечения, обратного обращения к базе данных на более позднее время, потому что он не может вернуть прокси-объект, и это поменял этот прокси-объект на ноль, когда пользователь фактически обращается к нему.
Давайте рассмотрим простой пример, в котором вы увидите, как они на самом деле используются, и разницу между Get и Load. Мы продолжим работу с теми же классами домена Customers и Orders и аналогичными файлами сопоставления из предыдущей главы.
В этом примере мы сначала будем использовать Get, как показано в следующей программе.
using System; using System.Data; using System.Linq; using System.Reflection; using HibernatingRhinos.Profiler.Appender.NHibernate; using NHibernate.Cfg; using NHibernate.Criterion; using NHibernate.Dialect; using NHibernate.Driver; using NHibernate.Linq; namespace NHibernateDemo { internal class Program { private static void Main() { var cfg = ConfigureNHibernate(); var sessionFactory = cfg.BuildSessionFactory(); using(var session = sessionFactory.OpenSession()) using(var tx = session.BeginTransaction()) { var id1 = Guid.Parse("4e97c816-6bce-11e1-b095-6cf049ee52be"); var id2 = Guid.Parse("AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE"); var customer1 = session.Get<Customer>(id1); Console.WriteLine("Customer1 data"); Console.WriteLine(customer1); var customer2 = session.Get<Customer>(id2); Console.WriteLine("Customer2 data"); Console.WriteLine(customer2); tx.Commit(); } Console.WriteLine("Press <ENTER> to exit..."); Console.ReadLine(); } private static Configuration ConfigureNHibernate() { NHibernateProfiler.Initialize(); var cfg = new Configuration(); cfg.DataBaseIntegration(x => { x.ConnectionStringName = "default"; x.Driver<SqlClientDriver>(); x.Dialect<MsSql2008Dialect>(); x.IsolationLevel = IsolationLevel.RepeatableRead; x.Timeout = 10; x.BatchSize = 10; }); cfg.SessionFactory().GenerateStatistics(); cfg.AddAssembly(Assembly.GetExecutingAssembly()); return cfg; } } }
Как вы можете видеть, у нас есть два идентификатора Guid , первый из которых является хорошим идентификатором, это идентификатор клиента, который, как мы знаем, находится в базе данных. Пока второго идентификатора нет в базе данных. Оба эти идентификатора передаются в качестве параметра в метод Get (), а затем результат выводится на консоль.
Когда приведенный выше код скомпилирован и выполнен, вы увидите следующий вывод.
Customer1 data Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be) Points: 74 HasGoldStatus: True MemberSince: 4/4/2009 12:00:00 AM (Utc) CreditRating: Neutral AverageRating: 0 Orders: Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be Customer2 data Press <ENTER> to exit...
Как видно, данные Customer1 напечатаны, но данные Customer2 пусты, потому что запись Customer2 недоступна в базе данных.
Когда вы снова запустите свое приложение, мы можем вставить точку останова перед оператором фиксации, а затем посмотрим на обоих клиентов в окне Watch.
Как вы можете видеть, данные Customer1 доступны, а Customer2 имеет значение null, а тип NHibernateDemo.Customer для обоих.
Теперь давайте использовать метод Load вместо Get в том же примере, как показано в следующем коде.
using System; using System.Data; using System.Linq; using System.Reflection; using HibernatingRhinos.Profiler.Appender.NHibernate; using NHibernate.Cfg; using NHibernate.Criterion; using NHibernate.Dialect; using NHibernate.Driver; using NHibernate.Linq; namespace NHibernateDemo { internal class Program { private static void Main() { var cfg = ConfigureNHibernate(); var sessionFactory = cfg.BuildSessionFactory(); using(var session = sessionFactory.OpenSession()) using(var tx = session.BeginTransaction()) { var id1 = Guid.Parse("4e97c816-6bce-11e1-b095-6cf049ee52be"); var id2 = Guid.Parse("AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE"); var customer1 = session.Load<Customer>(id1); Console.WriteLine("Customer1 data"); Console.WriteLine(customer1); var customer2 = session.Load<Customer>(id2); Console.WriteLine("Customer2 data"); Console.WriteLine(customer2); tx.Commit(); } Console.WriteLine("Press <ENTER> to exit..."); Console.ReadLine(); } private static Configuration ConfigureNHibernate() { NHibernateProfiler.Initialize(); var cfg = new Configuration(); cfg.DataBaseIntegration(x => { x.ConnectionStringName = "default"; x.Driver<SqlClientDriver>(); x.Dialect<MsSql2008Dialect>(); x.IsolationLevel = IsolationLevel.RepeatableRead; x.Timeout = 10; x.BatchSize = 10; }); cfg.SessionFactory().GenerateStatistics(); cfg.AddAssembly(Assembly.GetExecutingAssembly()); return cfg; } } }
Теперь давайте запустим этот пример, и вы увидите, что выдается следующее исключение, как показано на скриншоте.
Теперь, если вы посмотрите в окно Watch, вы увидите, что тип является прокси клиента для обоих объектов. И вы также видите те же данные для Customer1 в окне консоли.
Customer1 data Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be) Points: 74 HasGoldStatus: True MemberSince: 4/4/2009 12:00:00 AM (Utc) CreditRating: Neutral AverageRating: 0 Orders: Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be Customer2 data
NHibernate — Linq
В этой главе мы рассмотрим еще один распространенный API, который будут использовать люди, — поставщик NHibernate LINQ. Его доступ через метод расширения в ISession и подпись является Query <T> . Есть два типа синтаксиса при использовании LINQ —
- Синтаксис цепочки запросов
- Синтаксис понимания запроса
Синтаксис цепочки запросов
Вы можете получить доступ к любой записи из базы данных, используя синтаксис цепочки методов, как показано в следующей программе.
var customer = session.Query<Customer>() .Where(c => c.FirstName == "Laverne")
-
Вы можете видеть, что у нас есть запрос, а также предложение WHERE, у вас могут быть дополнительные предложения WHERE и аналогично предложение select.
-
Это стандартный синтаксис цепочки методов, который вы можете использовать в обычном LINQ.
-
LINQ to Objects или LINQ to SQL, любые другие поставщики LINQ, с которыми вы можете быть знакомы.
Вы можете видеть, что у нас есть запрос, а также предложение WHERE, у вас могут быть дополнительные предложения WHERE и аналогично предложение select.
Это стандартный синтаксис цепочки методов, который вы можете использовать в обычном LINQ.
LINQ to Objects или LINQ to SQL, любые другие поставщики LINQ, с которыми вы можете быть знакомы.
Давайте рассмотрим простой пример, в котором мы найдем клиента, которого зовут Лаверн. Теперь есть вероятность, что у нас может быть более одного клиента по имени Laverne, поэтому мы получим только первого.
using System; using System.Data; using System.Linq; using System.Reflection; using HibernatingRhinos.Profiler.Appender.NHibernate; using NHibernate.Cfg; using NHibernate.Criterion; using NHibernate.Dialect; using NHibernate.Driver; using NHibernate.Linq; namespace NHibernateDemo { internal class Program { private static void Main() { var cfg = ConfigureNHibernate(); var sessionFactory = cfg.BuildSessionFactory(); using(var session = sessionFactory.OpenSession()) using(var tx = session.BeginTransaction()) { var customer = session.Query<Customer>() .Where(c => c.FirstName == "Laverne").First(); Console.WriteLine(customer); tx.Commit(); } Console.WriteLine("Press <ENTER> to exit..."); Console.ReadLine(); } private static Configuration ConfigureNHibernate() { NHibernateProfiler.Initialize(); var cfg = new Configuration(); cfg.DataBaseIntegration(x => { x.ConnectionStringName = "default"; x.Driver<SqlClientDriver>(); x.Dialect<MsSql2008Dialect>(); x.IsolationLevel = IsolationLevel.RepeatableRead; x.Timeout = 10; x.BatchSize = 10; }); cfg.SessionFactory().GenerateStatistics(); cfg.AddAssembly(Assembly.GetExecutingAssembly()); return cfg; } } }
Теперь, когда приведенный выше код скомпилирован и выполнен, вы увидите следующий вывод.
Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be) Points: 74 HasGoldStatus: True MemberSince: 4/4/2009 12:00:00 AM (Utc) CreditRating: Neutral AverageRating: 0 Orders: Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be Press <ENTER> to exit...
Синтаксис понимания запроса
Существует также синтаксис понимания запросов, который во многом похож на SQL с использованием ключевых слов from, where и select.
Итак, давайте посмотрим на тот же пример, но на этот раз мы используем синтаксис пониманий LINQ, который во многом похож на SQL, как показано в следующей программе.
using System; using System.Data; using System.Linq; using System.Reflection; using HibernatingRhinos.Profiler.Appender.NHibernate; using NHibernate.Cfg; using NHibernate.Criterion; using NHibernate.Dialect; using NHibernate.Driver; using NHibernate.Linq; namespace NHibernateDemo { internal class Program { private static void Main() { var cfg = ConfigureNHibernate(); var sessionFactory = cfg.BuildSessionFactory(); using(var session = sessionFactory.OpenSession()) using(var tx = session.BeginTransaction()) { var customer = (from c in session.Query<Customer>() where c.FirstName == "Laverne" select c).First(); Console.WriteLine(customer); tx.Commit(); } Console.WriteLine("Press <ENTER> to exit..."); Console.ReadLine(); } private static Configuration ConfigureNHibernate() { NHibernateProfiler.Initialize(); var cfg = new Configuration(); cfg.DataBaseIntegration(x => { x.ConnectionStringName = "default"; x.Driver<SqlClientDriver>(); x.Dialect<MsSql2008Dialect>(); x.IsolationLevel = IsolationLevel.RepeatableRead; x.Timeout = 10; x.BatchSize = 10; }); cfg.SessionFactory().GenerateStatistics(); cfg.AddAssembly(Assembly.GetExecutingAssembly()); return cfg; } } }
Теперь давайте снова запустим это приложение, и вы увидите следующий вывод.
Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be) Points: 74 HasGoldStatus: True MemberSince: 4/4/2009 12:00:00 AM (Utc) CreditRating: Neutral AverageRating: 0 Orders: Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be Press <ENTER> to exit...
Давайте посмотрим на другой пример, в котором мы получим всех тех клиентов, чье FirstName начинается с буквы H.
using System; using System.Data; using System.Linq; using System.Reflection; using HibernatingRhinos.Profiler.Appender.NHibernate; using NHibernate.Cfg; using NHibernate.Criterion; using NHibernate.Dialect; using NHibernate.Driver; using NHibernate.Linq; namespace NHibernateDemo { internal class Program { private static void Main() { var cfg = ConfigureNHibernate(); var sessionFactory = cfg.BuildSessionFactory(); using(var session = sessionFactory.OpenSession()) using(var tx = session.BeginTransaction()) { var customers = session.Query<Customer>() .Where(c =< c.FirstName.StartsWith("H")); foreach (var customer in customers.ToList()) { Console.WriteLine(customer); } tx.Commit(); } Console.WriteLine("Press <ENTER> to exit..."); Console.ReadLine(); } private static Configuration ConfigureNHibernate() { NHibernateProfiler.Initialize(); var cfg = new Configuration(); cfg.DataBaseIntegration(x => { x.ConnectionStringName = "default"; x.Driver<SqlClientDriver>(); x.Dialect<MsSql2008Dialect>(); x.IsolationLevel = IsolationLevel.RepeatableRead; x.Timeout = 10; x.BatchSize = 10; }); cfg.SessionFactory().GenerateStatistics(); cfg.AddAssembly(Assembly.GetExecutingAssembly()); return cfg; } } }
Точно так же синтаксис понимания запросов будет выглядеть как следующая программа.
using System; using System.Data; using System.Linq; using System.Reflection; using HibernatingRhinos.Profiler.Appender.NHibernate; using NHibernate.Cfg; using NHibernate.Criterion; using NHibernate.Dialect; using NHibernate.Driver; using NHibernate.Linq; namespace NHibernateDemo { internal class Program { private static void Main() { var cfg = ConfigureNHibernate(); var sessionFactory = cfg.BuildSessionFactory(); using(var session = sessionFactory.OpenSession()) using(var tx = session.BeginTransaction()) { var customers = from c in session.Query<Customer>() where c.FirstName.StartsWith("H") select c; foreach (var customer in customers.ToList()) { Console.WriteLine(customer); } tx.Commit(); } Console.WriteLine("Press <ENTER> to exit..."); Console.ReadLine(); } private static Configuration ConfigureNHibernate() { NHibernateProfiler.Initialize(); var cfg = new Configuration(); cfg.DataBaseIntegration(x => { x.ConnectionStringName = "default"; x.Driver<SqlClientDriver>(); x.Dialect<MsSql2008Dialect>(); x.IsolationLevel = IsolationLevel.RepeatableRead; x.Timeout = 10; x.BatchSize = 10; }); cfg.SessionFactory().GenerateStatistics(); cfg.AddAssembly(Assembly.GetExecutingAssembly()); return cfg; } } }
Давайте снова запустим это приложение, и вы увидите всех клиентов, чье имя начинается с алфавита H.
Herman Crooks (4ead3480-6bce-11e1-b15c-6cf049ee52be) Points: 74 HasGoldStatus: True MemberSince: 12/3/2010 12:00:00 AM (Utc) CreditRating: Neutral AverageRating: 0 Orders: Order Id: 4ead3480-6bce-11e1-b15d-6cf049ee52be Order Id: 4ead3480-6bce-11e1-b15e-6cf049ee52be Order Id: 4ead3480-6bce-11e1-b15f-6cf049ee52be Order Id: 4ead3480-6bce-11e1-b160-6cf049ee52be Order Id: 4ead3480-6bce-11e1-b161-6cf049ee52be Order Id: 4ead3480-6bce-11e1-b162-6cf049ee52be Order Id: 4ead3480-6bce-11e1-b163-6cf049ee52be Hudson Bins (4ec03f80-6bce-11e1-b2b7-6cf049ee52be) Points: 56 HasGoldStatus: False MemberSince: 10/20/2008 12:00:00 AM (Utc) CreditRating: Terrible AverageRating: 0 Orders: Order Id: 4ec03f80-6bce-11e1-b2b8-6cf049ee52be Order Id: 4ec03f80-6bce-11e1-b2b9-6cf049ee52be Order Id: 4ec03f80-6bce-11e1-b2ba-6cf049ee52be Order Id: 4ec03f80-6bce-11e1-b2bb-6cf049ee52be Order Id: 4ec03f80-6bce-11e1-b2bc-6cf049ee52be Order Id: 4ec03f80-6bce-11e1-b2bd-6cf049ee52be Order Id: 4ec03f80-6bce-11e1-b2be-6cf049ee52be Order Id: 4ec03f80-6bce-11e1-b2bf-6cf049ee52be Hettie Feest (4ec50240-6bce-11e1-b300-6cf049ee52be) Points: 82 HasGoldStatus: False MemberSince: 4/10/2009 12:00:00 AM (Utc) CreditRating: Neutral AverageRating: 0 Orders: Order Id: 4ec50240-6bce-11e1-b301-6cf049ee52be Order Id: 4ec50240-6bce-11e1-b302-6cf049ee52be Order Id: 4ec50240-6bce-11e1-b303-6cf049ee52be Press <ENTER> to exit...
NHibernate — Язык запросов Hibernate
В этой главе мы рассмотрим Hibernate Query Language. HQL используется в Hibernate и NHibernate.
-
Это самый старый механизм запросов наряду с критериями .
-
Он был реализован очень рано, и это API запросов на основе строк.
-
Вы получаете к нему доступ через ISession CreateQuery , и это почти похоже на SQL.
-
Он использует много одинаковых ключевых слов, но имеет упрощенный синтаксис.
-
Это один из самых распространенных примеров. Если вы ищете способ выполнить запрос, вы часто найдете примеры HQL.
Это самый старый механизм запросов наряду с критериями .
Он был реализован очень рано, и это API запросов на основе строк.
Вы получаете к нему доступ через ISession CreateQuery , и это почти похоже на SQL.
Он использует много одинаковых ключевых слов, но имеет упрощенный синтаксис.
Это один из самых распространенных примеров. Если вы ищете способ выполнить запрос, вы часто найдете примеры HQL.
Ниже приведен простой пример HQL —
var customers = session.CreateQuery("select c from Customer c where c.FirstName = 'Laverne'");
-
Итак, здесь вы можете видеть, что они выбирают C из клиентов, это очень похоже на SQL. Это непрозрачная строка для NHibernate, так что вы не знаете, является ли это допустимым HQL до времени выполнения, что является одним из недостатков.
-
Одной из сильных сторон поставщика LINQ является то, что вы можете получить поддержку времени компиляции.
-
Но HQL — это один из наиболее гибких механизмов часто используемых запросов. Говорят, что если нет другого способа сделать это, то есть способ сделать это на HQL.
Итак, здесь вы можете видеть, что они выбирают C из клиентов, это очень похоже на SQL. Это непрозрачная строка для NHibernate, так что вы не знаете, является ли это допустимым HQL до времени выполнения, что является одним из недостатков.
Одной из сильных сторон поставщика LINQ является то, что вы можете получить поддержку времени компиляции.
Но HQL — это один из наиболее гибких механизмов часто используемых запросов. Говорят, что если нет другого способа сделать это, то есть способ сделать это на HQL.
Давайте посмотрим на пример simpe, в котором мы воссоздадим наши запросы LINQ, используя вместо этого HQL. Вы можете получить доступ к HQL, вызвав session.CreateQuery и передав в качестве параметра строку HQL.
using System; using System.Data; using System.Linq; using System.Reflection; using HibernatingRhinos.Profiler.Appender.NHibernate; using NHibernate.Cfg; using NHibernate.Criterion; using NHibernate.Dialect; using NHibernate.Driver; using NHibernate.Linq; namespace NHibernateDemo { internal class Program { private static void Main() { var cfg = ConfigureNHibernate(); var sessionFactory = cfg.BuildSessionFactory(); using(var session = sessionFactory.OpenSession()) using(var tx = session.BeginTransaction()) { var customers = session.CreateQuery("select c from Customer c where c.FirstName = 'Laverne'"); foreach (var customer in customers.List<Customer>()) { Console.WriteLine(customer); } tx.Commit(); } Console.WriteLine("Press <ENTER> to exit..."); Console.ReadLine(); } private static Configuration ConfigureNHibernate() { NHibernateProfiler.Initialize(); var cfg = new Configuration(); cfg.DataBaseIntegration(x => { x.ConnectionStringName = "default"; x.Driver<SqlClientDriver>(); x.Dialect<MsSql2008Dialect>(); x.IsolationLevel = IsolationLevel.RepeatableRead; x.Timeout = 10; x.BatchSize = 10; }); cfg.SessionFactory().GenerateStatistics(); cfg.AddAssembly(Assembly.GetExecutingAssembly()); return cfg; } } }
-
Эта строка HQL очень похожа на SQL, главное отличие в том, что FirstName — это имя свойства, а не имя столбца.
-
Таким образом, если есть расхождение между ними, вы используете имя свойства. То же самое, это выглядит как имя таблицы, но на самом деле это имя класса, из которого мы выбираем.
-
Если бы внутренняя таблица была названа как Клиенты, мы все равно использовали бы Клиента в нашем HQL-запросе.
Эта строка HQL очень похожа на SQL, главное отличие в том, что FirstName — это имя свойства, а не имя столбца.
Таким образом, если есть расхождение между ними, вы используете имя свойства. То же самое, это выглядит как имя таблицы, но на самом деле это имя класса, из которого мы выбираем.
Если бы внутренняя таблица была названа как Клиенты, мы все равно использовали бы Клиента в нашем HQL-запросе.
Давайте запустим это приложение, и вы увидите следующий вывод.
Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be) Points: 74 HasGoldStatus: True MemberSince: 4/4/2009 12:00:00 AM (Utc) CreditRating: Neutral AverageRating: 0 Orders: Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be Press <ENTER> to exit...
Давайте рассмотрим еще один простой пример, в котором мы получим всех тех клиентов, чье FirstName начинается с буквы H с использованием HQL.
using System; using System.Data; using System.Linq; using System.Reflection; using HibernatingRhinos.Profiler.Appender.NHibernate; using NHibernate.Cfg; using NHibernate.Criterion; using NHibernate.Dialect; using NHibernate.Driver; using NHibernate.Linq; namespace NHibernateDemo { internal class Program { private static void Main() { var cfg = ConfigureNHibernate(); var sessionFactory = cfg.BuildSessionFactory(); using(var session = sessionFactory.OpenSession()) using(var tx = session.BeginTransaction()) { var customers = session.CreateQuery("select c from Customer c where c.FirstName like 'H%'"); foreach (var customer in customers.List<Customer>()) { Console.WriteLine(customer); } tx.Commit(); } Console.WriteLine("Press <ENTER> to exit..."); Console.ReadLine(); } private static Configuration ConfigureNHibernate() { NHibernateProfiler.Initialize(); var cfg = new Configuration(); cfg.DataBaseIntegration(x => { x.ConnectionStringName = "default"; x.Driver<SqlClientDriver>(); x.Dialect<MsSql2008Dialect>(); x.IsolationLevel = IsolationLevel.RepeatableRead; x.Timeout = 10; x.BatchSize = 10; }); cfg.SessionFactory().GenerateStatistics(); cfg.AddAssembly(Assembly.GetExecutingAssembly()); return cfg; } } }
Давайте снова запустим ваше приложение, и вы увидите, что все клиенты, чье имя начинается с H, возвращаются из этого запроса.
Herman Crooks (4ead3480-6bce-11e1-b15c-6cf049ee52be) Points: 74 HasGoldStatus: True MemberSince: 12/3/2010 12:00:00 AM (Utc) CreditRating: Neutral AverageRating: 0 Orders: Order Id: 4ead3480-6bce-11e1-b15d-6cf049ee52be Order Id: 4ead3480-6bce-11e1-b15e-6cf049ee52be Order Id: 4ead3480-6bce-11e1-b15f-6cf049ee52be Order Id: 4ead3480-6bce-11e1-b160-6cf049ee52be Order Id: 4ead3480-6bce-11e1-b161-6cf049ee52be Order Id: 4ead3480-6bce-11e1-b162-6cf049ee52be Order Id: 4ead3480-6bce-11e1-b163-6cf049ee52be Hudson Bins (4ec03f80-6bce-11e1-b2b7-6cf049ee52be) Points: 56 HasGoldStatus: False MemberSince: 10/20/2008 12:00:00 AM (Utc) CreditRating: Terrible AverageRating: 0 Orders: Order Id: 4ec03f80-6bce-11e1-b2b8-6cf049ee52be Order Id: 4ec03f80-6bce-11e1-b2b9-6cf049ee52be Order Id: 4ec03f80-6bce-11e1-b2ba-6cf049ee52be Order Id: 4ec03f80-6bce-11e1-b2bb-6cf049ee52be Order Id: 4ec03f80-6bce-11e1-b2bc-6cf049ee52be Order Id: 4ec03f80-6bce-11e1-b2bd-6cf049ee52be Order Id: 4ec03f80-6bce-11e1-b2be-6cf049ee52be Order Id: 4ec03f80-6bce-11e1-b2bf-6cf049ee52be Hettie Feest (4ec50240-6bce-11e1-b300-6cf049ee52be) Points: 82 HasGoldStatus: False MemberSince: 4/10/2009 12:00:00 AM (Utc) CreditRating: Neutral AverageRating: 0 Orders: Order Id: 4ec50240-6bce-11e1-b301-6cf049ee52be Order Id: 4ec50240-6bce-11e1-b302-6cf049ee52be Order Id: 4ec50240-6bce-11e1-b303-6cf049ee52be Press <ENTER> to exit...
Мы можем делать более сложные вещи, такие как получение всех заказов, если количество клиентов превышает 9. Далее приведен запрос HQL для того же самого.
var customers = session.CreateQuery("select c from Customer c where size(c.Orders) > 9"); foreach (var customer in customers.List<Customer>()) { Console.WriteLine(customer); }
Нам также нужно указать, что нам нужен здесь размер, количество или длина. В HQL у нас есть возможность использовать метод специального размера, как показано выше.
Другой способ написать это, если вы предпочитаете, это c.Orders.size , и это дает точный эффект.
var customers = session.CreateQuery("select c from Customer c where c.Orders.size > 9"); foreach (var customer in customers.List<Customer>()) { Console.WriteLine(customer); }
Давайте запустим это приложение.
Lindsay Towne (4ea3aef6-6bce-11e1-b0cb-6cf049ee52be) Points: 50 HasGoldStatus: False MemberSince: 4/13/2007 12:00:00 AM (Utc) CreditRating: VeryGood AverageRating: 0 Orders: Order Id: 4ea3aef6-6bce-11e1-b0cc-6cf049ee52be Order Id: 4ea3aef6-6bce-11e1-b0cd-6cf049ee52be Order Id: 4ea3aef6-6bce-11e1-b0ce-6cf049ee52be Order Id: 4ea3aef6-6bce-11e1-b0cf-6cf049ee52be Order Id: 4ea3aef6-6bce-11e1-b0d0-6cf049ee52be Order Id: 4ea3aef6-6bce-11e1-b0d1-6cf049ee52be Order Id: 4ea3aef6-6bce-11e1-b0d2-6cf049ee52be Order Id: 4ea3aef6-6bce-11e1-b0d3-6cf049ee52be Order Id: 4ea3aef6-6bce-11e1-b0d4-6cf049ee52be Order Id: 4ea3aef6-6bce-11e1-b0d5-6cf049ee52be Wyman Hammes (4ea61056-6bce-11e1-b0e2-6cf049ee52be) Points: 32 HasGoldStatus: False MemberSince: 2/5/2011 12:00:00 AM (Utc) CreditRating: Good AverageRating: 0 Orders: Order Id: 4ea61056-6bce-11e1-b0e3-6cf049ee52be Order Id: 4ea61056-6bce-11e1-b0e4-6cf049ee52be Order Id: 4ea61056-6bce-11e1-b0e5-6cf049ee52be Order Id: 4ea61056-6bce-11e1-b0e6-6cf049ee52be Order Id: 4ea61056-6bce-11e1-b0e7-6cf049ee52be Order Id: 4ea61056-6bce-11e1-b0e8-6cf049ee52be Order Id: 4ea61056-6bce-11e1-b0e9-6cf049ee52be Order Id: 4ea61056-6bce-11e1-b0ea-6cf049ee52be Order Id: 4ea61056-6bce-11e1-b0eb-6cf049ee52be Order Id: 4ea61056-6bce-11e1-b0ec-6cf049ee52be Press <ENTER> to exit...
Вы видите, что все клиенты, у которых более 9 заказов, извлекаются из базы данных.
NHibernate — Критерии запросов
В этой главе мы рассмотрим механизм запроса критериев. API NHibernate Query by Criteria позволяет создавать запросы, манипулируя объектами критериев во время выполнения.
-
Этот подход позволяет динамически задавать ограничения без прямых манипуляций со строками, но он не теряет большую часть гибкости или мощи HQL.
-
С другой стороны, запросы, выраженные в качестве критериев, часто менее читабельны, чем запросы, выраженные в HQL.
-
Классический синтаксис критериев — это объектный API запросов, как показано в следующей программе.
Этот подход позволяет динамически задавать ограничения без прямых манипуляций со строками, но он не теряет большую часть гибкости или мощи HQL.
С другой стороны, запросы, выраженные в качестве критериев, часто менее читабельны, чем запросы, выраженные в HQL.
Классический синтаксис критериев — это объектный API запросов, как показано в следующей программе.
var customers = session.CreateCriteria<Customer>().Add(Restrictions.Like("FirstName", "H%"));
-
Как видите, мы выполняем критерии создания сеанса для клиента, и теперь мы добавляем объект ограничения в этот запрос.
-
Это полезно для страниц запросов, где пользователи могут выбирать определенные параметры, но не другие.
-
Проще создать запрос в виде древовидной структуры запроса, чем в HQL или LINQ, где вы можете использовать оператор AND или OR в предложении WHERE.
-
Проще просто добавить дополнительные ограничения, используя эти критерии объектов.
Как видите, мы выполняем критерии создания сеанса для клиента, и теперь мы добавляем объект ограничения в этот запрос.
Это полезно для страниц запросов, где пользователи могут выбирать определенные параметры, но не другие.
Проще создать запрос в виде древовидной структуры запроса, чем в HQL или LINQ, где вы можете использовать оператор AND или OR в предложении WHERE.
Проще просто добавить дополнительные ограничения, используя эти критерии объектов.
Давайте рассмотрим простой пример, в котором мы создадим запрос и получим доступ к API критериев через createCriteria, а затем добавим ограничение на то, что имя начинается с H.
using System; using System.Data; using System.Linq; using System.Reflection; using HibernatingRhinos.Profiler.Appender.NHibernate; using NHibernate.Cfg; using NHibernate.Criterion; using NHibernate.Dialect; using NHibernate.Driver; using NHibernate.Linq; namespace NHibernateDemo { internal class Program { private static void Main() { var cfg = ConfigureNHibernate(); var sessionFactory = cfg.BuildSessionFactory(); using(var session = sessionFactory.OpenSession()) using(var tx = session.BeginTransaction()) { var customers = session.CreateCriteria<Customer>() .Add(Restrictions.Like("FirstName", "H%")); foreach (var customer in customers.List<Customer>()) { Console.WriteLine(customer); } tx.Commit(); } Console.WriteLine("Press <ENTER> to exit..."); Console.ReadLine(); } private static Configuration ConfigureNHibernate() { NHibernateProfiler.Initialize(); var cfg = new Configuration(); cfg.DataBaseIntegration(x => { x.ConnectionStringName = "default"; x.Driver<SqlClientDriver>(); x.Dialect<MsSql2008Dialect>(); x.IsolationLevel = IsolationLevel.RepeatableRead; x.Timeout = 10; x.BatchSize = 10; }); cfg.SessionFactory().GenerateStatistics(); cfg.AddAssembly(Assembly.GetExecutingAssembly()); return cfg; } } }
Когда приведенный выше код скомпилирован и выполнен, вы увидите следующий вывод.
Herman Crooks (4ead3480-6bce-11e1-b15c-6cf049ee52be) Points: 74 HasGoldStatus: True MemberSince: 12/3/2010 12:00:00 AM (Utc) CreditRating: Neutral AverageRating: 0 Orders: Order Id: 4ead3480-6bce-11e1-b15d-6cf049ee52be Order Id: 4ead3480-6bce-11e1-b15e-6cf049ee52be Order Id: 4ead3480-6bce-11e1-b15f-6cf049ee52be Order Id: 4ead3480-6bce-11e1-b160-6cf049ee52be Order Id: 4ead3480-6bce-11e1-b161-6cf049ee52be Order Id: 4ead3480-6bce-11e1-b162-6cf049ee52be Order Id: 4ead3480-6bce-11e1-b163-6cf049ee52be Hudson Bins (4ec03f80-6bce-11e1-b2b7-6cf049ee52be) Points: 56 HasGoldStatus: False MemberSince: 10/20/2008 12:00:00 AM (Utc) CreditRating: Terrible AverageRating: 0 Orders: Order Id: 4ec03f80-6bce-11e1-b2b8-6cf049ee52be Order Id: 4ec03f80-6bce-11e1-b2b9-6cf049ee52be Order Id: 4ec03f80-6bce-11e1-b2ba-6cf049ee52be Order Id: 4ec03f80-6bce-11e1-b2bb-6cf049ee52be Order Id: 4ec03f80-6bce-11e1-b2bc-6cf049ee52be Order Id: 4ec03f80-6bce-11e1-b2bd-6cf049ee52be Order Id: 4ec03f80-6bce-11e1-b2be-6cf049ee52be Order Id: 4ec03f80-6bce-11e1-b2bf-6cf049ee52be Hettie Feest (4ec50240-6bce-11e1-b300-6cf049ee52be) Points: 82 HasGoldStatus: False MemberSince: 4/10/2009 12:00:00 AM (Utc) CreditRating: Neutral AverageRating: 0 Orders: Order Id: 4ec50240-6bce-11e1-b301-6cf049ee52be Order Id: 4ec50240-6bce-11e1-b302-6cf049ee52be Order Id: 4ec50240-6bce-11e1-b303-6cf049ee52be Press <ENTER> to exit…
Давайте посмотрим на еще один простой пример, в котором мы будем извлекать клиента, имя которого равно «Лаверне»
using System; using System.Data; using System.Linq; using System.Reflection; using HibernatingRhinos.Profiler.Appender.NHibernate; using NHibernate.Cfg; using NHibernate.Criterion; using NHibernate.Dialect; using NHibernate.Driver; using NHibernate.Linq; namespace NHibernateDemo { internal class Program { private static void Main() { var cfg = ConfigureNHibernate(); var sessionFactory = cfg.BuildSessionFactory(); using(var session = sessionFactory.OpenSession()) using(var tx = session.BeginTransaction()) { var customers = session.CreateCriteria<Customer>() .Add(Restrictions.Eq("FirstName", "Laverne")) .List<Customer>(); foreach (var customer in customers) { Console.WriteLine(customer); } tx.Commit(); } Console.WriteLine("Press <ENTER> to exit..."); Console.ReadLine(); } private static Configuration ConfigureNHibernate() { NHibernateProfiler.Initialize(); var cfg = new Configuration(); cfg.DataBaseIntegration(x => { x.ConnectionStringName = "default"; x.Driver<SqlClientDriver>(); x.Dialect<MsSql2008Dialect>(); x.IsolationLevel = IsolationLevel.RepeatableRead; x.Timeout = 10; x.BatchSize = 10; }); cfg.SessionFactory().GenerateStatistics(); cfg.AddAssembly(Assembly.GetExecutingAssembly()); return cfg; } } }
Давайте снова запустим это приложение, и вы увидите следующий результат.
Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be) Points: 74 HasGoldStatus: True MemberSince: 4/4/2009 12:00:00 AM (Utc) CreditRating: Neutral AverageRating: 0 Orders: Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be Press <ENTER> to exit...
Теперь одним из основных недостатков критериев API являются непрозрачные строки в именах свойств. Таким образом, если имя было реорганизовано как что-то другое, инструмент рефакторинга не обязательно обнаружит непрозрачную строку.
NHibernate — QueryOver Queries
В этой главе мы рассмотрим QueryOver Queries. Это новый синтаксис, который больше похож на LINQ с использованием синтаксиса цепочки методов, как показано в следующем запросе.
var customers = session.QueryOver<Customer>() .Where(x => x.FirstName == "Laverne");
-
Это все еще критерии под прикрытием, но теперь наши запросы строго типизированы.
-
Как мы видели в запросе критериев, первое имя — просто непрозрачная строка, теперь мы на самом деле используем x.FirstName , поэтому первое имя подвергается рефакторингу и переименовывается, что изменяется в запросе критериев стиля ссылки, используя запрос через ,
-
Мы все еще можем делать много подобных вещей, но вы не можете использовать синтаксис понимания запроса с запросом поверх, вы должны использовать синтаксис цепочки методов, и вы не можете смешивать и сопоставлять ссылку и критерии.
-
Для многих запросов запрос через API очень полезен и обеспечивает гораздо более простое понимание синтаксиса объекта, чем непосредственное использование критериев.
Это все еще критерии под прикрытием, но теперь наши запросы строго типизированы.
Как мы видели в запросе критериев, первое имя — просто непрозрачная строка, теперь мы на самом деле используем x.FirstName , поэтому первое имя подвергается рефакторингу и переименовывается, что изменяется в запросе критериев стиля ссылки, используя запрос через ,
Мы все еще можем делать много подобных вещей, но вы не можете использовать синтаксис понимания запроса с запросом поверх, вы должны использовать синтаксис цепочки методов, и вы не можете смешивать и сопоставлять ссылку и критерии.
Для многих запросов запрос через API очень полезен и обеспечивает гораздо более простое понимание синтаксиса объекта, чем непосредственное использование критериев.
Давайте рассмотрим простой пример, в котором мы будем извлекать клиента, чье имя Лаверн, используя запрос.
using System; using System.Data; using System.Linq; using System.Reflection; using HibernatingRhinos.Profiler.Appender.NHibernate; using NHibernate.Cfg; using NHibernate.Criterion; using NHibernate.Dialect; using NHibernate.Driver; using NHibernate.Linq; namespace NHibernateDemo { internal class Program { private static void Main() { var cfg = ConfigureNHibernate(); var sessionFactory = cfg.BuildSessionFactory(); using(var session = sessionFactory.OpenSession()) using(var tx = session.BeginTransaction()) { var customers = session.QueryOver<Customer>() .Where(x => x.FirstName == "Laverne"); foreach (var customer in customers.List()) { Console.WriteLine(customer); } tx.Commit(); } Console.WriteLine("Press <ENTER> to exit..."); Console.ReadLine(); } private static Configuration ConfigureNHibernate() { NHibernateProfiler.Initialize(); var cfg = new Configuration(); cfg.DataBaseIntegration(x => { x.ConnectionStringName = "default"; x.Driver<SqlClientDriver>(); x.Dialect<MsSql2008Dialect>(); x.IsolationLevel = IsolationLevel.RepeatableRead; x.Timeout = 10; x.BatchSize = 10; }); cfg.SessionFactory().GenerateStatistics(); cfg.AddAssembly(Assembly.GetExecutingAssembly()); return cfg; } } }
Как вы можете видеть, это все еще Критерии под прикрытием, но это просто более хороший синтаксис.
Когда приведенный выше код скомпилирован и выполнен, вы увидите следующий вывод.
Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be) Points: 74 HasGoldStatus: True MemberSince: 4/4/2009 12:00:00 AM (Utc) CreditRating: Neutral AverageRating: 0 Orders: Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be Press <ENTER> to exit...
Одним из недостатков является то, что, скажем, мы хотим сказать, что FirstName.StartsWith («A»), как показано в следующей программе.
var customers = session.QueryOver<Customer>() .Where(x => x.FirstName.StartsWith("A")); foreach (var customer in customers.List()) { Console.WriteLine(customer); } tx.Commit();
Теперь давайте снова запустим приложение, и вы увидите, что это не поставщик LINQ, поскольку он не знает, что такое метод StartsWith , поэтому вы получите исключение RunTime .
Исключение говорит нераспознанный вызов метода. Здесь мы делаем очевидную вещь, но это не обязательно работает.
Давайте попробуем что-нибудь еще, например FirstName равно «A%», как показано в следующем коде.
var customers = session.QueryOver<Customer>() .Where(x => x.FirstName == "A%"); foreach (var customer in customers.List()) { Console.WriteLine(customer); }
Давайте запустим это еще раз, и вы увидите, что мы не получим никаких результатов, как показано ниже.
Press <ENTER> to exit...
Чтобы понять, почему мы не получаем никаких результатов, давайте взглянем на профилировщик NHibernate.
Как видите, первое имя равно A%, а это не так. % Используется в SQL с использованием оператора like. Теперь нам нужно создать ограничение в предложении WHERE, как показано в следующей программе.
var customers = session.QueryOver<Customer>() .Where(Restrictions.On<Customer>(c => c.FirstName).IsLike("A%")); foreach (var customer in customers.List()) { Console.WriteLine(customer); }
Давайте снова запустим ваше приложение, и вы увидите, что все клиенты извлекаются с именем, начинающимся с A.
Alejandrin Will (4ea3aef6-6bce-11e1-b0b4-6cf049ee52be) Points: 24 HasGoldStatus: False MemberSince: 10/1/2011 12:00:00 AM (Utc) CreditRating: VeryVeryGood AverageRating: 0 Orders: Order Id: 4ea3aef6-6bce-11e1-b0b5-6cf049ee52be Austyn Nolan (4ea871b6-6bce-11e1-b110-6cf049ee52be) Points: 67 HasGoldStatus: True MemberSince: 12/29/2007 12:00:00 AM (Utc) CreditRating: Neutral AverageRating: 0 Orders: Order Id: 4ea871b6-6bce-11e1-b111-6cf049ee52be Antonia Murphy (4ea871b6-6bce-11e1-b121-6cf049ee52be) Points: 72 HasGoldStatus: True MemberSince: 6/15/2009 12:00:00 AM (Utc) CreditRating: Terrible AverageRating: 0 Orders: Order Id: 4ea871b6-6bce-11e1-b122-6cf049ee52be Order Id: 4ea871b6-6bce-11e1-b123-6cf049ee52be Order Id: 4ea871b6-6bce-11e1-b124-6cf049ee52be Order Id: 4ea871b6-6bce-11e1-b125-6cf049ee52be Order Id: 4ea871b6-6bce-11e1-b126-6cf049ee52be Order Id: 4ea871b6-6bce-11e1-b127-6cf049ee52be Order Id: 4ea871b6-6bce-11e1-b128-6cf049ee52be Order Id: 4ea871b6-6bce-11e1-b129-6cf049ee52be Order Id: 4ea871b6-6bce-11e1-b12a-6cf049ee52be
Он работает так же, как и раньше, за исключением использования нового синтаксиса QueryOver . Многие разработчики считают, что синтаксис LINQ более доступен и часто делает правильные вещи.
Если LINQ не может справиться с этим, тогда вы начнете смотреть на HQL или Критерии, чтобы увидеть, будет ли это более подходящим.
Он просто дает вам другой синтаксис, поэтому Criteria, и критерии создания, и QueryOver предоставляют вам еще один механизм запросов, который позволяет извлекать данные из базы данных с помощью NHibernate.
NHibernate — Native Sql
В этой главе мы рассмотрим, как использовать собственные запросы SQL в NHibernate. Если вы использовали рукописный SQL в течение ряда лет, вы можете быть обеспокоены тем, что ORM лишит вас той выразительности и гибкости, к которым вы привыкли.
-
Мощные средства запросов NHibernate позволяют вам делать практически все, что вы делаете в SQL, а в некоторых случаях и больше.
-
В тех редких случаях, когда вы не можете заставить собственные средства запросов NHibernate делать именно то, что вы хотите.
-
NHibernate позволяет вам извлекать объекты, используя собственный диалект SQL вашей базы данных.
Мощные средства запросов NHibernate позволяют вам делать практически все, что вы делаете в SQL, а в некоторых случаях и больше.
В тех редких случаях, когда вы не можете заставить собственные средства запросов NHibernate делать именно то, что вы хотите.
NHibernate позволяет вам извлекать объекты, используя собственный диалект SQL вашей базы данных.
Давайте рассмотрим простой пример запросов Native SQL в NHibernate.
using System; using System.Data; using System.Linq; using System.Reflection; using HibernatingRhinos.Profiler.Appender.NHibernate; using NHibernate.Cfg; using NHibernate.Criterion; using NHibernate.Dialect; using NHibernate.Driver; using NHibernate.Linq; using NHibernate; namespace NHibernateDemo { internal class Program { private static void Main() { var cfg = ConfigureNHibernate(); var sessionFactory = cfg.BuildSessionFactory(); using(var session = sessionFactory.OpenSession()) using(var tx = session.BeginTransaction()) { IQuery sqlQuery = session.CreateSQLQuery("SELECT * FROM CUSTOMER").AddEntity(typeof(Customer)); var customers = sqlQuery.List<Customer>(); foreach (var customer in customers) { Console.WriteLine(customer); } tx.Commit(); } Console.WriteLine("Press <ENTER> to exit..."); Console.ReadLine(); } private static Configuration ConfigureNHibernate() { NHibernateProfiler.Initialize(); var cfg = new Configuration(); cfg.DataBaseIntegration(x => { x.ConnectionStringName = "default"; x.Driver<SqlClientDriver>(); x.Dialect<MsSql2008Dialect>(); x.IsolationLevel = IsolationLevel.RepeatableRead; x.Timeout = 10; x.BatchSize = 10; }); cfg.SessionFactory().GenerateStatistics(); cfg.AddAssembly(Assembly.GetExecutingAssembly()); return cfg; } } }
Приведенный выше пример использует CreateSQLQuery () для получения списка объектов, и вы также заметите, что корневой тип сущности, который вы хотите, чтобы запрос возвращал, указан как Customer.
Запустим ваше приложение, и вы увидите, что все клиенты извлекаются из базы данных.
Emerson Prosacco (4ec2a0e0-6bce-11e1-b2cf-6cf049ee52be) Points: 17 HasGoldStatus: False MemberSince: 6/22/2007 12:00:00 AM (Utc) CreditRating: Excellent AverageRating: 0 Orders: Order Id: 4ec2a0e0-6bce-11e1-b2d0-6cf049ee52be Order Id: 4ec2a0e0-6bce-11e1-b2d1-6cf049ee52be Order Id: 4ec2a0e0-6bce-11e1-b2d2-6cf049ee52be Order Id: 4ec2a0e0-6bce-11e1-b2d3-6cf049ee52be Order Id: 4ec2a0e0-6bce-11e1-b2d4-6cf049ee52be Kaci Friesen (4ec2a0e0-6bce-11e1-b2d5-6cf049ee52be) Points: 30 HasGoldStatus: True MemberSince: 5/25/2007 12:00:00 AM (Utc) CreditRating: VeryVeryGood AverageRating: 0 Orders: Order Id: 4ec2a0e0-6bce-11e1-b2d6-6cf049ee52be Order Id: 4ec2a0e0-6bce-11e1-b2d7-6cf049ee52be Order Id: 4ec2a0e0-6bce-11e1-b2d8-6cf049ee52be Order Id: 4ec2a0e0-6bce-11e1-b2d9-6cf049ee52be Order Id: 4ec2a0e0-6bce-11e1-b2da-6cf049ee52be Order Id: 4ec2a0e0-6bce-11e1-b2db-6cf049ee52be Eveline Waters (4ec2a0e0-6bce-11e1-b2dc-6cf049ee52be) Points: 58 HasGoldStatus: False MemberSince: 10/29/2009 12:00:00 AM (Utc) CreditRating: Good AverageRating: 0 Orders: Order Id: 4ec2a0e0-6bce-11e1-b2dd-6cf049ee52be Order Id: 4ec2a0e0-6bce-11e1-b2de-6cf049ee52be Order Id: 4ec2a0e0-6bce-11e1-b2df-6cf049ee52be Order Id: 4ec2a0e0-6bce-11e1-b2e0-6cf049ee52be Order Id: 4ec2a0e0-6bce-11e1-b2e2-6cf049ee52be Molly Kuhn (4ec2a0e0-6bce-11e1-b2e3-6cf049ee52be) Points: 73 HasGoldStatus: False MemberSince: 12/16/2007 12:00:00 AM (Utc) CreditRating: VeryGood AverageRating: 0 Orders: Order Id: 4ec2a0e0-6bce-11e1-b2e4-6cf049ee52be Order Id: 4ec2a0e0-6bce-11e1-b2e5-6cf049ee52be Order Id: 4ec2a0e0-6bce-11e1-b2e6-6cf049ee52be Order Id: 4ec2a0e0-6bce-11e1-b2e7-6cf049ee52be Order Id: 4ec2a0e0-6bce-11e1-b2e8-6cf049ee52be Order Id: 4ec2a0e0-6bce-11e1-b2e9-6cf049ee52be Order Id: 4ec2a0e0-6bce-11e1-b2ea-6cf049ee52be Order Id: 4ec2a0e0-6bce-11e1-b2eb-6cf049ee52be Order Id: 4ec2a0e0-6bce-11e1-b2ec-6cf049ee52be
Вот еще один способ написания собственного SQL-запроса, как показано ниже.
IList<Customer> customers = session.CreateSQLQuery("SELECT * FROM CUSTOMER") .AddScalar("Id", NHibernateUtil.Guid) .AddScalar("FirstName", NHibernateUtil.String) .AddScalar("LastName", NHibernateUtil.String) .List<Customer>();
-
Как видно из вышеприведенного запроса, указана строка запроса SQL, а также столбцы и типы для возврата.
-
Это вернет массивы IList of Object со скалярными значениями для каждого столбца в таблице Customer.
-
Будут возвращены только эти три столбца, даже если запрос использует * и может вернуть больше трех перечисленных столбцов.
Как видно из вышеприведенного запроса, указана строка запроса SQL, а также столбцы и типы для возврата.
Это вернет массивы IList of Object со скалярными значениями для каждого столбца в таблице Customer.
Будут возвращены только эти три столбца, даже если запрос использует * и может вернуть больше трех перечисленных столбцов.
Давайте посмотрим на другой простой пример.
IList<Customer> customers = session.CreateSQLQuery("SELECT * FROM CUSTOMER WHERE FirstName = 'Laverne'") .AddEntity(typeof(Customer)) .List<Customer>(); foreach (var customer in customers) { Console.WriteLine(customer); }
Давайте снова запустим ваше приложение, и вы увидите следующий результат.
Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be) Points: 74 HasGoldStatus: True MemberSince: 4/4/2009 12:00:00 AM (Utc) CreditRating: Neutral AverageRating: 0 Orders: Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be Press <ENTER> to exit...
Точно так же вы можете указать любой тип SQL-запроса для извлечения данных из базы данных.
NHibernate — Свободный Hibernate
В этой главе мы будем подробно рассказывать о NHibernate. Свободный NHibernate — это еще один способ сопоставления, или вы можете сказать, что он является альтернативой стандартным файлам сопоставления XML для NHibernate. Вместо написания XML (.hbm.xml файлов) документов. С помощью Fluent NHibernate вы можете писать сопоставления в строго типизированном коде C #.
-
В Fluent NHibernate сопоставления компилируются вместе с остальной частью вашего приложения.
-
Вы можете легко изменить ваши отображения так же, как код вашего приложения, и компилятор потерпит неудачу при любых опечатках.
-
Он имеет обычную систему конфигурации, в которой вы можете указать шаблоны для переопределения соглашений об именах и многое другое.
-
Вы также можете установить, как вещи должны быть названы один раз, тогда Fluent NHibernate сделает все остальное.
В Fluent NHibernate сопоставления компилируются вместе с остальной частью вашего приложения.
Вы можете легко изменить ваши отображения так же, как код вашего приложения, и компилятор потерпит неудачу при любых опечатках.
Он имеет обычную систему конфигурации, в которой вы можете указать шаблоны для переопределения соглашений об именах и многое другое.
Вы также можете установить, как вещи должны быть названы один раз, тогда Fluent NHibernate сделает все остальное.
Давайте рассмотрим простой пример, создав новый консольный проект. В этой главе мы будем использовать простую базу данных, в которой у нас есть простая таблица Customer, как показано на следующем рисунке.
Установите Fluent NHibernate
Первым шагом для запуска Fluent NHibernate является установка пакета Fluent NHibernate. Поэтому откройте консоль диспетчера пакетов NuGet и введите следующую команду.
PM> install-package FluentNHibernate
После успешной установки вы увидите следующее сообщение.
Давайте добавим простой модельный класс Customer, и следующая программа покажет реализацию класса Customer.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace FluentNHibernateDemo { class Customer { public virtual int Id { get; set; } public virtual string FirstName { get; set; } public virtual string LastName { get; set; } } }
Теперь нам нужно создать Mappings, используя свободный NHibernate, поэтому добавьте еще один класс CustomerMap в ваш проект. Вот реализация класса CustomerMap.
using FluentNHibernate.Mapping; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace FluentNHibernateDemo { class CustomerMap : ClassMap<Customer> { public CustomerMap() { Id(x => x.Id); Map(x => x.FirstName); Map(x => x.LastName); Table("Customer"); } } }
Давайте добавим еще один класс NHibernateHelper, в котором мы будем устанавливать различные параметры конфигурации.
using FluentNHibernate.Cfg; using FluentNHibernate.Cfg.Db; using NHibernate; using NHibernate.Tool.hbm2ddl; namespace FluentNHibernateDemo { public class NHibernateHelper { private static ISessionFactory _sessionFactory; private static ISessionFactory SessionFactory { get { if (_sessionFactory == null) InitializeSessionFactory(); return _sessionFactory; } } private static void InitializeSessionFactory() { _sessionFactory = Fluently.Configure() String Data Source = asia13797\\sqlexpress; String Initial Catalog = NHibernateDemoDB; String Integrated Security = True; String Connect Timeout = 15; String Encrypt = False; String TrustServerCertificate = False; String ApplicationIntent = ReadWrite; String MultiSubnetFailover = False; .Database(MsSqlConfiguration.MsSql2008 .ConnectionString( @"Data Source + Initial Catalog + Integrated Security + Connect Timeout + Encrypt + TrustServerCertificate + ApplicationIntent + MultiSubnetFailover") .ShowSql() ) .Mappings(m => m.FluentMappings .AddFromAssemblyOf<Program>()) .ExposeConfiguration(cfg => new SchemaExport(cfg) .Create(true, true)) .BuildSessionFactory(); } public static ISession OpenSession() { return SessionFactory.OpenSession(); } } }
Теперь давайте перейдем к файлу Program.cs, в котором мы начнем сеанс, а затем создадим нового клиента и сохраним этого клиента в базе данных, как показано ниже.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace FluentNHibernateDemo { class Program { static void Main(string[] args) { using (var session = NHibernateHelper.OpenSession()) { using (var transaction = session.BeginTransaction()) { var customer = new Customer { FirstName = "Allan", LastName = "Bomer" }; session.Save(customer); transaction.Commit(); Console.WriteLine("Customer Created: " + customer.FirstName + "\t" + customer.LastName); } Console.ReadKey(); } } } }
Давайте запустим ваше приложение, и вы увидите следующий вывод.
if exists (select * from dbo.sysobjects where id = object_id(N'Customer') and OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table Customer create table Customer ( Id INT IDENTITY NOT NULL, FirstName NVARCHAR(255) null, LastName NVARCHAR(255) null, primary key (Id) ) NHibernate: INSERT INTO Customer (FirstName, LastName) VALUES (@p0, @p1); select SCOPE_IDENTITY();@p0 = 'Allan' [Type: String (4000)], @p1 = 'Bomer' [Type: String (4000)] Customer Created: Allan Bomer
Как видите, новый клиент создан. Чтобы просмотреть запись о клиенте, перейдем в базу данных и просмотрим данные, и вы увидите, что добавлен 1 клиент.