BugzillaORM — различия между версиями

Материал из YourcmcWiki
Перейти к: навигация, поиск
(Багзилла)
Строка 48: Строка 48:
 
== Багзилла ==
 
== Багзилла ==
  
Надо так:
+
Есть объекты:
 +
 
 +
* Баг
 +
* Продукт
 +
* Группа
 +
* ...
 +
 
 +
Для каждого объекта есть '''метакласс'''<ref>Название "метакласс" в данном контексте может вызвать негодование великих специалистов по ООП, но фиг с ним.</ref>. При старте приложения (или первой необходимости) создаётся объект метакласса - класс.
 +
 
 +
У объектов есть поля. Поля бывают разных типов. Для каждого типа полей тоже есть метакласс.
 +
 
 +
Например, описание поля связи с объектом (в скобках пример):
 +
 
 +
* Название поля (product)
 +
* Объект, к которому относится поле (баг)
 +
* Объект, с которым поле связывает (продукт)
 +
* Поле, от которого зависит видимость поля
 +
* Поле, от которого зависит набор значений поля (классификация)
 +
** Значение по умолчанию, для каждого набора значений поля
 +
* Активно?
 +
* Выбирается при создании объекта?
 +
* Редактируется?
  
 
== FunMap ==
 
== FunMap ==

Версия 18:05, 24 сентября 2009

Здесь, наверное, будет статья, в которой я постараюсь выразить свои мысли по поводу ORM, обычного взгляда на вещи и необычного взгляда на вещи.

А пока что это сборище идей.

Типичный взгляд

Пример: http://rutube.ru/tracks/793494.html - доклад с YAPC::Russia. Автор — Егор Шиповалов, название «Применение ORM в Perl».

Первый же слайд ясно даёт понять проблемы, обычно решаемые авторами объектно-реляционных мапперов в Perl’е:

  • Дублирование кода работы с БД.
  • Трудоёмкость адаптации кода под изменения схемы и различные СУБД.
  • Необходимость знания устройства и работы с БД всеми программистами команды.

Так вот, все эти причины — «4.2 (чёрта с два)». Бесплатная подсказка, тривиальный путь решения всех трёх проблем: код работы с БД для каждого «компонента» программы выносится в отдельный класс и разбивается на отдельные процедуры. При грамотном разбиении кода работы с БД на отдельные процедуры он получается достаточно компактным, а если не компактным, то по меньшей мере простым и очевидным. Весь код работы с БД таким образом концентрируется в одном месте, и переписывать под различные СУБД его становится гораздо проще. И уж конечно, имея код работы с БД в отдельном классе, неспециалисты по БД могут и не заниматься работой с ней. Между прочим, этот подход использует Skype, за тем исключением, что функционал в процедуры они оборачивали не на стороне клиента БД, а внутри самой СУБД (PostgreSQL) — об этом Иван Золотухин рассказывал в статье «Масштабирование PostgreSQL: Готовые решения от Skype».

Ещё одна почти бессмысленная для веб-приложений возможность: итераторы. Даже DBR её, кстати, не лишён. То есть она осмысленная — в GUI-приложениях. Но не в веб-приложениях, где, во-первых, сам DBI никогда не держит открытых курсоров, во-вторых, это часто не даёт делать СУБД, а в-третьих, это совершенно не нужно, так как время исполнения запроса должно быть как можно меньше, а следующий запрос, вероятно, попадёт в другой поток — а может, и вообще в другой процесс.

Небольшой обзор существующих модулей ORM:

  • SPOPS — ужас кривой, тормозной, неиллюзорный, заброшенный в 2004 году.
  • DBIx::Class — лидер области. Тяжеловесный, имеет кучу возможностей и вторую кучу — зависимостей. Например, использует mro 'c3'. Частично совместим с Class::DBI. Но вы как хотите, а «БД-независимый» язык запросов, основанный на хешах — это ад. Что-то типа Vsem::DB::Query из vsem.ru, кстати.
  • Class::DBI — говорят, легковесный. Наиболее старый — родился в 2001 году.
  • ORM — сразу настораживает фраза «No SQL queries needed». Не бывает такого.
  • DBR — весьма похож на Funq. Но явно попроще. Наиболее, кажется, «альтернативный».
  • Rose::DB::Object — ещё одна вариация на тему Class::DBI.
  • Class::AutoDB.

Реальная проблема

Реальная проблема, ради решения которой задуман ORM — сложность, возникающая при попытке организовать сохранение в БД большого числа объектов разных типов со сложными отношениями друг между другом.

Даже Bugzilla имеет своё подобие ORM’а. И причина именно в этом — нужно управлять многими типами объектов. У бага много атрибутов, для каждого атрибута заведён отдельный тип «поле», а некоторые из этих типов одновременно являются самостоятельными объектами (продукты, компоненты). Отличительная черта багзильного ORM’а, по-моему, заключается в том, что он ориентирован на поля объектов и на универсальность доступа к значениям этих полей, но при этом не использует термина «связь» (один-к-одному, один-ко-многим и т. п.).

В целом этот ORM, конечно, кривоват и простоват. Но зато минимален и имеет одну очень правильную черту — он никогда не тратит силы на выполнение тех операций, о которых его пока ещё никто не просил. Создание экземпляра класса выглядит очень просто, в одну строчку — это просто bless хеша-строчки, полученной из БД. Никаких дополнительных манёвров при этом не происходит. А все методы, возвращающие те или иные данные, создают, извлекают или вычисляют эти данные только в момент первого вызова.

Для ORM’а «ленивая» идеология, с моей точки зрения, особенно важна, так как если на каждый чих будут извлекаться и создаваться кучи объектов — связанных и т. п. — даже тогда, когда это вообще не нужно, производительность, конечно, пострадает.

«Правильный» ORM должен делиться на две части:

  1. Обеспечивающий язык запросов и
  2. Схема объектного взаимодействия.

При этом язык запросов должен быть полным относительно возможностей нижележащего SQL. Аргументация простая: если полноты не будет, некоторый процент ситуаций потребует перехода на «ручное» выполнение собственных SQL-запросов, что обычно означает потерю всех преимуществ языка. Остаётся, конечно, вопрос процента таких ситуаций — если их очень мало, то и проблем, наверное, также будет очень мало.

Реально же многие (если не большинство) ORM-движков пытается скрестить ужа и ежа, то есть обе части, в одну.

Багзилла

Есть объекты:

  • Баг
  • Продукт
  • Группа
  • ...

Для каждого объекта есть метакласс[1]. При старте приложения (или первой необходимости) создаётся объект метакласса - класс.

У объектов есть поля. Поля бывают разных типов. Для каждого типа полей тоже есть метакласс.

Например, описание поля связи с объектом (в скобках пример):

  • Название поля (product)
  • Объект, к которому относится поле (баг)
  • Объект, с которым поле связывает (продукт)
  • Поле, от которого зависит видимость поля
  • Поле, от которого зависит набор значений поля (классификация)
    • Значение по умолчанию, для каждого набора значений поля
  • Активно?
  • Выбирается при создании объекта?
  • Редактируется?

FunMap

  1. Обеспечивающий язык запросов — Funq.
  2. Схема объектного взаимодействия — ниже.
    1. Название "метакласс" в данном контексте может вызвать негодование великих специалистов по ООП, но фиг с ним.