Pull to refresh

Comments 16

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

Каким образом?

Одна из проблем при прямом отображении классов в таблицы: отображение атрибутов, имеющих множественный характер, требует отдельных таблиц.

А почему это проблема?

таблица, как долговременное хранилище состояний объекта, имеет вполне адекватный образ в любой развитой объектно-ориентированной среде, а именно — коллекцию (Collection).

Ага, на этом факте основано представление в любом ORM. Так что не понятно, почему вы умудряетесь на этот факт «опирать» другую парадигму.

Рисунок 3

… и тут мы видим нормальную такую документо-ориентированную БД. Иии?
… и тут мы видим нормальную такую документо-ориентированную БД. Иии?

Вот я тоже не понял, в чём суть то CoRM?

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

Каким образом?

(не исчерпывающе, но в частности). Инкапсуляция заставляет существующие реализации получать весь кортеж простых атрибутов состояния объекта всякий раз при доступе к объекту (в некоторых случаях — еще и атрибуты из связанных таблиц). При обширном количестве атрибутов, это существенно увеличивает трафик данных между БД и приложением, заметно снижая общую эффективность приложения по сравнению с «чистым» SQL.

Одна из проблем при прямом отображении классов в таблицы: отображение атрибутов, имеющих множественный характер, требует отдельных таблиц.

А почему это проблема?

Это оказывается проблемой, когда природа приложения такова, что данные множественного атрибута не имеют самостоятельной ценности без владеющего ими объекта. Например — документ и его строки.
В этом случае, вторая таблица становится ненужной с той точки зрения, что по ней приложение не осуществляет никаких глобальных запросов, поэтому ее наличие в базе только утяжеляет ее, с точки зрения наличия индексов на таблице строк например.

таблица, как долговременное хранилище состояний объекта, имеет вполне адекватный образ в любой развитой объектно-ориентированной среде, а именно — коллекцию (Collection).

Ага, на этом факте основано представление в любом ORM. Так что не понятно, почему вы умудряетесь на этот факт «опирать» другую парадигму.

Не совсем. «Любой» ORM почему-то склонен считать, что в программе будет ровно по одной коллекции для хранения состояния объектов каждого из классов, входящих в отображение.

Рисунок 3


… и тут мы видим нормальную такую документо-ориентированную БД. Иии?

Документ-ориентированные СУБД, исходя из их общего описания, например MongoDB, опираются на «монолитное» хранение набора хранимых атрибутов, как простых, так и имеющих множественный характер, допускают хранение произвольных наборов простых и сложных атрибутов в любых коллекциях, индексируют отдельные атрибуты. Это действительно, очень похоже на CoRM в той части, которая отображена на рисунке. Вероятно, я должен был в исходном посте сослаться на них, как на один из прототипов.

Различий, тем не менее, есть здесь несколько.
Во-первых, в отличие от MongoDB и других документ-ориентированных СУБД, гордо относящих себя к NoSQL, CoRM не отказывается от использования некоторой разновидности SQL. Более того, полностью наследуются все преимущества использования реляционных отношений из нижележащего хранилища SQL.
Во-вторых, в отличие от MongoDB, CoRM не пытается воспроизвести собственный движок СУБД, а опирается на существующие СУБД, что дает значительно более обширные возможности, связанные например с совместимостью в гетерогенной среде разработки.
В-третьих, документ-ориентированные СУБД занимаются хранением состояния объекта и запросами к этому состоянию, в то время как у CoRM хранение состояния поручено нижележащему движку SQL, а сам CoRM осуществляет сокрытие деталей хранения, обеспечивая корректное преобразование хранимого состояния в объект заданного класса и обратно.
Инкапсуляция заставляет существующие реализации получать весь кортеж простых атрибутов состояния объекта всякий раз при доступе к объекту (в некоторых случаях — еще и атрибуты из связанных таблиц).

Не заставляет. Как раз наоборот, инкапсуляция позволяет объекту получать свое состояние из БД таким образом, каким ему нужно.

ее наличие в базе только утяжеляет ее

Реальные цифры есть? Насколько БД «утяжеляет» лишняя таблица (по сравнению с размещением этих данных в какой-нибудь другой таблице).

«Любой» ORM почему-то склонен считать, что в программе будет ровно по одной коллекции для хранения состояния объектов каждого из классов, входящих в отображение.

Это утверждение неверное. Как минимум есть Entity Framework, который так не считает.

Более того, полностью наследуются все преимущества использования реляционных отношений из нижележащего хранилища SQL.

Я в этом не уверен.

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

Вы уверены, что это достоинства, а не недостатки? В частности, что у вас нет потерь производительности на лишнем уровне преобразований?
Инкапсуляция заставляет существующие реализации получать весь кортеж простых атрибутов состояния объекта всякий раз при доступе к объекту (в некоторых случаях — еще и атрибуты из связанных таблиц).

Не заставляет. Как раз наоборот, инкапсуляция позволяет объекту получать свое состояние из БД таким образом, каким ему нужно.

По крайней мере, на примере конкретно Django ORM, SQLAlchemy или Elixir — еще как заставляет. Я не говорил и не говорю о том, что это обязательно должно случаться, поэтому в частности, в исходном посте отметил, что это обстоятельство, наблюдаемое на существующих ORM, с которыми мне пришлось иметь дело (см. выше), возможно, исходит из теоретических противоречий. Данный вопрос требует отдельной серьезной проработки. Тем не менее, исходя из того, что ООП подходит к объекту в целом, как к монолитному конструкту, в то время как «запись» или «кортеж» в реляционной СУБД — это открытый и гибкий по составу набор значений, можно отметить некоторое противоречие при прямом отображении объекта и кортежа друг на друга.

Реальные цифры есть? Насколько БД «утяжеляет» лишняя таблица (по сравнению с размещением этих данных в какой-нибудь другой таблице).

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

«Любой» ORM почему-то склонен считать, что в программе будет ровно по одной коллекции для хранения состояния объектов каждого из классов, входящих в отображение.

Это утверждение неверное. Как минимум есть Entity Framework, который так не считает.

Именно поэтому «любой» я поставил в кавычки. Впрочем, я все равно попрошу ссылку на то, какой именно фреймворк Вы имеете в виду.

Вы уверены, что это достоинства, а не недостатки? В частности, что у вас нет потерь производительности на лишнем уровне преобразований?

Прямое сравнение MongoDB и CoRM по этим параметрам — не совсем корректно, поскольку во-первых, они решают сходные, но не идентичные задачи, а во-вторых — MongoDB имеет место быть, в то время как CoRM только еще проектируется.
Тем не менее, исходя из того, что ООП подходит к объекту в целом, как к монолитному конструкту, в то время как «запись» или «кортеж» в реляционной СУБД — это открытый и гибкий по составу набор значений, можно отметить некоторое противоречие при прямом отображении объекта и кортежа друг на друга.

… противоречие, которое вы разрешаете, уничтожая гибкость набора значений в СУБД, да. В то время как современные ORM (в частности, тот же EF) прекрасно позволяют выбрать из объекта только нужные поля.

Исходя из обычной арифметики, можно отметить [...] Конкретные цифры вы можете получить самостоятельно на любом движке SQL.

Это означает, что вы пока не столкнулись с реальными проблемами, связанными с этим оверхедом. Я их тоже, знаете ли, никогда не видел.

Впрочем, я все равно попрошу ссылку на то, какой именно фреймворк Вы имеете в виду.

Я, вроде бы, явно написал: Entity Framework.

Прямое сравнение MongoDB и CoRM по этим параметрам — не совсем корректно, поскольку во-первых, они решают сходные, но не идентичные задачи

И в чем же различие?
Исходя из обычной арифметики, можно отметить [...] Конкретные цифры вы можете получить самостоятельно на любом движке SQL.


Это означает, что вы пока не столкнулись с реальными проблемами, связанными с этим оверхедом. Я их тоже, знаете ли, никогда не видел.

Это означает, что мне лениво поднимать соответствующий тест и проводить формальные замеры. Я не только столкнулся с этой проблемой лоб в лоб, мне пришлось ее решать способом даже более кардинальным, чем я здесь описываю. В частности, мне вовсе пришлось отказаться от попытки как-либо использовать ORM непосредственно при первичной обработке данных, а при анализе — прикручивать сбоку наляпки разного рода. Именно поэтому в частности, я начал думать о чем-то таком, что я здесь описал.
В данной статье у меня не было желания подробно разбирать ORM с точки зрения производительности например. Если желаете, можете ознакомиться со статьей вот здесь, где я подробно описываю, с конкретными замерами и цифрами, как именно отдельные ORMы сами по себе, просто в силу своего применения, тормозят исполнение кода и расходуют ресурсы, а также вот здесь, где я предлагаю наляпку на Django ORM для прикручивания и кеширования параметризованных запросов, ускоряющую выполнение кода в несколько раз. Правда там нет того конкретного юзкейза, который мы обсуждаем здесь (запись-владелец и набор записей-деталей). На написание и проведение тестов, которые приведены в каждой из статей, я потратил не менее одного рабочего дня и еще немножко. Вы при желании, можете потратить примерно столько же, чтобы убедиться, прав я или не прав. Но прежде чем заявлять здесь что я неправ, неплохо бы было именно Вам провести подобные тесты.

Я, вроде бы, явно написал: Entity Framework.

Это вот этот что-ли? msdn.microsoft.com/ru-ru/library/bb399572.aspx

В таком случае вот здесь msdn.microsoft.com/ru-ru/data/jj591617 описаны юзкейзы различного использования маппинга:
— Specifying That a Class Is a Complex Type
— Specifying Not to Map a CLR Entity Type to a Table in the Database
— Mapping a CLR Entity Type to a Specific Table in the Database
— Mapping the Table-Per-Hierarchy (TPH) Inheritance
— Mapping the Table-Per-Type (TPT) Inheritance
— Mapping the Table-Per-Concrete Class (TPC) Inheritance
— Mapping CLR Properties of an Entity Type to Multiple Tables in the Database (Entity Splitting)
— Mapping Multiple Entity Types to One Table in the Database (Table Splitting)

Я что-то не вижу здесь возможности отобразить один и тот же класс так, чтобы он хранил часть объектов — в одной таблице, а часть — в другой (Entity Splitting как я понимаю, позволяет разделять один объект на два, мапируя их на разные таблицы, это не то, ну а остальные варианты только сокращают количество коллекций), а это и было одним из описанных преимуществ CoRM.

Таким образом, мое предыдущее утверждение о том что <<«любой» ORM почему-то склонен считать, что в программе будет ровно по одной коллекции для хранения состояния объектов каждого из классов, входящих в отображение>>, остается верным, с точностью до «ровно», которое надо разумеется, заменить на «не более чем» (последнее верно и для других ORM). Ну и конечно же, «склонен» формально не означает, что так оно и есть во всех случаях.

Прямое сравнение MongoDB и CoRM по этим параметрам — не совсем корректно, поскольку во-первых, они решают сходные, но не идентичные задачи

И в чем же различие?

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

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

В этом и состоит различие. CoRM — не движок датабазы, а лишь маппинг, а MongoDB — движок, поэтому производительность движка и производительность CoRM — разные, хотя и взаимосвязанные вещи. Кроме того, результатом работы самой MongoDB является в чистом виде структура данных, что требует навешивания сверху дополнительных слоев, реализующих поведение объекта, см здесь. Поэтому корректное сравнение должно сопоставлять производительность ORM-ов, представленных на указанной ссылке, с производительностью библиотеки CoRM на различных движках SQL, буде таковая библиотека разработана.
Но прежде чем заявлять здесь что я неправ, неплохо бы было именно Вам провести подобные тесты.

У меня с «подобными тестами» все очень просто: у меня есть система с традиционным ORM, и я не вижу в ней никаких проблем, вызванных оверхедом из-за лишних таблиц для представления сущностей внутри аггрегатов.

Я что-то не вижу здесь возможности отобразить один и тот же класс так, чтобы он хранил часть объектов — в одной таблице

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

Таким образом, мое предыдущее утверждение о том что <<«любой» ORM почему-то склонен считать, что в программе будет ровно по одной коллекции для хранения состояния объектов каждого из классов, входящих в отображение>>, остается верным, с точностью до «ровно», которое надо разумеется, заменить на «не более чем» (последнее верно и для других ORM). Ну и конечно же, «склонен» формально не означает, что так оно и есть во всех случаях.

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

Поэтому корректное сравнение должно сопоставлять производительность ORM-ов, представленных на указанной ссылке, с производительностью библиотеки CoRM на различных движках SQL, буде таковая библиотека разработана.

Именно об этом я и говорю. Хотя бы в качестве PoC.

Кстати, неоднократно замечено, что при сложных бизнес-слоях не рекомендуется ставить бизнес-логику напрямую в те объекты, которые отображаются на БД, а использовать их в качестве DTO.
Интересная концепция. Хотелось бы увидеть псевдокод на любом распространенном языке — как это будет выглядеть?

Я лично для себя принял такие правила (и реализовал их в своем велосипеде, куда без этого):
— запись в таблице — это объект
— вся таблица — это класс
— набор записей — коллекция объектов

Из этого легко вытекает, что операции с таблицей в целом производятся статическими методами класса, над конкретной записью — динамическими методами объекта, а операции над коллекцией — методами объекта-коллекции с возможными «пробросом» к методам объектов-членов коллекции.

Ну типа так (жуткая лапша):

$users = User::findAllByRegion('Russia');
$user0 = $users[0];
$user0->setNickname('Habr');
$user1 = $users[1];
$user1->setPassword('password');
$users->save();


Пока такой концепции хватает для большинства разумных случаев, где вообще применим ORM.
Поздравляю вас, вы описали помесь ActiveRecord с Recordset.
Хотелось бы побольше кода, как здесь.
И сравнение по скорости запросов чистого SQL с Вашим решением тоже не помешало бы.

PS: сам использую СУБД, нативно поддерживающую ООП (не только для Python), SQL и NoSQL, поэтому и вопросов с ORM не возникает.
сам использую СУБД, нативно поддерживающую ООП (не только для Python), SQL и NoSQL, поэтому и вопросов с ORM не возникает.

Какую именно, если не секрет?
Какую именно, если не секрет?
Нисколько не секрет — это СУБД Caché.

Эх, а я думал Вы догадаетесь сами по моему профилю.
Я помню ее, когда-то — лет 10 назад — очень внимательно разглядывал, очень интересная концепция была, хорошо что она столь успешно развивается.
Sign up to leave a comment.

Articles