Обзор средств объектно-реляционной проекции (ORM) для платформы .NET

Что такое ОРП (Object Relational Mapping - ORM), зачем и с чем его едят, обзор имеющихся на рынке продуктов для .NET.

Зачем это нужно

Для разработчиков программного обеспечения не является секретом, что в мире хранения данных доминируют реляционные СУБД, тогда как объектный подход к проектированию и программированию - в мире обработки данных. В рамках статьи мы не будем обсуждать, почему так произошло и хороша ли сложившаяся ситуация, мы примем ее за данность и постараемся найти средства разрешения. Для этого проблему нужно вначале сформулировать, ведь, как известно, правильно поставленная задача уже половина решения.

Объектная и реляционная модели ортогональны. Это значит, что они моделируют одну и ту же сущность, но с разных сторон, под разными, я бы сказал перпендикулярными углами зрения. Реляционная модель акцентирует свое внимание на структуре и связях сущностей, объектная - на их свойствах и поведении. Цель использования реляционной модели - информационное моделирование, выделение существенных для нас атрибутов, сохранение их значений и последующего поиска, обработки и анализа. Цель использования объектной - моделирование поведения, выделение существенных для нас функций и последующего их использования. Между моделями есть пересечение - структурные сущности, которые по-разному в этих моделях отражаются. Для того, чтобы отобразить артефакты реляционной модели в артефакты же объектной в наших программах и требуется средство объектно-реляционной проекции - ОРП или широко распространенное англоязычное обозначение - ORM (Object Relational Mapping).

Выражаясь более простым языком, объектно-реляционный проектор - ОРП - теоретически позволяет программисту работать с таблицами, полями и связями реляционной БД, как с объектами, свойствами и коллекциями (массивами), не отвлекаясь на подробности более низкого уровня, такими, например, как порядок выборки и сохранения модифицированных данных, вопросы переносимости и особенностей диалекта SQL конкретной СУБД, генерации уникальных первичных ключей, заполнения полей ссылок для моделирования связей.

Рассмотрим для сравнения код с использованием ADO.NET и ОРП.

ADO.NET

SqlConnection myConnection = new SqlConnection(myConnectionString);
// Создаем два объекта DataAdapater для таблиц Customers и Orders
SqlDataAdapter mySqlDataAdapter = new SqlDataAdapter("select * from customers", myConnection);
SqlDataAdapter mySqlDataAdapter1 = new SqlDataAdapter("select * from orders", myConnection);
DataSet myDataSet = new DataSet();
object DataRow myDataRow;
// Создаем объект command builder для работы с таблицами Customers и Orders
SqlCommandBuilder mySqlCommandBuilder = new SqlCommandBuilder(mySqlDataAdapter);
SqlCommandBuilder mySqlCommandBuilder2 = new SqlCommandBuilder(mySqlDataAdapter1);
// Заполняем DataSet-ы
mySqlDataAdapter.Fill(myDataSet,"Customers");
mySqlDataAdapterl.Fill(myDataSet,"Orders");
// Добавляем связь между таблицами
myDataSet.Relations.Add("CustOrders", myDataSet.Tables["Customers"].Columns["CustomerId"], myDataSet.Tables["Orders"].Columns["CustomerId"]);
// Меняем значения поля ContactName первой строки Customer
myDataSet.Tables["Customers"].Rows[0] ["ContactName"] ="Peach";
// Добавляем строку в Customer и инициализируем поля
myDataRow = myDataSet.Tables["Customers"].NewRow();
myDataRow["CustomerId"] ="TST4";
myDataRow["ContactName"] = "New Contact Name";
myDataRow["CompanyName"] = "New Company Name";
myDataSet.Tables["Customers"].Rows.Add(myDataRow);
// Добавляем новый Заказ для данного Клиента
DataRow orderRow = myDataSet.Tables["Orders"].NewRow();
orderRow["OrderDate"] = DateTime.Now;
// Устанавливаем связь "родитель-потомок"
orderRow.SetParentRow( myDataRow, myDataSet.Relations["CustOrders"]);
// Добавляем строку в DataSet
myDataSet.Tables["Orders"] .Rows.Add(orderRow);
// Вносим изменения в базу данных
mySqlDataAdapter.Update(myDataSet, "Customers");
mySqlDataAdapterl.Update(myDataSet, "Orders");

ОРП

// Создаем новый объект DataManager и соединяемся с базой данных
// Объект Config берет данные из стандартного файла конфигурации App.config
DataManager data = new DataManager(Config.Dsn);
// Создаем коллекцию объектов Клиент(Customers) и связанных с ними Заказов и заполняем ее данными из базы данных
CustomersCollection customers = data.GetCustomersCollection (FetchPath.Customers.Orders);
// Изменяем атрибут ContactName для Клиента с CustomerId = "ALFK1"
customers.FindByCustomerID("ALFK1").ContactName = "Peach";
// Создаем нового Клиента
// В объектной модели атрибуты CustomerId и CompanyName описаны, как обязательные для заполнения
Customers customer = data.NewCustomers( "Tst7", "Olero Software, Inc.");
customer.ContactName = "Tech Support";
// Создаем новый Заказ для Клиента
Orders order = customer.NewOrders();
order.Customer = customer;
order.OrderDate = DateTime.Now;
// Вносим изменения в базу данных
data.CommitAll();

Преимущества подобного подхода очевидны:

  • рутинный код манипуляции данными в программах уменьшается в среднем на треть по сравнению с работой через классические табличные DataSet
  • в архитектуре приложения можно четко разделить слой хранения данных, обеспечиваемый СУБД, и слой прикладных объектов (или "бизнес-объектов", business objects layer).

Разумеется, за все нужно платить. ОРП является дополнительным слоем абстракций и создает накладные расходы по использованию процессора и памяти, а работа с реляционной СУБД становится в некоторых случаях неоптимальной или даже неудобной по сравнению с SQL-командами. Не следует также забывать, что SQL - это промышленный стандарт, тогда как внутренние языки запросов, используемые в ОРП, таковым не являются. Поэтому кроме объектно-ориентированной работы с данными большинство ОРП поддерживают возможность прямого использования SQL и вызовов хранимых процедур, а некоторые могут даже отображать объекты на хранимые процедуры. Данные возможности следует отнести к разряду оптимизации, сейчас мы не будем их рассматривать подробно, но если ваша БД содержит миллионы записей при одновременном доступе множества пользователей, то с большой вероятностью вам они понадобятся.

Из уже названных различий между целями использования реляционной и объектной моделей следует в частности, что если в вашей системе основной упор делается на многокритериальный поиск и массированное извлечение информации (класс информационно-поисковых систем, OLAP, генерация отчетности), то использование объектов для доступа к данным не является оправданным, а попросту излишне. Никакого различия между табличным представлением информации в базе данных, внутри вашей программы и на экране пользователя или в отчете нет, промежуточная обработка сводится к соединениям все тех таблиц и простым пересчетам значений их полей. Другое дело, если ваша система осуществляет транзакционную обработку (OLTP), сложные расчеты, оповещения о событиях, диспетчеризацию, моделирует поведение - здесь преимущества использования ОРП наибольшие.

Портрет "идеального" ОРП с точки зрения разработчика

Давайте отвлечемся немного от рутины и попытаемся сформулировать основные требования к инструменту, который бы мог нам облегчить решение задач.

  • Платформа .NET. Я не хочу обидеть поклонников Java, но в данной статье я речь пойдет только о продуктах для платформы Microsoft. Тем более, что в мире Java продукты этого класса находятся в более зрелом состоянии.
  • Интеграция с Visual Studio. Средства "малой механизации" разработки, встроенная документация и шаблоны не будут лишними.
  • Наличие среды моделирования. UML является стандартом для объектного моделирования, поэтому вместо ручного написания кода сотни классов и последующего управления этой оравой желательно иметь возможность делать это "легким движением мышки" на диаграмме классов. Но об этом чуть позже.
  • Собственно ОРП. Фреймворк, оформленный в виде библиотеки (сборки), готовый к последующему распространению вместе с вашей гениальной программой.
  • Изолированность. В идеале, ОРП должен уметь присоединиться к уже существующим набору классов и/или схеме БД не меняя ни того, ни другого. Чтобы не пришлось вам добавлять в БД новые поля, а классы в обязательном порядке наследовать от некоего "базового объекта" или, тем более, интерфейса с необходимостью его реализации.
  • Поддержка нескольких СУБД. Чтобы охватить больший круг потенциальных покупателей, ваша гениальная программа должна уметь хранить данные в тех СУБД, которые являются корпоративным стандартом Клиента.
  • Прямой и обратный инжиниринг с "ручным управлением". В идеальном варианте вы начинаете разработку с чистого листа. Тогда вам будет достаточно создать объектную модель средствами вашего любимого CASE (Rational Rose, Together, PowerDesigner...) и сгенерировать из нее схему базы данных, используя подходящую стратегию отображения (mapping) (см. Приложение 1). Это и есть прямой инжиниринг (forward engineering). Но в реальной жизни чаще всего приходится начинать с доделки/переделки уже работающей системы, когда необходимо сохранить совместимость с имеющимся программным обеспечением на уровне базы данных, не меняя ее структуры. В этом случае вам предстоит обратная задача: взяв все тот же любимый CASE провести обратный инжиниринг (reverse engineering). В обоих случаях необходима либо простая интеграция ОРП с вашим CASE (более гибкий вариант), либо наличие CASE в составе самого ОРП (менее гибкий, но зато "все в одном флаконе"). И в обоих случаях необходимо иметь возможность вручную вмешаться на любой стадии генерации/регенерации кода.
  • Желательно с коммерческой лицензией и/или солидным поставщиком (Microsoft подойдет. Шутка. Печальная, в свете задержки выхода ObjectSpaces). Я не имею целью очередной "бессмысленный и беспощадный" спор на тему open source, дело не в бесплатности и открытости кода, который доступен и в коммерческих продуктах, а в том, к кому можно обратиться за поддержкой и быстро ее получить. Linux, MySQL и Mono хороши не потому, что бесплатные, а потому, что, несмотря на бесплатность (я бы даже сказал, вопреки), существуют фирмы, вкладывающие в них миллионы и отвечающие за их выпуск. Чего нельзя сказать об абсолютном большинстве других продуктов, пасущихся на www.sourceforge.net.

Я оставил за рамками требования к качеству генерируемого кода, ресурсоемкости, скоростным характеристикам, особенностям "ленивой" загрузки объектов и кэширования, поскольку удовлетворительные ответы на них можно получить только создав и опробовав прототип с использованием данного ОРП в применении к вашим конкретным условиям задачи. Обобщению результатов каждого такого теста можно посвятить целую отдельную статью.

А мы тем временем взглянем, что нам предлагает рынок.

Краткий обзор ОРП

Длинный список ОРП (на англ.языке) вы можете увидеть на странице по ссылке: http://sharptoolbox.com/Category74089b0a-1105-4389-b1db-eedf27e20cfb.aspx и на многих других, которые можно найти посредством поиска в Интернет (google, yandex) с ключевыми словами "ORM .NET". К сожалению практически вся найденная вами информация будет на английском языке, поэтому если не хотите лишний раз напрягаться, то расслабьтесь и продолжайте чтение статьи.

Название D4Modelizer
Производитель, сайт DORLAC S.T, http://www.d4modelizer.com/
Лицензия свободно распространяемый, с открытым исходным кодом
Поддержка СУБД MS SQL Server, DB/2, Oracle, Sybase, MySQL, Access, Firebird, PostgreSQL

ОРП является простым кодогенератором, который создает набор C#-классов на основе схемы существующей базы данных. Имеется примитивный и нестабильно работающий интерфейс, где разработчик может модифицировать названия элементов, добавлять новые. Продукт сырой, создать схему существующей базы на PostgreSQL не удалось, для MSSQL была сгенерирована куча классов. Документация довольно скудная, четкой отработанной методики и контрольных примеров, уменьшающих порог вхождения нет. По всей видимости, автор создавал решение для своих нужд, а потом решил попробовать обобщить его для широкого круга.

Название Data Tier Modeler for .NET
Производитель, сайт Evaluant, http://www.evaluant.com/en/solutions/dtm/default.aspx
Лицензия Коммерческая, на установки
Поддержка СУБД MS SQL Server, DB/2, Oracle, MySQL, Access

Продукт французской фирмы представляет собой полное решение, обеспечивающее как интеграцию с существующими БД, так и создание новых на основе объектной модели. Качественная презентация, обширная документация и описание методики использования. Интегрируется с Visual Studio: отдельная закладка в project explorer служит для управления моделью. Интегрируется с существующими CASE на уровне XMI. Схема проекции описывается в виде XML, то есть при необходимости интеграции с существующей БД вы имеете возможность изменять параметры вручную. Требуется наследование от базовых классов фреймворка. Основным недостатком является схема лицензирования, при которой нужно платить за каждую установку приложения у клиента. Таким образом, данный продукт может представлять интерес скорее для внутрикорпоративных приложений, чем для тиражируемых.

Название DataObjects.NET
Производитель, сайт X-tensive, http://www.x-tensive.com/Products/DataObjects.NET/
Лицензия Коммерческая, на разработчика
Поддержка СУБД MS SQL Server, Oracle, SAP DB, Firebird/Interbase

Приятно увидеть в этом ряду продукцию отечественного производителя. ОРП поддерживает прямую и обратную разработку и весьма интересные дополнительные возможности, такие как полнотекстовый поиск (на основе MS Search для MS SQL Server или DotLucene - независимой от конкретной СУБД системы), систему безопасности схожую по принципам с NTFS, возможность выгрузки в т.н. offline layer - временное хранилище для обеспечения работы приложения вне сети с последующей синхронизацией, версионность объектов, поддержка более одного языка для значений атрибутов. Документация достаточно полная, однако презентация из 140 слайдов, которая рассчитана по словам самих разработчиков на 2 часа просмотра вряд ли может быть названа удачной находкой. Продукт нельзя отнести только к классу ОРП: по причине широкого функционала он ближе к каркасу разработки корпоративных приложений. Соответственно, если ваша задача умещается в рамки ОРП, то фреймворк такого уровня может оказаться излишне "тяжелым". Также в руководстве ничего не удалось найти на тему интеграции с CASE и UML.

Название eXpress Persistent Objects (XPO)
Производитель, сайт Developer Express, http://www.devexpress.com/products/NET/XPO/
Лицензия Коммерческая, на разработчика, исходный код доступен
Поддержка СУБД MS SQL Server, Access, MySQL, Oracle, Advanced Data Server, PostgreSQL

Компания Developer Express давно известна своим великолепным набором визуальных компонентов (eXtra grid, eXtra reports...) для сред Delphi и .NET. XPO в этом ряду выглядят не менее привлекательно. Продукт оставляет ощущение легкости, но не легковесности. Документация достаточна при своем небольшом объеме, порог вхождения для новичка низок, в поставку входит множество примеров. Интеграция с Visual Studio на уровне шаблонов и дизайнеров для компонентов. Схему проекции можно задавать как непосредственно в метаданных классов, так и в отдельном XML-файле, обеспечивая тем самым, использование одного и того же набора классов для работы с разными СУБД. Возможность проекции на хранимые процедуры (атрибуты отображаются на параметры) анонсирована в версии 2 (актуальная версия имеет номер 1.6), которая, однако, не спешит появится на свет.

Кроме функций ОРП реализована поддержка логического удаления объектов по принципу "мусорной корзины" с возможностью их восстановления. Хочется отметить хорошую поддержку как в новостной группе devexpress.public.dotnet.xpo на сервере компании news.devexpress.com, так и на форуме. Имеется обширная база знаний, на сайте опубликован ряд статей, освещающих рекомендуемые методы решения часто встречающихся ситуаций и проблем. Коллекции объектов без каких-либо ограничений подключаются как к стандартным сеткам (grids) в .NET, так и к собственным eXtra grids. Мое субъективное мнение об XPO благоприятное: с помощью документации и базы знаний прототип был собран за несколько часов, в то время как аналогичная работа для NHibernate заняла три дня, оставив больше вопросов, чем ответов. К числу недостатков можно отнести отсутствие готовых средств моделирования или интеграции с CASE, необходимость модификации БД (вручную или автоматически добавляются 2 служебные таблицы и по 2 поля в каждую таблицу). Неудачным решением мне кажется отсутствие языка запросов, условия выборки (where) конструируются путем создания массива объектов-выражений и связок.

Название LLBLGen Pro
Производитель, сайт http://www.llblgen.com
Лицензия Коммерческая, на разработчика
Поддержка СУБД MS SQL Server, Access, Oracle, Firebird/Intervase, DB2, MySQL

Продукт рассчитан в большей степени на обратный инжиниринг. Даже если вы создаете новое приложение, то рекомендуется начать процесс с проектирования базы данных, затем генератор создает вам слой прикладных объектов. В дальнейшем, вы сможете вносить изменения уже на уровне объектной модели: для этого разработчику предоставляется графический интерфейс, в котором он может добавлять/модифицировать элементы и управлять отображением (mapping). Продукт фактически создается и поддерживается единственным голландским разработчиком, в этом есть и плюсы: максимальное качество ответов на вопросы, единая непротиворечивая архитектура, так и минусы: риски остаться наедине с продуктом, если автор потеряет к нему интерес. Успешная истории использования LLBLGen нашими партнерами из Firstream (http://www.firstream.com) в достаточно большом проекте (портал финансовых транзакций типа WebMoney с широкими возможностями персонификации, 1,3 млн. клиентов, решение целиком создано на платформе Microsoft: ASP.NET, WebServices, MS SQL Server) позволяет утверждать, что продукт готов для промышленного использования в OLTP-приложениях. Интеграция с CASE отсутствует, имеется собственный интерфейс моделирования, отображающий артефакты в виде древовидных структур, что не всегда удобно по сравнению с диаграммой классов.

Название NEO (.NET Entity Objects)
Производитель, сайт http://neo.codehaus.org
Лицензия Свободно распространяемый, с открытым исходным кодом
Поддержка СУБД Любая через ADO.NET

Продукт поддерживается небольшой командой разработчиков, начальная версия была создана в 2003 году в процессе работы над программой для энергетической компании, развернутой позже во множестве филиалах, в ссылках указывается цифра 50 тысяч одновременно используемых объектов в памяти (С.Т.: видимо, имеется в виду кэш). Основной процесс разработки состоит в создании описания модели в виде XML-файла, но основе которого может быть сгенерированы как слой прикладных объектов, так и схема базы данных. К такому процессу напрашивается использование CASE вместо ручного кодирования, но никаких упоминаний об интеграции не было найдено, а своих инструментов моделирования не поставляется. Соответственно, возможен и обратный инжиниринг на основе существующей базы данных. К числу интересных возможностей также следует отнести наличие работы в отсоединенном от сети режиме, то есть поддерживается временное хранилище объектов, как и в DataObjects.NET. Декларированная работа с любой СУБД, имеющей ADO.NET-провайдер, с одной стороны является плюсом ввиду универсальности подхода. С другой стороны, по сравнению с продуктами, которые поддерживают несколько СУБД на основе абстрактного слоя и специфичных стратегий или адаптеров, взаимодействие с СУБД будет неоптимальным (фактически, это работа с чистыми DataSet вместо диалекта SQL) и не учитывающим специфику конкретной СУБД. Вдобавок, имея достаточный опыт оптимизации, я всегда настороженно отношусь к заявлениям вроде "поддержки любой СУБД".

Название ObjectMapper
Производитель, сайт MathSoft, http://www.objectmapper.com
Лицензия Коммерческая, бесплатный для некоммерческого использования
Поддержка СУБД MS SQL Server, Access

На самом деле, это не ОРП, а среда моделирования на основе UML, которая генерирует схему БД и схему отображения (mapping) для ОРП NHibernate, NPersist и Borland ECO II. Продукт пока "сырой", поддерживаются только MSSQL и Access, основной разработчик тоже один (Mats Helander). Пожалуй, это единственное на сегодня доступное средство моделирования для ОРП на .NET, поэтому выбор может стоять между "сырым", но бесплатным для внутренней разработки, ObjectMapper и совсем недешевыми CASE. В потенциале продукт может поддерживать все СУБД, которые поддерживают перечисленные выше ОРП, для которых он служит моделирующей надстройкой.

Название NHibernate
Производитель, сайт http://wiki.nhibernate.org/display/NH/Home
Лицензия Свободно распространяемый, с открытым исходным кодом
Поддержка СУБД MS SQL Server, Oracle, DB2, Access, MySQL, PostgreSQL, SQLite, Firebird

NHibernate является перенесенной на платформу .NET версией ОРП Hibernate для Java. Несомненным плюсом является зрелость своего Java-собрата по части концепций, архитектуры и алгоритмов, который с другой стороны является и минусом: "лобовой" перенос неоптимален, значит, часть кода переписывается с учетом специфики .NET, что означает новые ошибки и обычные проблемы "детского возраста". Версия для .NET по естественным причинам также обречена отставать от своего Java-аналога (в настоящее время .NET аналог соответствует версии 2.0.3, тогда как Java-разработчикам уже доступна версия 3). Поддерживается прямой и обратный инжиниринг, правда, только в ручном режиме.

В качестве недостатков следует отметить незрелость продукта, которая проявляется в скудной документации (рекомендуется читать ее полную версию для Java-аналога), плохой поддержке, отсутствии четкой и стабильной методики разработки. Весь опыт и "база знаний" пока концентрируются в нескольких разбросанных по сети статьях разработчиков, использовавших NHibernate, и паре не вполне наглядных примеров. Создание тестового приложения напоминало постоянное "хождение по граблям", при этом найти решение очередной проблемы занимало большое количество времени (многие часы поисков в сети). Никаких средств моделирования не поставляется, но в дополнительном пакете (contribution) есть утилита командной строки, которая по XML-файлу описания отображения генерирует либо схему БД, либо слой прикладных объектов.

Нельзя обойти вниманием и очевидные достоинства продукта: полный спектр поддерживаемых СУБД, язык запросов. близкий к SQL по синтаксису, отсутствие необходимости наследования от общего предка, четкое разделение кода, схемы отображения и схемы БД. Например, если вы уже имеете иерархию классов, то для отображения ее на базу данных вам не придется что-то в них изменять: NHibernate аккуратно сопоставит схему отображения с вашими классами и сохранит их в БД. При этом код уровня IL также остается нетронутым. NHibernate также отлично справляется с задачей генерации уникальных идентификаторов объектов: алгоритм (их доступно несколько) задается на уровне описания, разработчику не нужно ничего дополнительно программировать. По моему мнению, NHibernate имеет хороший потенциал для развития и через год может стать одним из лидеров рынка.

Название Versant Open Access .NET (VOA)
Производитель, сайт Versant, http://www.versant.com/products/openaccess/dotnet
Лицензия Коммерческая, на разработчика
Поддержка СУБД MS SQL Server, Oracle, FastObjects

Этот продукт также является переносом на платформу .NET своей одноименной Java-версии. Поддерживается прямой и обратный инжиниринг. Хорошо интегрированный с Visual Studio, он позволяет визуально управлять (mapping designer) отображением и генерацией/модификацией схемы БД прямо из среды, однако, сами прикладные классы необходимо либо изначально писать вручную, либо генерировать из вашего любимого CASE, либо генерировать с помощью самого VOA на основе существующей базы данных.

К числу недостатков следует отнести небольшой спектр поддерживаемых СУБД, проблемы с управлением генерацией уникальных идентификаторов объектов (не поддерживается GUID), модификация кода ваших объектов на IL-уровне. Тем не менее, продукт активно развивается, на сайте опубликованы планы по устранению проблем в новой версии до конца этого года, но уже для платформы .NET 2.

Выводы

По естественным причинам, связанным с более поздним появлением платформы .NET, основные стандарты и решения вырабатываются в мире Java и переносятся в .NET. Поэтому на сегодняшний день рынок ОРП для .NET незрелый, имеется "зоопарк" продуктов, зачастую выполненных на уровне студенческих дипломных проектов. Большинство ОРП поддерживают MS SQL Server и Access, работа с другими СУБД вызывает определенные проблемы. Интеграция со средствами моделирования находится в состоянии зародыша. Среди лидеров пока еще неоформившегося рынка хотелось бы выделить Versant Open Access, XPO, LLBLGen Pro, DataObjects.NET и NHibernate. Использование перечисленных в статье средств может в некоторых случаях существенно уменьшить ваши затраты на разработку и внесение изменений, но риски, связанные с выбором инструмента, пока остаются высокими.

Приложение 1. Стратегии отображения, основные понятия и термины

Можно выделить три основные стратегии отображения объектов на реляционную схему БД. Подробнее см. статью А.Усова "ООП о реляционной модели" (http://www.alexus.ru/russian/articles/dbms/oop_rm/page02.htm#defects)

  1. Хранение всех атрибутов в одной таблице
  2. Группировка общих атрибутов в одной таблице и разнесение уникальных атрибутов (подклассов) по связанным таблицам
  3. Представление каждого класса в виде отдельной таблицы

Стратегия 1 не относится собственно к реляционной модели и может рассматриваться разве что как средство оптимизации. Стратегии 2 и 3 используются CASE-средствами проектирования базы данных (ErWin, PowerDesigner) и обозначаются как "complete/incomplete subtyping" со схемой "roll-up" (стратегия 2, атрибуты подкласса в новой таблице) и "roll-down" (стратегия 3, наследуемые атрибуты добавляются к таблице суперкласса).

В ОРП, обеспечивающих выбор стратегии генерации схемы БД (например, JDO) обозначения несколько иные. Стратегия 1 называется "плоским отображением" (flat mapping), остальные стратегии реализуются либо при помощи "вертикального отображения" (стратегия 2, vertical mapping), либо "смешанного отображения" (стратегии 2 и 3, mixed mapping).

Сергей Тарасов, июнь 2005

Статья также опубликована в журнале ZDnet.ru

Оценка: 4.8 (Голосов 12)

Настройки просмотра комментариев

Выберите нужный метод показа комментариев и нажмите "Сохранить установки".

Виталий, попробую ответить кра

Виталий, попробую ответить кратко.
Ситуацию с ОРП я не отслеживал год, сейчас глянул по ссылкам: есть заментный прогресс, но в целом, как мне показалось, ситуация не изменилась.

Выбор может находиться в плоскости "лучший из бесплатных - лучший из коммерческих", и, как мне кажется, ситуация здесь сохранилась: NHibernate и Versant.

Хибернатор куплен JBoss, точнее, куплен его ведущий разработчик (русский. кстати, Сергей Кощеев), теперь он занимается развитием продукта за счет фирмы. Вот официальная новость на эту тему.
Вышла релиз-версия. Я изучал его еще в бета 0.7 и качество уже было приемлемым.
Минусы понятны: мало документации, фактически нет поддержки.
Для меня это перевешивается огромными плюсами: свободной лицензией, открытой архитектурой, отсутствием необходимости менять существующие классы или БД - все можно интегрировать на уровне описаний отображения.

Версант также выпустил новую версию под .NET 2. Этот продукт интересен тем, что растет из полноценной объектной СУБД, а не самоделки, как вы правильно заметили, некоей фирмы, сделанной когда-то для своих нужд.
Но за все надо платить :)

В дополнение, чтобы не генерировать схемы маппинга руками я бы рекомендовал использовать описанную мною методику "Разработка на основе моделей", там есть и пример скрипта для генерации. Необязательно использовать относительно дорогой PowerDesigner (1000 евро за лицензию), можно сделать аналогичный скрипт для какого-нибудь Microsoft Visio, а то и даже бесплатного Argo UML.

С другой стороны хотел бы предостеречь от чрезмерного увлечения ОРП :)
Если перед вами стоит задача класса информационной системы, то есть логики мало, а информации и запросов к БД много (примерно 20% на 80%) то необходимость использование ОРП становится совершенно неочевидной, наоборот, неизбежно возникнут серьезные проблемы как с производительностью, так и с написанием запросов даже средней сложности, которые легким движением руки пишутся на SQL.

Наконец, есть и более экзотичное решение - Реализация сервера объектного представления средствами реляционной СУБД

Ну батенька, после стольких-то

Ну батенька, после стольких-то подготовок... ;-)

Про черезмерное увлечение ORM понятно. :-)
Это не серебрянная пуля, а всего лишь инструмент.
Логика на проекте приличная. 10 тыщ строк хранимых процедурок.

Не вполне понял ваше неудовлетворение документацией по NHibernate
Вот документация на предыдущую версию http://www.hibernate.org/hib_docs/nhibernate/html/
В поставке к текущему релизу бетты документация подправлена в соответсвии с изменеиями в релизе.
Вот, до кучи, документация по Hibernate, но зато по русски
http://www.hibernate.org/hib_docs/reference/ru/html/
На форуме поддерживают терпимо, если вспомнить цену поддержки - твердая "тройка".

Выяснил недавно касательно Versant-ов...
Есть две конторы:
Versant - выпускает FastObjects .NET: http://www.versant.com/en_US/products/fastobjects. Это полноценная объектная СУБД для .NET. Дорогая - капец.
И Vanatec - дочерняя контора, продает собтсвенно ORM OpenAccess: http://www.vanatec.com/en/product-information
По всей видимости, вещь хорошая, и не очень дорогая. Но после агрессивного черного пиара на google addwords ('Stop HIBERNATING!' etc), вещь ушла на последнюю позицую в моем списке. :-) Идиоты, блин в команде маркетинга. Надо будет написать в тех поддержку, шоб там кого уволили нафиг. :-)))

Лано, буду копать дальше. Про подшивку UML дизайнеров, и про все такое.

По поводу статей - извиняйте. Теорией Конг-Фу интересуюсь не очень. Мне-бы освоить хук слева, да аперкот снизу. :-)

Виталий, благодарствую, конечн

Виталий, благодарствую, конечно, за лестное сравнение уровня статей с теорией кунг-фу, но они вряд ли дотягивают выше описаний тактических приемов боя, если уж придерживаться аналогий. Поскольку вас интересуют конкретные техники ударов, то в первой статье есть главка "Краткое описание цикла внесения изменений", где они и приведены :)

Мне же ваша аналогия понравилась, поэтому я ее еще попользую :)

Про логику проекта небольшая ремарка - 10 тысяч это может быть и очень мало. Например, если у вас всего 100 таблиц по 10 столбцов в среднем, и для каждой сгенерирован CRUD-набор хранимых процедур, то тогда эта часть кода занимает 5-7 тысяч строк. Это я не к тому, что сомневаюсь в правильности вашей оценки, а к тому, что она может быть по-разному интерпретирована.

Документация по хибернатору - больная тема.
Да, есть некий номинальный набор описаний, как раз уровня "теории кунг-фу". Этот набор должен быть у любого продукта, но он является необходимым, а не достаточным. Документация же для входа в систему строится на простых типовых примерах "проблема-решение". Дальше, если типовые примеры недостаточны, то клиент лезет в высокую теорию, если имеет достаточный уровень образования, конечно. Порог повышается. Отсутствие документации с типовыми примерами выливается в а) необходимость предварительного изучения специалистом высокой квалификации б) передачу его опыта специалистам смежной или просто более низкой квалификации. Все это происходит не за 1 день, а за недели и месяцы. Что, собственно, происходило и со мной.
Необходимо, постоянно наступая на грабли, сделать типовые примеры вроде "мастер-деталь", "несколько объектов в одном гриде", синхронизация, lookup-ы и прочее. Я это делал еще в 2003 студии. В .NET 2 появился BindingContext и DataGridView, которые упрощают перечисленные задачи, но не исключают необходимость их решения. С другой стороны, несколько недель ушло на правильную генерацию маппинга из UML.

То есть надо быть готовым к тому, что первые результаты, пригодные для промышленного (боевого) применения у вас появятся через несколькуо недель. Лучше, конечно, чтобы и начальство было в курсе. Возможно, оно решит, что ваша зарплата за этот срок гораздо выше, чем стоимость лицензии у коммерческих продуктов. Такая вот петрушка...

В статье я сравнивал с уровнем входа для XPO и Versant, это, как говорится, день и ночь.

По поводу Versanta - посыпаю голову пеплом, поскольку "слона я не заметил". Да, версант с марта 2006 теперь зовется Ванатеком. Но это, к счастью, ничего не меняет в моих оценках. Поддержку LINQ они уже сделали в версии 4.3.
В свое время я общался с представителем версанта на форуме RSDN, вот ссылка на обсуждение.

По поводу NUnit у меня большие сомнения в возможности его использовать в том виде, который вы упомянули.
Собственно, тестировать там нечего - get/set и коллекции, к тому же сгенерированные автоматически, а если проверять в связке с БД, то это уже не модульный тест, а интеграционный/системный, поэтому NUnit здесь не к месту.
В примерах "опытных собаководов" (например в LLBLGen Pro) DAL генерируется и не трогается программистом. Ваша логика идет уровнем выше в BL - это классы, производные от DAL либо просто сервисы (контейнеры) которые обрабатывают данные, предоставляемы DAL. Вот там-то и возникают модульные тесты.

Про документацию к NHibernate.

Про документацию к NHibernate.
Мастер-деталь отношение победилось довольно быстро.
Ответы на все вопросы, которые я не нашел в доках, были найдены на форуме. Ну и датабиндинг в .NET 2 конечно помог. Наверное, за год, ситауция немного изменилась к лучшему.
Попробую Ванатек, и возможно увижу разницу, о которой вы говорите.

То есть надо быть готовым к тому, что первые результаты, пригодные для промышленного (боевого) применения у вас появятся через несколькуо недель. Лучше, конечно, чтобы и начальство было в курсе. Возможно, оно решит, что ваша зарплата за этот срок гораздо выше, чем стоимость лицензии у коммерческих продуктов. Такая вот петрушка...

Как я уже писал, начальство не против раскошелится уже сейчас. :-)
Вот и выбираю.

Спасибо за ссылку на дискурс по ORM - почитаю.

По поводу NUnit у меня большие сомнения в возможности его использовать в том виде, который вы упомянули.
Собственно, тестировать там нечего - get/set и коллекции, к тому же сгенерированные автоматически, а если проверять в связке с БД, то это уже не модульный тест, а интеграционный/системный, поэтому NUnit здесь не к месту.

На мой взгляд, не обязательно что бы DAL слой должен был отделен от BL. Если база данных в вашем полном распоряжении, и доступна для рефакторинга, а еесли разработчик DAL и BL един в двух лицах. В таком случае, я не вижу никакой нужды для этого разделения. А стало быть, и вопрос применения тестов к persistent объектам имеет место быть.
Вот от того, на мой взгляд, и различаются системы генерирующие прокси классы от абстрактных классов, или от классов BL.

Genome - пример ORM для разработки DAL слоя из абстрактных классов. Мой вопрос: Could you please explain how it could be possible to use NUnit tests for persistent objects with Genome?
Совершенно не смутил разработчика, но полюбуйтесь какие расскошные методы он насоветовал:

Depending on the structure of your business logic you want to test, there can be different options. In general you can choose from three different ways (maybe there are others too):

1. Mock the database.
In the unit test setup, you initalize a test database (or a part of it) and insert the mock test data into that. You call the business logic that will load up the data from the database and perform the required action. You can check the returned values or the final state of the database.
This solution requires the less effort to make your classes testable. This is rather an integration test as it also test the full solution including the database interactions (this can be an advantage and a disadvantage too). You will need a database connection to run these tests and you have to consider that preparing the mock database for the test case might need significant time, slowing down your tests.

2. Mock your persistent classes.
As the persistent classes are abstract classes, you can write (or use a tool to write) derived mock classes to support the unit tests. If your business logic is interface based (and your business classes anyway implementing these interfaces) than you can create mock interface implementations too.
The main disadvantage of this solution is that writing the mock classes can be a huge amount of work.

3. Use mock Genome context
You can also create a new long running transaction context (LocalContext.Create) for the tests which you will never commit. In the unit test setup, you can create and initialize the objects to be tested in this context and pass them to the business methods.
As you never commit this context, you will not need a database to perform the tests, however of course you have to extract all the querying logic to mockable services to ensure that your test also do not querying the database.

C NHibernate, например, все гораздо проще.

Экий вы хитрый, с хибернатором

Экий вы хитрый, с хибернатором вам легче, потому что он не требует наследования от базовых классов, а по сути предоставляет сервис для сохранения объектов.
А сами не хотите отделять DAL от BL - собственно бизнес-сервисов/правил :)

Разумеется, это не догма.

Но по мне гораздо логичнее не добавлять в классы из DAL новой логики, а реализовать ее на уровне контейнеров.
Простой пример: вмсето того, чтобы делать ересь :) в виде
Товар.Отгрузить()
У вас будет
Отгрузчик.Выполнить(Товар)
Причем отгрузчик не является хранимым классом.

Представьте, например, что вы сделали учет на складе. И продолжаете его развивать.
Потом пришел еще один программист и начал делать магазин.
В случае выделенного BL у вас будут небольшие пересечения. Вы сможете их контролировать. В случае смешанных DAL + BL он получит полный доступ к вашим классам.

Ну да ладно, сами разберетесь, а то советовать все горазды ;)

Genome, конечно, шикарные рекомендации выдвинул. Но это уже не модульные тесты. Это полноценная интеграция, с наполнением БД, с соединением и т.д. Тут NUnit может использоваться только для решения части задачи.

Попробуете Vanatec - напишите, какие впечатления. Я к этому продукту неравнодушен, поскольку выбор в пользу хибернатора тогда был сделан по одной причине: "Раз бесплатно, значит хорошо".

Экий вы хитрый, с


Экий вы хитрый, с хибернатором вам легче, потому что он не требует наследования от базовых классов, а по сути предоставляет сервис для сохранения объектов.
А сами не хотите отделять DAL от BL - собственно бизнес-сервисов/правил :)

Что-то я не уловил вашей мысли. Соответсвенно, и соли шутки. :-)

Разумеется, это не догма.

Но по мне гораздо логичнее не добавлять в классы из DAL новой логики, а реализовать ее на уровне контейнеров.
Простой пример: вмсето того, чтобы делать ересь :) в виде
Товар.Отгрузить()
У вас будет
Отгрузчик.Выполнить(Товар)
Причем отгрузчик не является хранимым классом.

Опять, вынужден для себя уяснить, что обладаю пониманием теории марксизма/ленинизма лишь на начальном уровне. товар->деньги->товар, или деньги->товар->деньги. По мне дак лучше: деньги->Деньги->ДЕНЬГИ. :-)))
А, серьезно, то не очень понял, почему у меня будет именно Товар.Отгрузить() ? Почему станет именно Отгрузчик.Выполнить(Товар)? В зависимости от контекста, вариантов-то масса: МестоХранения1.Отгрузить(Товар, МестоХранения2), Отгрузка(Товар, МестоХранения1, Покупатель), Товар.Перевести(МестоХранения, ТорговыйЗал) + Покупатель.Купить(Товар),... Что вы хотели тут сказать? Что объект BL может и не быть as persistent? Ну дак это, извиняюсь, и ежу понятно. :-) Любые объекты - действия, являются подобным примером. Хранить-то как бы и нечего.

Представьте, например, что вы сделали учет на складе. И продолжаете его развивать.
Потом пришел еще один программист и начал делать магазин.
В случае выделенного BL у вас будут небольшие пересечения. Вы сможете их контролировать. В случае смешанных DAL + BL он получит полный доступ к вашим классам.

Во! Это я понять в состоянии. Только я опять не вижу прелести расслоения бизнес логики в сторону DAL. Этому второму программисту потребуется список товаров. А больше и нет никакого пересечения по данным. Что (или кого) вы собрались контролировать, как, и самое главное - зачем? По namespace-ам рассовывать классы BL что ли? Программирование [DAL<-> Склад]: [DAL <-> Магазин] потребует сверхусидчивости, а геморрой не за горами. Или вы про пересечения по бизнес логике?
Типа, если мы продаем товар, то это коснется обоих частей системы. Ну дак и чем тут слияние DAL+BL помешает?
Ну, еще программисту потребуются цены товаров (если их еще не было определено). Придется добавлять поле в таблицу товаров и переписывать класс Товар (или в случае разделение, то BL+DAL классы: собственно класс Товар из BL + соответствующие классы из DAL поднимающие данные для Товар и Магазин) А вот если вдруг искомый склад был складом российской аптеки, то бойтесь! Ибо там товары хранятся по входным ценам (аптеки работают по фиксированной торговой надбавке без усреднения) Тогда вам придется все "складовские" классы перетрясать, фифо/ лифо, и прочие прелести реализовывать. Тут и до переделки формочек дело может дойти. Но и тут прелесть разделения BL – DAL видится с трудом.
А если цена на каждый товар одна, то где затык с параллельным развитием систем склад и магазин? И в чем тут прелесть выделения DAL? Нормальный ORM даст вам список товаров по запросу с критериями. И сумму по кол-ву получить не проблема. Это конечно, если они вам понадобятся. Но в данном примере, их даже и не надо. Может быть, только что для проведения ревизии по складу.

Я вот, блин заметил три фазы осмысления ORM:
1. Настраиваем мапинг объектов BL нашей тестовой предметной области.
2. Пытаемся изобразить их на вин формочках или на ASP.
После успешного выполнения первых двух пунктов, и непродолжительной паузы, идет следующий шаг:
3. Надоть создать DAслой!
А вот кому надо? И зачем оно надо? Почему трех слоев не хватает?
Лично мне, причиной возникновения задачи (3) видится засоренность мозгов рекламой. Эту волну гонят не технари - продавцы. Им продавать продухты надо. Чем больше туману в головах у покупателей, тем более покупатели сговорчивы. Берут охапками сервера приложений и все такое. На лукавую составляющую програмера давят – охота ему сделать, что ин то на века и универсальное. Даром книжки с названием «Рефакторинг» выпускают.
(3) должно бы звучать как «попробовать сие на, хоть и тестовом, но реальном проекте»

Реально, разделение BL-DAL надо для сложных задач, когда рефакторинг БД затруднителен (например потому, что эту базу юзает еще 5 приложений, и не вы их разработчик). Или у вас так много програмеров, что вы начали уже коллектив расслаивать: Как ни соберутся в курилке BL да DAL програмеры, и все без драки не обходится.. (Те, которые по логике представления, все больше к стеночке жмутся.) :-))

Genome, конечно, шикарные рекомендации выдвинул. Но это уже не модульные тесты. Это полноценная интеграция, с наполнением БД, с соединением и т.д. Тут NUnit может использоваться только для решения части задачи.

Да нормальные рекомендации. Про то, что гланды резать нужно. Но с геномом получится только через зад и автогеном :-) База данных подразумевалась тестовая. А в последнем способе трик, как без базы обойтись вообще.
Прикол в том, что бизнес логику вы можете написать на уровне этих абстрактных классов. А вот с тестами извиняйте.

Я имел в виду вот что.Хиберн

Я имел в виду вот что.
Хибернатор работает по принципу сервиса хранения. То есть
Сервис.СохранитьОбъект(объект)
Есть другие подходы, когда логика хранения реализуется в базовыхх классах, от которых надо наследовать свои классы DAL. Тогда получаем что-то вроде
объект.Сохранить()

Аналогичная ситуация и с разделением DAL и BL.
насколько я понял, вы хотите часть логики реализовать в DAL-классах, а потому вас беспокоит проблема тестирования. Ведь в сочетании со вторым походом (наследование от базовых классов ОРП) это дает серъезные проблемы в организации тестирования.

Если это не так, прошу меня поправить, чтобы я более точно понимал ситуацию.

Я предлагал по сути примерно такое же деление на сервисы, которые обрабатывают DAL-объекты. Сами сервисы при этом не являются долговременными, они реализуют логику.

Я имел в виду вот


Я имел в виду вот что.
Хибернатор работает по принципу сервиса хранения. То есть
Сервис.СохранитьОбъект(объект)
Есть другие подходы, когда логика хранения реализуется в базовыхх классах, от которых надо наследовать свои классы DAL. Тогда получаем что-то вроде
объект.Сохранить()

В последней версии хибернатора (в вашей нотации) возможно следуещее:
объект1 = Сервис.ПолучитьОбъект(...)
объект2 = Сервис.ПолучитьОбъект(...)
объект1.ОбъектыДети[3].АтрибутСтрока = "все путем";
объект2.АтрибутЧисло = 3.1415926;
объект1.АтрибутДата = DateTime.Now;
if(ПогодаХорошая)
Сервис.Commit();
else
Сервис.Rollback();

Что означает, что разработчики хибернатора вполне реализовали паттерн ЕдиницаРаботы - UnitOfWork http://www.martinfowler.com/eaaCatalog/unitOfWork.html

В DAL классах я собираюсь реализовывать *всю логику*. До того самого момента, пока это не станет невозможным.

Ладно, Сергей. Дискутировать в общем-то не о чем.
Настоятельно рекомендую книжицу к прочтению http://www.books.ru/shop/books/156126 Ответы на большинстов ваших вопросов там.
Спасибо за рекомендацию на ванатек, и за ссылку на дискурс по ОРМам.

За сим отшаркиваюсь.

Любопытный пример.Не могли б

Любопытный пример.
Не могли бы ткнуть ссылкой на место в документации?
Сходу вижу только старые варианты, типа

parent.AddChild(child);
Child newChild = new Child();
parent.AddChild(newChild);
session.Update(parent);
session.Flush();

Любопытный пример.


Любопытный пример.
Не могли бы ткнуть ссылкой на место в документации?
Сходу вижу только старые варианты, типа

parent.AddChild(child);
Child newChild = new Child();
parent.AddChild(newChild);
session.Update(parent);
session.Flush();

9.4. Updating objects
9.4.1. Updating in the same ISession

DomesticCat cat = (DomesticCat) sess.Load( typeof(Cat), 69L );
cat.Name = "PK";
sess.Flush(); // changes to cat are automatically detected and persisted

ок, спасибо за ссылку.К со

ок, спасибо за ссылку.

К сожалению, не нашел подробности реализации.
Полагаю, поскольку интерфейсов извещений объект не реализует, хаков вроде нет, а необходимым условием является привязка к сессии, значит сессия хранит ссылку на объект и его состояние, как минимум.
При массированных операциях памяти может не хватить.
Хотя массированные операции, это уже для сиквела.

Да, для Delphi (Win32) есть не

Да, для Delphi (Win32) есть немало ОРП, навскидку вспомню InstantObjects, Bold (теперь ECO), OPF

.net ORM mapping

ПРЕВЕД!
Пока вы тут выбираете какой мапер лучше я свой накалякал :)
http://www.codeplex.com/easypersist
вот!

Тоже вариант

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

Теперь LINQ всех

Теперь LINQ всех их убьет
Не потому, что лучше, а потому, что Microsoft

А броузером вы каким пользуетесь?

А броузером вы каким пользуетесь? IE? А я вот нет. А мессенжером msn? Я вот по старинке icq. А intellisense в студии родной или Resharper? А unit-тесты встроенные или nunit? А сколько народу на cvn и subversion сидит и не думает на vss/tfs переходить? ...